From 764312e136ccec8ecddcb061b2e642d884557109 Mon Sep 17 00:00:00 2001 From: "Xunnamius (Romulus)" Date: Fri, 26 May 2023 19:13:32 -0700 Subject: [PATCH] refactor: copy forward qoverflow api source --- .codecov.yml | 2 + .editorconfig | 9 + .env.default | 197 + .eslintrc.js | 157 + .gitattributes | 1 + .github/CODEOWNERS | 3 + .github/CODE_OF_CONDUCT.md | 82 + .github/ISSUE_TEMPLATE/BUG_REPORT.md | 77 + .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md | 26 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/PULL_REQUEST_TEMPLATE.md | 15 + .github/SUPPORT.md | 57 + .github/dependabot.yml | 21 + .github/pipeline.config.js | 60 + .github/workflows/README.md | 6 + .gitignore | 13 + .husky/.gitignore | 1 + .husky/commit-msg | 7 + .husky/pre-commit | 5 + .ncurc.js | 22 + .prettierignore | 6 + .spellcheckignore | 0 .vercelignore | 22 + CONTRIBUTING.md | 191 + README.md | 3 +- SECURITY.md | 15 + babel.config.js | 96 + commitlint.config.js | 28 + conventional.config.js | 4 + expect-env.js | 268 + external-scripts/README.md | 8 + external-scripts/ban-hammer.ts | 288 + .../initialize-data-cache-20220809.json | 149342 +++++++++++++++ external-scripts/initialize-data/api-test.ts | 220 + external-scripts/initialize-data/api.ts | 80 + external-scripts/initialize-data/crypto.ts | 58 + external-scripts/initialize-data/index.ts | 1480 + external-scripts/log-stats.ts | 650 + external-scripts/prune-data.ts | 269 + jest.config.js | 35 + lib/README.md | 5 + lib/debug-extended/index.ts | 47 + lib/debug-extended/package.json | 3 + lib/debug-extended/unit.test.ts | 38 + lib/find-project-root/index.ts | 33 + lib/find-project-root/package.json | 4 + lib/find-project-root/unit.test.ts | 59 + lib/is-plain-object/index.ts | 5 + lib/is-plain-object/package.json | 3 + lib/jest-mock-date/index.ts | 21 + lib/jest-mock-date/package.json | 3 + lib/json-node-fetch/index.ts | 360 + lib/json-node-fetch/package.json | 3 + lib/json-node-fetch/test/integration.test.ts | 178 + lib/json-node-fetch/test/unit.test.ts | 317 + lib/json-unfetch/index.ts | 454 + lib/json-unfetch/package.json | 3 + lib/json-unfetch/test/integration.test.ts | 186 + lib/json-unfetch/test/unit.test.ts | 382 + lib/mongo-common/index.ts | 149 + lib/mongo-common/package.json | 3 + lib/mongo-common/unit.test.ts | 59 + lib/mongo-item/index.ts | 193 + lib/mongo-item/package.json | 3 + lib/mongo-item/unit.test.ts | 234 + lib/mongo-schema/index.ts | 317 + lib/mongo-schema/package.json | 3 + lib/mongo-schema/unit.test.ts | 343 + lib/mongo-test/index.ts | 255 + lib/mongo-test/package.json | 3 + lib/mongo-test/test/integration.test.ts | 106 + lib/mongo-test/test/unit.test.ts | 702 + lib/next-adhesive/add-raw-body.ts | 167 + lib/next-adhesive/auth-request.ts | 115 + lib/next-adhesive/check-content-type.ts | 179 + lib/next-adhesive/check-method.ts | 49 + lib/next-adhesive/check-version.ts | 37 + lib/next-adhesive/contrive-error.ts | 39 + lib/next-adhesive/handle-error.ts | 126 + lib/next-adhesive/limit-request.ts | 42 + lib/next-adhesive/log-request.ts | 46 + lib/next-adhesive/package.json | 3 + .../test/unit-add-raw-body.test.ts | 305 + .../test/unit-auth-request.test.ts | 228 + .../test/unit-check-content-type.test.ts | 719 + .../test/unit-check-method.test.ts | 173 + .../test/unit-check-version.test.ts | 174 + .../test/unit-contrive-error.test.ts | 53 + .../test/unit-handle-error.test.ts | 183 + .../test/unit-limit-request.test.ts | 158 + .../test/unit-log-request.test.ts | 104 + lib/next-adhesive/test/unit-use-cors.test.ts | 77 + lib/next-adhesive/use-cors.ts | 36 + lib/next-api-glue/index.ts | 385 + lib/next-api-glue/package.json | 3 + lib/next-api-glue/unit.test.ts | 1144 + lib/next-api-respond/index.ts | 206 + lib/next-api-respond/package.json | 3 + lib/next-api-respond/unit.test.ts | 533 + lib/next-auth/index.ts | 699 + lib/next-auth/package.json | 3 + lib/next-auth/unit.test.ts | 1318 + lib/next-contrived/index.ts | 33 + lib/next-contrived/package.json | 3 + lib/next-contrived/unit.test.ts | 139 + lib/next-env/index.ts | 183 + lib/next-env/package.json | 3 + lib/next-env/unit.test.ts | 178 + lib/next-limit/index.ts | 115 + lib/next-limit/package.json | 3 + lib/next-limit/unit.test.ts | 334 + lib/next-log/index.ts | 77 + lib/next-log/package.json | 3 + lib/next-log/unit.test.ts | 238 + lib/suppress-experimental-warnings/index.ts | 39 + .../package.json | 3 + lib/throttled-fetch/index.ts | 919 + lib/throttled-fetch/package.json | 3 + lib/throttled-fetch/unit.test.ts | 1564 + lint-staged.config.js | 7 + next.config.js | 49 + package-lock.json | 37149 ++++ package.json | 177 + prettier.config.js | 20 + public/.gitkeep | 0 release.config.js | 99 + spellcheck-commit.js | 116 + src/backend/api.ts | 17 + src/backend/db.ts | 911 + src/backend/env.ts | 109 + src/backend/index.ts | 2082 + src/backend/middleware.ts | 103 + src/constants.ts | 7 + src/error.ts | 104 + src/pages/_app.tsx | 19 + src/pages/api/sys/auth/index.ts | 50 + src/pages/api/sys/auth/unban.ts | 27 + src/pages/api/sys/ping.ts | 25 + src/pages/api/v1/mail/[username].ts | 26 + src/pages/api/v1/mail/index.ts | 25 + .../comments/[comment_id]/index.ts | 27 + .../comments/[comment_id]/vote/[username].ts | 51 + .../answers/[answer_id]/comments/index.ts | 37 + .../answers/[answer_id]/index.ts | 27 + .../answers/[answer_id]/vote/[username].ts | 50 + .../questions/[question_id]/answers/index.ts | 35 + .../comments/[comment_id]/index.ts | 27 + .../comments/[comment_id]/vote/[username].ts | 50 + .../questions/[question_id]/comments/index.ts | 40 + .../api/v1/questions/[question_id]/index.ts | 34 + .../[question_id]/vote/[username].ts | 50 + src/pages/api/v1/questions/index.ts | 25 + src/pages/api/v1/questions/search.ts | 45 + src/pages/api/v1/users/[username]/answers.ts | 26 + src/pages/api/v1/users/[username]/auth.ts | 38 + src/pages/api/v1/users/[username]/index.ts | 31 + src/pages/api/v1/users/[username]/points.ts | 31 + .../api/v1/users/[username]/questions.ts | 26 + src/pages/api/v1/users/index.ts | 25 + src/pages/index.tsx | 43 + test/api/integration.test.ts | 239 + test/api/unit-api-mail.test.ts | 59 + test/api/unit-api-questions.test.ts | 532 + test/api/unit-api-users.test.ts | 219 + test/api/unit-backend.test.ts | 5800 + test/api/unit-sys-auth.test.ts | 640 + test/api/unit-sys-ping.test.ts | 124 + test/db.ts | 427 + test/externals/integration-initialize.test.ts | 223 + test/externals/unit-ban.test.ts | 329 + test/externals/unit-initialize.test.ts | 287 + test/externals/unit-prune.test.ts | 264 + test/externals/unit-stats.test.ts | 60 + test/fixtures/index.ts | 340 + test/fixtures/integration.ts | 1227 + test/pages/unit-app.test.tsx | 19 + test/pages/unit-index.test.tsx | 18 + test/setup.ts | 1198 + tsconfig.docs.json | 4 + tsconfig.eslint.json | 4 + tsconfig.json | 58 + tsconfig.lint.json | 11 + tsconfig.types.json | 14 + types/next-env.d.ts | 5 + types/next__bundle-analyzer.d.ts | 1 + types/random-case.d.ts | 3 + types/stackexchange-api.ts | 70 + types/unique-filename.d.ts | 7 + vercel.json | 3 + webpack.config.js | 198 + 190 files changed, 223669 insertions(+), 1 deletion(-) create mode 100644 .codecov.yml create mode 100644 .editorconfig create mode 100644 .env.default create mode 100644 .eslintrc.js create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/ISSUE_TEMPLATE/BUG_REPORT.md create mode 100644 .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SUPPORT.md create mode 100644 .github/dependabot.yml create mode 100644 .github/pipeline.config.js create mode 100644 .github/workflows/README.md create mode 100644 .gitignore create mode 100644 .husky/.gitignore create mode 100755 .husky/commit-msg create mode 100755 .husky/pre-commit create mode 100644 .ncurc.js create mode 100644 .prettierignore create mode 100644 .spellcheckignore create mode 100644 .vercelignore create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 babel.config.js create mode 100644 commitlint.config.js create mode 100644 conventional.config.js create mode 100644 expect-env.js create mode 100644 external-scripts/README.md create mode 100644 external-scripts/ban-hammer.ts create mode 100644 external-scripts/initialize-data-cache-20220809.json create mode 100644 external-scripts/initialize-data/api-test.ts create mode 100644 external-scripts/initialize-data/api.ts create mode 100644 external-scripts/initialize-data/crypto.ts create mode 100644 external-scripts/initialize-data/index.ts create mode 100644 external-scripts/log-stats.ts create mode 100644 external-scripts/prune-data.ts create mode 100644 jest.config.js create mode 100644 lib/README.md create mode 100644 lib/debug-extended/index.ts create mode 100644 lib/debug-extended/package.json create mode 100644 lib/debug-extended/unit.test.ts create mode 100644 lib/find-project-root/index.ts create mode 100644 lib/find-project-root/package.json create mode 100644 lib/find-project-root/unit.test.ts create mode 100644 lib/is-plain-object/index.ts create mode 100644 lib/is-plain-object/package.json create mode 100644 lib/jest-mock-date/index.ts create mode 100644 lib/jest-mock-date/package.json create mode 100644 lib/json-node-fetch/index.ts create mode 100644 lib/json-node-fetch/package.json create mode 100644 lib/json-node-fetch/test/integration.test.ts create mode 100644 lib/json-node-fetch/test/unit.test.ts create mode 100644 lib/json-unfetch/index.ts create mode 100644 lib/json-unfetch/package.json create mode 100644 lib/json-unfetch/test/integration.test.ts create mode 100644 lib/json-unfetch/test/unit.test.ts create mode 100644 lib/mongo-common/index.ts create mode 100644 lib/mongo-common/package.json create mode 100644 lib/mongo-common/unit.test.ts create mode 100644 lib/mongo-item/index.ts create mode 100644 lib/mongo-item/package.json create mode 100644 lib/mongo-item/unit.test.ts create mode 100644 lib/mongo-schema/index.ts create mode 100644 lib/mongo-schema/package.json create mode 100644 lib/mongo-schema/unit.test.ts create mode 100644 lib/mongo-test/index.ts create mode 100644 lib/mongo-test/package.json create mode 100644 lib/mongo-test/test/integration.test.ts create mode 100644 lib/mongo-test/test/unit.test.ts create mode 100644 lib/next-adhesive/add-raw-body.ts create mode 100644 lib/next-adhesive/auth-request.ts create mode 100644 lib/next-adhesive/check-content-type.ts create mode 100644 lib/next-adhesive/check-method.ts create mode 100644 lib/next-adhesive/check-version.ts create mode 100644 lib/next-adhesive/contrive-error.ts create mode 100644 lib/next-adhesive/handle-error.ts create mode 100644 lib/next-adhesive/limit-request.ts create mode 100644 lib/next-adhesive/log-request.ts create mode 100644 lib/next-adhesive/package.json create mode 100644 lib/next-adhesive/test/unit-add-raw-body.test.ts create mode 100644 lib/next-adhesive/test/unit-auth-request.test.ts create mode 100644 lib/next-adhesive/test/unit-check-content-type.test.ts create mode 100644 lib/next-adhesive/test/unit-check-method.test.ts create mode 100644 lib/next-adhesive/test/unit-check-version.test.ts create mode 100644 lib/next-adhesive/test/unit-contrive-error.test.ts create mode 100644 lib/next-adhesive/test/unit-handle-error.test.ts create mode 100644 lib/next-adhesive/test/unit-limit-request.test.ts create mode 100644 lib/next-adhesive/test/unit-log-request.test.ts create mode 100644 lib/next-adhesive/test/unit-use-cors.test.ts create mode 100644 lib/next-adhesive/use-cors.ts create mode 100644 lib/next-api-glue/index.ts create mode 100644 lib/next-api-glue/package.json create mode 100644 lib/next-api-glue/unit.test.ts create mode 100644 lib/next-api-respond/index.ts create mode 100644 lib/next-api-respond/package.json create mode 100644 lib/next-api-respond/unit.test.ts create mode 100644 lib/next-auth/index.ts create mode 100644 lib/next-auth/package.json create mode 100644 lib/next-auth/unit.test.ts create mode 100644 lib/next-contrived/index.ts create mode 100644 lib/next-contrived/package.json create mode 100644 lib/next-contrived/unit.test.ts create mode 100644 lib/next-env/index.ts create mode 100644 lib/next-env/package.json create mode 100644 lib/next-env/unit.test.ts create mode 100644 lib/next-limit/index.ts create mode 100644 lib/next-limit/package.json create mode 100644 lib/next-limit/unit.test.ts create mode 100644 lib/next-log/index.ts create mode 100644 lib/next-log/package.json create mode 100644 lib/next-log/unit.test.ts create mode 100644 lib/suppress-experimental-warnings/index.ts create mode 100644 lib/suppress-experimental-warnings/package.json create mode 100644 lib/throttled-fetch/index.ts create mode 100644 lib/throttled-fetch/package.json create mode 100644 lib/throttled-fetch/unit.test.ts create mode 100644 lint-staged.config.js create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 prettier.config.js create mode 100644 public/.gitkeep create mode 100644 release.config.js create mode 100644 spellcheck-commit.js create mode 100644 src/backend/api.ts create mode 100644 src/backend/db.ts create mode 100644 src/backend/env.ts create mode 100644 src/backend/index.ts create mode 100644 src/backend/middleware.ts create mode 100644 src/constants.ts create mode 100644 src/error.ts create mode 100644 src/pages/_app.tsx create mode 100644 src/pages/api/sys/auth/index.ts create mode 100644 src/pages/api/sys/auth/unban.ts create mode 100644 src/pages/api/sys/ping.ts create mode 100644 src/pages/api/v1/mail/[username].ts create mode 100644 src/pages/api/v1/mail/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/vote/[username].ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/[answer_id]/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/[answer_id]/vote/[username].ts create mode 100644 src/pages/api/v1/questions/[question_id]/answers/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/comments/[comment_id]/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/comments/[comment_id]/vote/[username].ts create mode 100644 src/pages/api/v1/questions/[question_id]/comments/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/index.ts create mode 100644 src/pages/api/v1/questions/[question_id]/vote/[username].ts create mode 100644 src/pages/api/v1/questions/index.ts create mode 100644 src/pages/api/v1/questions/search.ts create mode 100644 src/pages/api/v1/users/[username]/answers.ts create mode 100644 src/pages/api/v1/users/[username]/auth.ts create mode 100644 src/pages/api/v1/users/[username]/index.ts create mode 100644 src/pages/api/v1/users/[username]/points.ts create mode 100644 src/pages/api/v1/users/[username]/questions.ts create mode 100644 src/pages/api/v1/users/index.ts create mode 100644 src/pages/index.tsx create mode 100644 test/api/integration.test.ts create mode 100644 test/api/unit-api-mail.test.ts create mode 100644 test/api/unit-api-questions.test.ts create mode 100644 test/api/unit-api-users.test.ts create mode 100644 test/api/unit-backend.test.ts create mode 100644 test/api/unit-sys-auth.test.ts create mode 100644 test/api/unit-sys-ping.test.ts create mode 100644 test/db.ts create mode 100644 test/externals/integration-initialize.test.ts create mode 100644 test/externals/unit-ban.test.ts create mode 100644 test/externals/unit-initialize.test.ts create mode 100644 test/externals/unit-prune.test.ts create mode 100644 test/externals/unit-stats.test.ts create mode 100644 test/fixtures/index.ts create mode 100644 test/fixtures/integration.ts create mode 100644 test/pages/unit-app.test.tsx create mode 100644 test/pages/unit-index.test.tsx create mode 100644 test/setup.ts create mode 100644 tsconfig.docs.json create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json create mode 100644 tsconfig.lint.json create mode 100644 tsconfig.types.json create mode 100644 types/next-env.d.ts create mode 100644 types/next__bundle-analyzer.d.ts create mode 100644 types/random-case.d.ts create mode 100644 types/stackexchange-api.ts create mode 100644 types/unique-filename.d.ts create mode 100644 vercel.json create mode 100644 webpack.config.js diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..16b82b1 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,2 @@ +coverage: + range: '75...100' diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4960585 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/.env.default b/.env.default new file mode 100644 index 0000000..d0e3350 --- /dev/null +++ b/.env.default @@ -0,0 +1,197 @@ +### GLOBAL VARIABLES ### + +# If !false, Next's bundle(s) will be analyzed and report files generated. +ANALYZE=false + +# This will overwrite the NODE_ENV setting during runtime and for the compiled +# applications. +# +# Recognized values: test development production +# Default value: empty +NODE_ENV= + +# MongoDB connect URI. Specify auth credentials if necessary. YOU MUST *NOT* +# SPECIFY A DATABASE AT THE END! +MONGODB_URI=mongodb://127.0.0.1:27017 + +# Dedicated port to be used by the MongoDB Memory Server during unit tests. +# Especially useful when stepping through code, since you can always access the +# db at `mongodb://127.0.0.1:MONGODB_MS_PORT` when the debugger is paused. +# Tip: call `jest.setTimeout()` with a large number (i.e. 10**6) to ensure the +# MongoClient isn't closed randomly leading to strange errors. +# +# Leave this blank to choose any random port (not recommended). Note: this +# option is also used when Node is started in debug mode, e.g. `node +# --inspect-brk` or `node --debug`, or if the debugger is attached before the +# database connection is memoized. +MONGODB_MS_PORT=6666 + +# Determines the maximum allowed character length of an *entire* HTTP +# Authorization header. The default is 500. +AUTH_HEADER_MAX_LENGTH=500 + +# Controls which versions of the API will respond to requests. Examples (disable +# v1; disable v1 and v2; disable v3, v5, and v7): +# DISABLED_API_VERSIONS=1 +# DISABLED_API_VERSIONS=1,2 +# DISABLED_API_VERSIONS=3,5,7 +# +# Note that `DISABLED_API_VERSIONS=` (i.e. empty) means no +# versions are disabled! +DISABLED_API_VERSIONS= + +# Determines the number of items returned by paginated endpoints. +RESULTS_PER_PAGE=100 + +# If !false, all rate limits and exponential soft banning will be ignored. +IGNORE_RATE_LIMITS=false + +# If !false, no one will be able to use the API. +LOCKOUT_ALL_CLIENTS=false + +# Controls what request methods are allowed. Empty means all are allowed +# (default). +# +# Example, to make API read-only: +# DISALLOWED_METHODS=POST,PUT +DISALLOWED_METHODS= + +# Every Nth request will be be cancelled and an HTTP 555 response returned. Note +# that, in addition to every Nth request, the very first request sent to the API +# will also return a contrived error. Set to 0 to disable all contrived errors. +REQUESTS_PER_CONTRIVED_ERROR=10 + +# Maximum allowed size of a request body (and content-length header value) in +# bytes. Should be a string like 1kb, 1mb, 500b. +MAX_CONTENT_LENGTH_BYTES=10kb + +# Maximum number of parameters that can be passed to endpoints that accept +# multiple slash parameters. +MAX_PARAMS_PER_REQUEST=100 + +# Minimum allowed string length of a username. +MIN_USER_NAME_LENGTH=4 + +# Maximum allowed string length of a username. +MAX_USER_NAME_LENGTH=16 + +# Minimum allowed string length of a user email. +MIN_USER_EMAIL_LENGTH=4 + +# Maximum allowed string length of a user email. +MAX_USER_EMAIL_LENGTH=75 + +# Expected string length of a user's cryptographic salt (hexadecimal length). +USER_SALT_LENGTH=32 + +# Expected string length of a user's cryptographic key (hexadecimal length). +USER_KEY_LENGTH=128 + +# Maximum allowed length of a comment. +MAX_COMMENT_LENGTH=150 + +# Maximum allowed length of a question title. +MAX_QUESTION_TITLE_LENGTH=150 + +# Maximum allowed size of a question body. Should be a string like 1kb, 1mb, +# 500b. +MAX_QUESTION_BODY_LENGTH_BYTES=3kb + +# Maximum allowed size of an answer body. Should be a string like 1kb, 1mb, +# 500b. +MAX_ANSWER_BODY_LENGTH_BYTES=3kb + +# Maximum allowed length of a question subject. +MAX_MAIL_SUBJECT_LENGTH=75 + +# Maximum allowed size of an mail (direct message) body. Should be a string like +# 1kb, 1mb, 500b. +MAX_MAIL_BODY_LENGTH_BYTES=512b + +### EXTERNAL SCRIPT VARIABLES ### +# (optional unless using the relevant external script) + +# How often this script is going to be invoked. This doesn't determine anything +# automatically on its own, this is useful to ensure the script works no matter +# how often you decide to call it. +BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS=60 + +# The maximum number of requests per BAN_HAMMER_RESOLUTION_WINDOW_SECONDS +# allowed by a single client. +BAN_HAMMER_MAX_REQUESTS_PER_WINDOW=10 + +# How far back into the past this script looks when checking a key or ip against +# BAN_HAMMER_MAX_REQUESTS_PER_WINDOW. +BAN_HAMMER_RESOLUTION_WINDOW_SECONDS=1 + +# The initial amount of time an offender is banned. +BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES=1 + +# When an offender is banned twice in the same "period," they're banned for +# BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES * BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER +# minutes instead of the default. This is also the length of the "period". +BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER=2 + +# The size (in bytes) of the root request-log collection will not be allowed to +# exceed this amount. Oldest entries are deleted first. Should be a string like +# 1kb, 1mb, 500b. +PRUNE_DATA_MAX_LOGS_BYTES=50mb + +# The size (in bytes) of the root limited-log collection will not be allowed to +# exceed this amount. Oldest entries are deleted first. Should be a string like +# 1kb, 1mb, 500b. +PRUNE_DATA_MAX_BANNED_BYTES=10mb + +# The size (in bytes) of the mail collection will not be allowed to exceed this +# amount. Oldest entries are deleted first. Should be a string like 1kb, 1mb, +# 500b. +PRUNE_DATA_MAX_MAIL_BYTES=10mb + +# The size (in bytes) of the questions collection will not be allowed to exceed +# this amount. Oldest entries are deleted first. Should be a string like 1kb, +# 1mb, 500b. +PRUNE_DATA_MAX_QUESTIONS_BYTES=350mb + +# The size (in bytes) of the users collection will not be allowed to exceed this +# amount. Oldest entries are deleted first. Should be a string like 1kb, 1mb, +# 500b. +PRUNE_DATA_MAX_USERS_BYTES=10mb + +# The StackExchange client key used to ensure the initialize-data external script +# has a much higher request quota. +STACKAPPS_AUTH_KEY= + +# A maximum of STACKAPPS_INTERVAL_PERIOD_MS requests will be sent to the +# StackExchange API every >=STACKAPPS_MAX_REQUESTS_PER_INTERVAL milliseconds. +# +# Must be a positive number such that no greater than 30 requests are sent per +# second or the StackExchange API will rate limit you. +STACKAPPS_INTERVAL_PERIOD_MS=1000 + +# A maximum of STACKAPPS_INTERVAL_PERIOD_MS requests will be sent to the +# StackExchange API every >=STACKAPPS_MAX_REQUESTS_PER_INTERVAL milliseconds. +# +# Must be a positive number such that no greater than 30 requests are sent per +# second or the StackExchange API will rate limit you. +STACKAPPS_MAX_REQUESTS_PER_INTERVAL=10 + +# The number of questions to render from the StackExchange API, which will be +# one less than the total number of questions inserted into the database. +STACKAPPS_TOTAL_API_GENERATED_QUESTIONS=100 + +# The number of answers the "collect all" questions will have. +STACKAPPS_COLLECTALL_QUESTION_ANSWERS=150 + +# The number of comments the "collect all" questions will have. +STACKAPPS_COLLECTALL_QUESTION_COMMENTS=250 + +# The number of comments the first answer of the "collect all" question will +# have. +STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS=150 + +# The maximum number of items that will be requested from the StackExchange API. +# Must be a positive number no greater than 100. +STACKAPPS_MAX_PAGE_SIZE=100 + +### TOOLS FRONTEND VARIABLES ### +# (optional unless using tools) diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f6a5bf3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,157 @@ +'use strict'; + +const debug = require('debug')(`${require('./package.json').name}:eslint-config`); +const restrictedGlobals = require('confusing-browser-globals'); + +module.exports = { + parser: '@typescript-eslint/parser', + plugins: ['jest', 'jest-dom', '@typescript-eslint', 'import'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:@next/next/recommended' + ], + parserOptions: { + ecmaVersion: 8, + sourceType: 'module', + ecmaFeatures: { + impliedStrict: true, + experimentalObjectRestSpread: true, + jsx: true + }, + project: 'tsconfig.eslint.json' + }, + env: { + es6: true, + node: true, + jest: true, + 'jest/globals': true, + browser: true, + webextensions: true + }, + rules: { + 'no-console': 'warn', + 'no-return-await': 'warn', + 'no-await-in-loop': 'warn', + 'import/no-unresolved': [ + 'error', + { + commonjs: true + } + ], + 'no-restricted-globals': ['warn'].concat(restrictedGlobals), + 'no-extra-boolean-cast': 'off', + 'no-empty': 'off', + '@typescript-eslint/camelcase': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/prefer-ts-expect-error': 'warn', + '@typescript-eslint/no-floating-promises': [ + 'error', + { ignoreVoid: true, ignoreIIFE: true } + ], + '@typescript-eslint/ban-ts-comment': [ + 'warn', + { + 'ts-expect-error': 'allow-with-description', + minimumDescriptionLength: 6 + } + ], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_+', + varsIgnorePattern: '^_+', + caughtErrorsIgnorePattern: '^ignored?\\d*$', + caughtErrors: 'all' + } + ], + // ? Ever since v4, we will rely on TypeScript to catch these + 'no-undef': 'off', + '@typescript-eslint/no-var-requires': 'off', + 'no-unused-vars': 'off', + // ? Broken as of next 12.0.7 + '@next/next/no-page-custom-font': 'off', + '@typescript-eslint/no-non-null-assertion': 'off' + }, + overrides: [ + { + files: ['*.test.*'], + extends: [ + 'plugin:jest/all', + 'plugin:jest/style', + 'plugin:jest-dom/recommended' + ], + rules: { + 'jest/lowercase': 'off', + 'jest/consistent-test-it': 'off', + 'jest/require-top-level-describe': 'off', + 'jest/valid-describe': 'off', + 'jest/no-hooks': 'off', + 'jest/require-to-throw-message': 'off', + 'jest/prefer-called-with': 'off', + 'jest/prefer-spy-on': 'off', + 'jest/no-if': 'off', + 'jest/no-disabled-tests': 'warn', + 'jest/no-commented-out-tests': 'warn', + 'jest/require-hook': 'off', + 'jest/no-alias-methods': 'off', + 'jest/no-conditional-in-test': 'off', + 'jest/no-conditional-expect': 'off', + 'jest/max-expects': 'off', + 'jest/prefer-mock-promise-shorthand': 'off' + } + } + ], + settings: { + react: { + version: 'detect' + }, + 'import/extensions': ['.ts', '.tsx', '.js', '.jsx'], + // ? Switch parsers depending on which type of file we're looking at + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + 'babel-eslint': ['.js', '.jsx'] + }, + 'import/resolver': { + alias: { + map: [ + // ! If changed, also update these aliases in tsconfig.json, + // ! webpack.config.js, next.config.ts, and jest.config.js + ['universe', './src'], + ['multiverse', './lib'], + ['testverse', './test'], + ['externals', './external-scripts'], + ['types', './types'], + ['package', './package.json'], + // ? These are used at various points (including at compile time by + // ? Next.js) to get mongo schema configuration and/or test dummy data. + // ! Must be defined if using @xunnamius/mongo-schema + ['configverse/get-schema-config', './src/backend/db.ts'], + // ! Must be defined if using @xunnamius/mongo-test + ['configverse/get-dummy-data', './test/db.ts'] + ], + extensions: ['.js', '.jsx', '.ts', '.tsx'] + }, + typescript: {} + }, + 'import/ignore': [ + // ? Don't go complaining about anything that we don't own + '.*/node_modules/.*', + '.*/bin/.*' + ] + }, + ignorePatterns: ['coverage', 'dist', 'bin', 'build', '/next.config.js'], + globals: { + page: true, + browser: true, + context: true, + jestPuppeteer: true + } +}; + +debug('exports: %O', module.exports); diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..7557ad6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# See https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners + +* @Xunnamius diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..289daad --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,82 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and +expression, level of experience, education, socioeconomic status, nationality, +personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- Racism or sexism in any shape or form +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at +[Xunnamius@users.noreply.github.com][1]. All complaints will be reviewed and +investigated and will result in a response that is deemed necessary and +appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. Further details of +specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +[https://www.contributor-covenant.org/version/1/4/code-of-conduct.html][2] + +For answers to common questions about this code of conduct, see +[https://www.contributor-covenant.org/faq][3] + +[homepage]: https://www.contributor-covenant.org +[1]: mailto:Xunnamius@users.noreply.github.com +[2]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +[3]: https://www.contributor-covenant.org/faq diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md new file mode 100644 index 0000000..a94a69a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -0,0 +1,77 @@ +--- +name: 🤯 Bug report +about: Alert us about an issue +labels: bug +--- + + + +
The problem + + + +
+ +
Reproduction steps + + + +
+ + + + + + diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 0000000..e699b28 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,26 @@ +--- +name: 🤩 Feature request +about: Tell us about your awesome idea +labels: enhancement +--- + + + + + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..1ffe6a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Security Channel + url: 'mailto:security@ergodark.com?subject=SECURITY%20INCIDENT%3A%20%28five%20word%20summary%29' + about: Report security-related issues here! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..264969c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ + + + + + + +--- + + + +- [ ] I have read **[CONTRIBUTING.md][1]** +- [ ] This PR is not security related (see [SECURITY.md][2]) + +[1]: /CONTRIBUTING.md +[2]: /SECURITY.md diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..a2a4902 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,57 @@ +# Support [![Average time to resolve an issue][1]][2] [![Percentage of issues still open][3]][2] + +Need help? Want to help this project? See below! + +## Reporting an Issue + +Before you begin, please [search open and past issues][4] to see if your issue +or feature request is already being talked about. + +If you find your issue already exists, make sure to [bump the issue by adding a +reaction][5]. **If you're serious about wanting an issue to get attention, use a +reaction in place of a "+1" or other similar comment.** + +If you cannot find an existing issue that describes your bug or feature, then +you're clear to create your issue using the guidelines below. + +### Standard Reactions + +Analysis of issue popularity (by tooling or otherwise) relies on the following +reactions being present on issues, PRs, and replies: + +:+1: 👍🏿👍🏾👍🏽👍🏼👍🏻👍 — up vote (also: _approve_, _like_, _+1_) + +\:-1: 👎🏿👎🏾👎🏽👎🏼👎🏻👎 — down vote (also: _disapprove_, _dislike_, _-1_) + +### Bug Reports and Feature Requests + +For the timeliest resolution to bug reports specifically, please make a good +faith effort to [follow the template][6]. The more quality information you can +provide, the quicker a fix will be found. Additionally: + +- Ensure you file one issue per problem or feature request +- Do not enumerate multiple bugs or feature requests in the same issue +- Do not add the issue as a comment to an existing issue unless you're + experiencing identical behavior or behavior that stems from the exact same + issue + +That's it! You are now ready to [submit a new issue][6]! Thank you for +contribution 🎉, your efforts are greatly appreciated! 🙌🏿 + +#### Closure Policy + +- Issues that do not follow the appropriate template or contain necessary + information will be closed immediately and the poster directed to these + guidelines +- Issues that go for an extended period without activity are subject to closure + +[1]: + https://isitmaintained.com/badge/resolution/Xunnamius/projector-lens-lib-cjs.svg +[2]: + https://isitmaintained.com/project/Xunnamius/projector-lens-lib-cjs + 'Average time to resolve an issue' +[3]: https://isitmaintained.com/badge/open/Xunnamius/projector-lens-lib-cjs.svg +[4]: https://github.com/Xunnamius/projector-lens-lib-cjs/issues?q= +[5]: + https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments +[6]: https://github.com/Xunnamius/projector-lens-lib-cjs/issues/new/choose diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..26d18ee --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + commit-message: + prefix: 'chore' + prefix-development: 'chore' + include: 'scope' + target-branch: 'canary' + + - package-ecosystem: npm + directory: / + schedule: + interval: daily + commit-message: + prefix: 'build' + prefix-development: 'chore' + include: 'scope' + target-branch: 'canary' diff --git a/.github/pipeline.config.js b/.github/pipeline.config.js new file mode 100644 index 0000000..330ac3b --- /dev/null +++ b/.github/pipeline.config.js @@ -0,0 +1,60 @@ +/** + * This object is used to configure the GitHub Actions that comprise the + * build-test-deploy pipeline. Each property is optional. + */ +module.exports = { + // * The name and email used to author commits and interact with GitHub. + // ! This should correspond to the identity of the GH_TOKEN secret. + // committer: { + // name: 'xunn-bot', + // email: 'bot@xunn.io' + // }, + // + // * Selectively enable debugger verbose output in the pipeline. + // ? To enable debugging across all source in this repo (excluding + // ? node_modules) without having to type the package name, use a boolean: + // debugString: true, // false is treated the same as null/commented out + // ? Or you can type out a custom debug namespace string instead, e.g.: + // debugString: '@your-namespace/some-package:*', + // ? This key can only appear in local pipeline config and not global. + // ? See also: https://www.npmjs.com/package/debug#wildcards + // ? For even more debugging tools, see: https://bit.ly/2R6NAdZ + // + // * The semver version of node to install and setup before each job. + // nodeCurrentVersion: '18.x', + // + // * Node semver versions to run unit and integration tests against. + // nodeTestVersions: ['14.x', '16.x', '18.x'], + // + // * Webpack semver versions to run unit and integration tests against. + // webpackTestVersions: ['5.x'], + // + // * Regular expressions for skipping CI/CD. To skip CL, use git with the + // * `--no-verify` option. + // ciSkipRegex: /\[skip ci\]|\[ci skip\]/i, + // cdSkipRegex: /\[skip cd\]|\[cd skip\]/i, + // + // * Should auto-merge be retried on failure even when the PR appears + // * unmergeable? If `true`, uses exponential back-off internally. + // ! WARNING: leaving this as `true` might waste Actions minutes and $$$ in + // ! private repositories. Watch your usage numbers! + // canRetryAutomerge: true, + // + // * Npm audit will fail upon encountering problems of at least the specified + // * severity. + // npmAuditFailLevel: 'high', + // + // * Attempt to upload project coverage data to codecov if `true`. + // canUploadCoverage: true, + // + // * The maximum amount of time in seconds any "retry"-type operation can + // * continue retrying. This includes all exponential backoff steps. + // ! A 5 minute limit is hardcoded into pipeline workflows, so values above + // ! ~250 might lead to undesirable VM hard stops. + // retryCeilingSeconds: 180, + // + // * How many days GitHub should keep uploaded artifacts around. + // ! 90 days is GitHub's default, but this should be lowered to 1 day for + // ! private repos where artifact storage costs $$$. + // artifactRetentionDays: 90 +}; diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..2b6db42 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,6 @@ +Note: the `build-test`, `deploy`, `cleanup`, and `post-release-check` workflows +are part of the [Projector][1] pipeline. Details on pipeline design and +operation can be found [here][2]. + +[1]: https://github.com/Xunnamius/projector +[2]: https://github.com/Xunnamius/projector-pipeline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd1c650 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.ignore +*.ignore.* +ignore.* +*.tsbuildinfo +dist +build +.vscode +.vercel +.env +external-scripts/bin +/next-env.d.ts +node_modules +coverage diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..62eaff5 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,7 @@ +#!/bin/sh +. "$(dirname $0)/_/husky.sh" + +npx commitlint -e +if [ -z $GAC_VERIFY_SIMPLE ]; then npm run test; fi +echo +node spellcheck-commit.js diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..9e70d49 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname $0)/_/husky.sh" + +if [ -z $GAC_VERIFY_SIMPLE ]; then npm run lint; fi +npx lint-staged --concurrent false diff --git a/.ncurc.js b/.ncurc.js new file mode 100644 index 0000000..881af99 --- /dev/null +++ b/.ncurc.js @@ -0,0 +1,22 @@ +// * https://www.npmjs.com/package/npm-check-updates#configuration-files + +// TODO: remove outdated deps and exceptions (below) when externalizing libs to +// TODO: packages + +module.exports = { + reject: [ + // ? Pin the CJS version of execa + 'execa', + // ? Pin the CJS version of node-fetch (and its types) + 'node-fetch', + '@types/node-fetch', + // ? Pin the CJS version of find-up + 'find-up', + // ? Pin the CJS version of chalk + 'chalk', + // ? Pin the CJS version of auto-bind + 'auto-bind', + // ? Pin the CJS version of inquirer + 'inquirer' + ] +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..5e930eb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +/build +/external-scripts/bin +/node_modules +/dist +/coverage +/package-lock.json diff --git a/.spellcheckignore b/.spellcheckignore new file mode 100644 index 0000000..e69de29 diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 0000000..2cecbe8 --- /dev/null +++ b/.vercelignore @@ -0,0 +1,22 @@ +# This file's syntax is only KINDA similar like .gitignore... + +# Ignore all root-level files +/* + +# Re-add root-level dirs +!/data +!/lib +!/public +!/src +!/types + +# Re-add root-level files +!/.gitignore +!/babel.config.js +!/next.config.js +!/package-lock.json +!/package.json +!/tsconfig.json + +# Custom adds +!/expect-env.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c42f2a4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,191 @@ +# Contributing + +Hi there! First off, we're thrilled 🤩 you want contribute to this project! + +First time contributor to a GitHub project? If you could use some help getting +started, [take a look at this quick and easy guide][1]. 💜 + +## Briefly: Submitting a Pull Request (PR) + +> See also: [CODE_OF_CONDUCT.md][2] + +This repository uses a [fully automated][3] [continuous linting][4] (CL), +[integration testing][5] (CI), and [deployment][5] (CD) +[semantic-release][6]-based pipeline for integrating PRs and publishing +releases. The nice thing about a fully automated CL/CI/CD pipeline is that +anyone anywhere can make a contribution quickly and with minimal tedium all +around! + +This repository makes extensive use of [debug][7] (through [rejoinder][8]). +Should you wish to view all possible debugging output, [export `DEBUG='*'`][9]. +To get debugging output for just this project's components, [export +`DEBUG='blogpress.api.hscc.bdpa.org:*'`][9]. + +The ideal contributor flow is as follows: + +1. [Fork][10] this repository and [clone it locally][11]. +2. Configure and install dependencies with `npm ci`. + - You use `npm ci` here instead of `npm install` to [prevent unnecessary + updates to `package.json` and `package-lock.json`][12], but if it makes + more sense to use `npm install` feel free to use that instead. + - If `.env.example` exists, consider copying it to `.env` and configuring + sensible defaults. + - If you're using `npm@<=6`, you'll need to install any [peer + dependencies][13] manually. If you're using `npm@>=7`, you may have to + [forcefully][14] allow peer deps to be satisfied by custom forks of + certain packages. +3. Before making any changes, ensure all unit tests are passing with + `npm run test`. +4. _(optional but recommended)_ Create a new branch, usually off `main`. + - Example: `git checkout -b contrib-feature-1` +5. Make your changes and commit. Thanks to CL, your work will be checked as you + commit it; any problems will abort the commit attempt + - Ensure any new tests still pass even when the `DEBUG` environment variable + is defined. + - Various [import aliases][24] are available in some projects. Check the + [tsconfig.json][25] `"paths"` key to see which if any aliases this project + supports. +6. Push your commits to your fork and, when you're ready, [_fearlessly_ submit + your PR][15]! Your changes will be tested in our CI pipeline. +7. Pat yourself on the back! Your hard work is well on its way to being + reviewed and, if everything looks good, merged and released 🚀 + +Additionally, there are a few things you can do to increase the likelihood your +PR passes review: + +- **Do** [open an issue][16] and discuss your proposed changes (to prevent + wasting your valuable time, e.g. _maybe we're already working on a fix!_), and + [search][17] to see if there are any existing issues related to your concerns +- **Do** practice [atomic committing][18] +- **Do not** reduce code coverage ([codecov][19] checks are performed during CI) +- **Do** [follow convention][20] when coming up with your commit messages +- **Do not** circumvent CL, i.e. automated pre-commit linting, formatting, and + unit testing +- **Do** ensure `README.md` and other documentation that isn't autogenerated is + kept consistent with your changes +- **Do not** create a PR to introduce [_purely_ cosmetic commits][21] + - Code de-duplication and other potential optimizations we **do not** consider + _purely_ cosmetic 🙂 +- **Do** ensure your tests still pass in the presence of the `DEBUG` environment + variable +- **Do** keep your PR as narrow and focused as possible + - If you ran `npm install` instead of `npm ci` and it updated `package.json` + or `package-lock.json` and those updates have nothing to do with your PR + (e.g. random nested deps were updated), **do not** stage changes to those + files + - If there are multiple related changes to be made but (1) they do not + immediately depend on one another or (2) one implements extended/alternative + functionality based on the other, consider submitting them as separate PRs + instead 👍🏿 + +> Be aware: all contributions to this project, regardless of committer, origin, +> or context and immediately upon push to this repository, are [released][22] in +> accordance with [this project's license][23]. + +At this point, you're ready to create your PR and ✨ contribute ✨! + +## npm Scripts + +This repo ships with several [npm scripts][49]. Use `npm run list-tasks` to see +which of the following scripts are available for this project. + +> Using these scripts requires a linux-like development environment. None of the +> scripts are likely to work on non-POSIX environments. If you're on Windows, +> use [WSL][50]. + +### Developing + +- `npm run dev` to start a development server or instance +- `npm run lint` to run a project-wide type check (handled by CL/CI) +- `npm run test` (or `npm test`, `npm run test-unit`) to run the unit tests + (handled by CL/CI) + - Also [gathers test coverage data][51] as HTML files (under `coverage/`) + - Can also run `npm run test-integration` to run all the integration tests +- `npm run test-integration-node` to run integration tests on the last three LTS + Node versions (handled by CI) +- `npm run test-integration-client` to run client (browser/cli/etc) integration + tests with [puppeteer][52] (handled by CI) +- `npm run test-integration-webpack` to run tests verifying the distributable + can be bundled with Webpack 5 (as both ESM and CJS) (handled by CI) +- `npm run test-integration-externals` to run tests on compiled external + executables (under `external-scripts/bin/`) (handled by CI) + +#### Other Development Scripts + +- `npm run test-repeat` to run the entire test suite 100 times + - Good for spotting bad async code and heisenbugs +- `npm run generate` to transpile config files (under `config/`) from scratch +- `npm run regenerate` to quickly re-transpile config files (under `config/`) +- `npm run postinstall` to (re-)install [Husky Git hooks][53] if not in a CI + environment (handled by npm) + +### Building and Deploying + +- `npm run build` (alias: `npm run build-dist`) to compile `src/` into `dist/` + (or `build/`), which is what ships to production (handled by CI/CD) +- `npm run format` to run source formatting over the codebase (handled by + CL/CI/CD) +- `npm run start` to deploy a _local production mode_ instance +- `npm run deploy` to deploy to production (bring your own auth tokens) (handled + by CD) + +#### Other Build Scripts + +- `npm run clean` to delete all build process artifacts (except `node_modules/`) +- `npm run build-changelog` to re-build the changelog (handled by CI/CD) + - You can run this as `CHANGELOG_SKIP_TITLE=true npm run build-changelog` to + skip prepending the header +- `npm run build-docs` to re-build the documentation (handled by CI/CD) +- `npm run build-externals` to compile `external-scripts/` into + `external-scripts/bin/` +- `npm run build-stats` to gather statistics about Webpack (look for + `bundle-stats.ignore.json`) + +### NPX Scripts + +> These commands might be installed as a project dependency but are expected to +> be run using [`npx X`][54] instead of `npm run X` regardless. + +- `npx npm-force-resolutions` to forcefully patch security audit problems + (rarely necessary) +- `npx semantic-release -d` to run the CD pipeline locally (in [dry-run + mode][55]) + +[1]: https://www.dataschool.io/how-to-contribute-on-github +[2]: /.github/CODE_OF_CONDUCT.md +[3]: https://github.com/features/actions +[4]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/tree/main/.husky +[5]: .github/workflows/build-test.yml +[6]: https://github.com/semantic-release/semantic-release#readme +[7]: https://www.npmjs.com/package/debug +[8]: https://www.npmjs.com/package/rejoinder +[9]: https://www.npmjs.com/package/debug#wildcards +[10]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/fork +[11]: + https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/cloning-a-repository +[12]: https://docs.npmjs.com/cli/v6/commands/npm-ci +[13]: + https://docs.npmjs.com/cli/v6/configuring-npm/package-json#peerdependencies +[14]: + https://docs.npmjs.com/cli/v7/commands/npm-install#configuration-options-affecting-dependency-resolution-and-tree-design +[15]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/compare +[16]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/issues/new/choose +[17]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/issues?q= +[18]: https://www.codewithjason.com/atomic-commits-testing/ +[19]: https://about.codecov.io/ +[20]: https://www.conventionalcommits.org/en/v1.0.0/#summary +[21]: https://github.com/rails/rails/pull/13771#issuecomment-32746700 +[22]: + https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license +[23]: LICENSE +[49]: https://docs.npmjs.com/cli/v6/commands/npm-run-script +[50]: https://docs.microsoft.com/en-us/windows/wsl/install-win10 +[51]: https://jestjs.io/docs/en/cli.html#--coverageboolean +[52]: https://github.com/puppeteer/puppeteer +[53]: https://github.com/typicode/husky +[54]: https://www.npmjs.com/package/npx +[55]: + https://semantic-release.gitbook.io/semantic-release/usage/configuration#dryrun +[24]: + https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping +[25]: tsconfig.json diff --git a/README.md b/README.md index 28ca353..836378a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # blogpress.api.hscc.bdpa.org -Live API used by solutions to the BlogPress NHSCC problem statement. + +Live API used by solutions to the BlogPress NHSCC problem statement. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..472cf19 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security ☠️ [![Known Vulnerabilities][2]][1] + +If the issue is related to a public alert from OWASP/GitHub/Dependabot/CVE/etc +and [does not already have an open issue][3], feel free to [open a new +issue][4]. Otherwise, please report any security vulnerability, other +security-related incident, or otherwise sensitive subject to us [via email][5]. + +Thank you for your contribution! + +[1]: https://snyk.io/test/github/Xunnamius/blogpress.api.hscc.bdpa.org +[2]: https://snyk.io/test/github/Xunnamius/blogpress.api.hscc.bdpa.org/badge.svg +[3]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/issues?q= +[4]: https://github.com/Xunnamius/blogpress.api.hscc.bdpa.org/issues/new/choose +[5]: + mailto:security@ergodark.com?subject=ALERT%3A%20SECURITY%20INCIDENT%3A%20%28five%20word%20summary%29 diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..ab408bb --- /dev/null +++ b/babel.config.js @@ -0,0 +1,96 @@ +'use strict'; +// * Every now and then, we adopt best practices from CRA +// * https://tinyurl.com/yakv4ggx + +const debug = require('debug')(`${require('./package.json').name}:babel-config`); + +// ! This is pretty aggressive. It targets modern browsers only. +// ? For some projects, less aggressive targets will make much more +// ? sense! +const browserTargets = + 'Chrome >= 60, Safari >= 10.1, iOS >= 10.3, Firefox >= 54, Edge >= 15'; +// ? Something like the following might be more appropriate: +//const targets = '>1% in US and not ie 11'; + +// ? Next.js-specific Babel settings +const nextBabelPreset = [ + 'next/babel', + { + 'preset-env': { + targets: browserTargets, + + // ? If users import all core-js they're probably not concerned with + // ? bundle size. We shouldn't rely on magic to try and shrink it. + useBuiltIns: false, + + // ? Do not transform modules to CJS + // ! MUST BE FALSE (see: https://nextjs.org/docs/#customizing-babel-config) + modules: false, + + // ? Exclude transforms that make all code slower + exclude: ['transform-typeof-symbol'] + }, + 'preset-typescript': { + allowDeclareFields: true + } + } +]; + +module.exports = { + parserOpts: { strictMode: true }, + plugins: [ + '@babel/plugin-proposal-export-default-from' + + // ? Interoperable named CJS imports for free + // [ + // 'transform-default-named-imports', + // { + // exclude: [/^next([/?#].+)?/, /^mongodb([/?#].+)?/] + // } + // ] + ], + // ? Sub-keys under the "env" config key will augment the above + // ? configuration depending on the value of NODE_ENV and friends. Default + // ? is: development + env: { + // * Used by Jest and `npm test` + test: { + sourceMaps: 'both', + presets: [ + ['@babel/preset-env', { targets: { node: true } }], + '@babel/preset-react', + ['@babel/preset-typescript', { allowDeclareFields: true }] + // ? We don't care about minification + ], + plugins: [ + // ? Only active when testing, the plugin solves the following problem: + // ? https://stackoverflow.com/q/40771520/1367414 + 'explicit-exports-references' + ] + }, + // * Used by Vercel, `npm start`, and `npm run build` + production: { + // ? Source maps are handled by Next.js and Webpack + presets: [nextBabelPreset] + // ? Minification is handled by Webpack + }, + // * Used by `npm run dev`; is also the default environment + development: { + // ? Source maps are handled by Next.js and Webpack + presets: [nextBabelPreset], + // ? https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers + plugins: ['@babel/plugin-transform-react-jsx-source'] + // ? We don't care about minification + }, + // * Used by `npm run build-externals` + external: { + presets: [ + ['@babel/preset-env', { targets: { node: true } }], + ['@babel/preset-typescript', { allowDeclareFields: true }] + // ? Minification is handled by Webpack + ] + } + } +}; + +debug('exports: %O', module.exports); diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..73f7aaf --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,28 @@ +'use strict'; + +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'body-leading-blank': [2, 'always'], + 'footer-leading-blank': [2, 'always'], + 'type-enum': [ + 2, + 'always', + [ + 'feat', + 'feature', + 'fix', + 'perf', + 'revert', + 'build', + 'docs', + 'style', + 'refactor', + 'test', + 'ci', + 'cd', + 'chore' + ] + ] + } +}; diff --git a/conventional.config.js b/conventional.config.js new file mode 100644 index 0000000..eb01d6a --- /dev/null +++ b/conventional.config.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports = require('@xunnamius/conventional-changelog-projector')({ + // * Your customizations here +}); diff --git a/expect-env.js b/expect-env.js new file mode 100644 index 0000000..d2cc0d9 --- /dev/null +++ b/expect-env.js @@ -0,0 +1,268 @@ +'use strict'; +/* eslint-disable no-console */ + +const debug = require('debug')(`${require('./package.json').name}:expect-env`); + +const DEFAULT_VALUE_REGEX = [/^.*$/, /.*/]; + +class IllegalEnvironmentError extends Error {} + +module.exports = { + /** + * This function accepts a single object parameter with a list of `rules` used + * to verify `env`. If `env` is not defined, `process.env` is used instead. If + * `rules` is not defined, the "rules" defined under the "expect-env" key in + * ./package.json are used instead. + * + * Below, "name" is the name of an environment variable and "value" is its + * expected value. + * + * Each rule can be one of: + * + * (1) A simple STRING variable "name" interpreted as RegExp(`^${STRING}$`) + * + * (2) An OBJECT where "name" and "value" are both regex STRINGs; "required" + * is optional and defaults to `true`; and "errorMessage" is optional: + * + * { + * "name": "^(MY_)?VARIABLE_NAME$", + * "value": "^(true|false)$", + * "required": true, // ◄ optional + * "errorMessage": "some custom error message" // ◄ optional + * } + * + * (3) An OBJECT where "operation" is either "xor", "or", "and", or "not"; + * "variables" is an array where each element is (1) or (2) (without + * "required" or "errorMessage"); "required" is optional and defaults to + * `true`; and "errorMessage" is optional: + * + * { + * "operation": "xor", + * "variables": ["^MY_V_1$", { name: "^MY_V_2$", value: "^.*$" }], + * "required": false, // ◄ optional + * "errorMessage": "some custom error message" // ◄ optional + * } + * + * When "operation" is "xor", exactly one of the elements in "variables" must + * match the environment. When it's "or", at least one of the elements must + * match (this is the default). When it's "and", all of the elements must + * match. When it's "not", exactly zero elements must match. When "required" + * is `false` and "operation" is something other than "not", rules matching + * non-existent variable names will be skipped (others will still be + * verified). + * + * With both (2) and (3), "errorMessage" is output to the console if the rule + * fails verification or was not found in `env` but "required" is `true`. + * + * When `isCli=false`, this function will return an array of objects each with + * the shape of (3) representing all the rules that failed to match against + * the current environment. An empty array is returned if all rules matched + * and verification succeeded. When `isCli=true` (the default), this function + * will output errors to the console and non-zero exit if verification fails. + */ + verifyEnvironment( + { rules, env: outsideEnv, isCli } = { + rules: undefined, + env: undefined, + isCli: true + } + ) { + let fromPkg = false; + let errorMessage = null; + + const normalize = (rule) => { + let normalizedRule; + const makeErrorMessage = (reason) => `missing dep: ${reason}`; + + debug('::normalize (not normalized): %O', rule); + + if (typeof rule == 'string' || rule instanceof RegExp) { + if (typeof rule == 'string') { + rule = rule.startsWith('^') ? rule.slice(1) : rule; + rule = rule.startsWith('$') ? rule.slice(0, -1) : rule; + rule = RegExp(`^${rule}$`); + } + + normalizedRule = { + operation: 'or', + variables: [{ name: rule, value: DEFAULT_VALUE_REGEX[0] }], + required: true, + errorMessage: String( + makeErrorMessage( + `must define environment variable with name matching regex ${rule}` + ) + ) + }; + } else if (typeof rule.name == 'string' || rule.name instanceof RegExp) { + rule.name = rule.name instanceof RegExp ? rule.name : RegExp(rule.name); + rule.value = + typeof rule.value == 'string' + ? RegExp(rule.value) + : rule.value instanceof RegExp + ? rule.value + : DEFAULT_VALUE_REGEX[0]; + + normalizedRule = { + operation: 'or', + variables: [{ name: rule.name, value: rule.value }], + required: typeof rule.required == 'undefined' || !!rule.required, + errorMessage: String( + rule.errorMessage || + makeErrorMessage( + `must define environment variable with name matching regex ` + + `${rule.name}${ + DEFAULT_VALUE_REGEX.map(String).includes(rule.value.toString()) + ? '' + : ' and value matching regex ' + rule.value + }` + ) + ) + }; + } else if ( + ['and', 'or', 'xor', 'not'].includes(rule.operation) && + Array.isArray(rule.variables) + ) { + const variables = rule.variables.map((r) => normalize(r).variables[0]); + const not = rule.operation == 'not'; + const opString = not ? ' ' : `${rule.operation} `; + + const makeSubstr = (name, value) => + `name ${not ? 'NOT ' : ''}matching regex ${name}` + + (DEFAULT_VALUE_REGEX.map(String).includes((value || '').toString()) + ? '' + : ` and value ${not ? 'NOT ' : ''}matching regex ${value}`); + + normalizedRule = { + operation: rule.operation, + variables, + required: typeof rule.required == 'undefined' || !!rule.required, + errorMessage: String( + rule.errorMessage || + makeErrorMessage( + `must define ${ + not ? 'ALL environment variables' : 'environment variable' + } with` + + (variables.length > 1 ? ':\n' : ' ') + + variables + .slice(1) + .reduce( + (str, { name, value }) => + `${str}\n${opString}${makeSubstr(name, value)}`, + `${' '.repeat(opString.length)}${makeSubstr( + variables[0].name, + variables[0].value + )}` + ) + ) + ) + }; + } else { + debug('::normalize BAD RULE ENCOUNTERED: %O', rule); + throw new IllegalEnvironmentError( + `bad rule encountered${ + fromPkg ? ' in ./package.json "expect-env"' : '' + }: ${JSON.stringify(rule, undefined, 2)}` + ); + } + + debug('::normalize (normalized): %O', normalizedRule); + return normalizedRule; + }; + + if (!rules) { + try { + ({ + 'expect-env': { rules, errorMessage } + } = require('./package.json')); + fromPkg = true; + } catch (ignored) {} + } + + debug('rules: %O', rules); + debug('errorMessage: %O', errorMessage); + + if (typeof rules == 'undefined') return []; + + if (!Array.isArray(rules)) { + throw new IllegalEnvironmentError( + fromPkg + ? '"expect-env" key must have an array value in package.json' + : '`rules` must be an array' + ); + } + + const env = outsideEnv || process.env; + const envVars = Object.keys(env); + const violations = []; + let verificationSucceeded = true; + + rules.map(normalize).forEach((rule) => { + let succeeded = null; + + for (const { name, value } of rule.variables) { + let matchedAtLeastOneVar = false; + + // ? Attempt to match variable names and values vs name and value + const matched = envVars.some((v) => { + const matchesName = name.test(v); + matchedAtLeastOneVar = matchedAtLeastOneVar || matchesName; + return matchesName && value.test(env[v]); + }); + + // ? If it's not required and not in env, skip evaluation + if (!rule.required && !matchedAtLeastOneVar) { + succeeded = true; + continue; + } + + if (rule.operation == 'or') { + if (matched) { + succeeded = true; + break; + } + } else if (rule.operation == 'and') { + if (matched) succeeded = true; + else { + succeeded = false; + break; + } + } else if (rule.operation == 'not') { + if (!matched) succeeded = true; + else { + succeeded = false; + break; + } + } else if (rule.operation == 'xor') { + if (succeeded === null) succeeded = !matched ? null : true; + else if (succeeded && matched) { + succeeded = false; + break; + } + } else { + throw new IllegalEnvironmentError( + `unrecognized operation "${rule.operation}"` + ); + } + } + + if (!succeeded) + isCli ? console.error(rule.errorMessage) : violations.push(rule); + verificationSucceeded = verificationSucceeded && succeeded; + }); + + if (!verificationSucceeded && isCli) + throw new IllegalEnvironmentError( + !errorMessage ? 'environment verification failed' : errorMessage + ); + + debug('violated rules: %O', violations); + return violations; + } +}; + +try { + !module.parent && module.exports.verifyEnvironment(); +} catch (e) { + console.error(e); + process.exit(1); +} diff --git a/external-scripts/README.md b/external-scripts/README.md new file mode 100644 index 0000000..5dce5c0 --- /dev/null +++ b/external-scripts/README.md @@ -0,0 +1,8 @@ +# External Scripts + +External scripts or "externals" are auxiliary executables that are co-located +with application source for ease of compilation and management. See each +external's documentation for usage instructions. + +Note that externals must first be compiled before they can be run, i.e. +`npm run build-externals`. diff --git a/external-scripts/ban-hammer.ts b/external-scripts/ban-hammer.ts new file mode 100644 index 0000000..31e5ea3 --- /dev/null +++ b/external-scripts/ban-hammer.ts @@ -0,0 +1,288 @@ +import { AppError, InvalidAppEnvironmentError } from 'named-app-errors'; + +import { debugNamespace as namespace } from 'universe/constants'; +import { getEnv } from 'universe/backend/env'; + +import { getDb } from 'multiverse/mongo-schema'; +import { debugFactory } from 'multiverse/debug-extended'; + +const debugNamespace = `${namespace}:ban-hammer`; + +const oneSecondInMs = 1000; +const log = debugFactory(debugNamespace); +const debug = debugFactory(`${debugNamespace}:debug`); + +// eslint-disable-next-line no-console +log.log = console.info.bind(console); + +// ? Ensure this next line survives Webpack +if (!globalThis.process.env.DEBUG && getEnv().NODE_ENV != 'test') { + debugFactory.enable( + `${debugNamespace},${debugNamespace}:*,-${debugNamespace}:debug` + ); +} + +/** + * Pores over request-log entries and drops the ban hammer on rule breaking + * clients. + */ +const invoked = async () => { + try { + const { + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: calledEverySeconds, + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: maxRequestsPerWindow, + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: resolutionWindowSeconds, + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: defaultBanTimeMinutes, + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: punishMultiplier + } = getEnv(); + + if (!calledEverySeconds || !(Number(calledEverySeconds) > 0)) { + throw new InvalidAppEnvironmentError( + 'BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS must be greater than zero' + ); + } + + if (!maxRequestsPerWindow || !(Number(maxRequestsPerWindow) > 0)) { + throw new InvalidAppEnvironmentError( + 'BAN_HAMMER_MAX_REQUESTS_PER_WINDOW must be greater than zero' + ); + } + + if (!resolutionWindowSeconds || !(Number(resolutionWindowSeconds) > 0)) { + throw new InvalidAppEnvironmentError( + 'BAN_HAMMER_RESOLUTION_WINDOW_SECONDS must be greater than zero' + ); + } + + if (!defaultBanTimeMinutes || !(Number(defaultBanTimeMinutes) > 0)) { + throw new InvalidAppEnvironmentError( + 'BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES must be greater than zero' + ); + } + + if (!punishMultiplier || !(Number(punishMultiplier) > 0)) { + throw new InvalidAppEnvironmentError( + 'BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER must be greater than zero' + ); + } + + const calledEveryMs = oneSecondInMs * calledEverySeconds; + const defaultBanTimeMs = oneSecondInMs * 60 * defaultBanTimeMinutes; + const resolutionWindowMs = oneSecondInMs * resolutionWindowSeconds; + const db = await getDb({ name: 'root' }); + + const pipeline = [ + { + $limit: 1 + }, + { + $project: { _id: 1 } + }, + { + $project: { _id: 0 } + }, + { + $lookup: { + from: 'request-log', + as: 'headerBased', + pipeline: [ + { + $match: { + header: { $ne: null }, + $expr: { + $gte: [ + '$createdAt', + { $subtract: [{ $toLong: '$$NOW' }, calledEveryMs] } + ] + } + } + }, + { + $group: { + _id: { + header: '$header', + interval: { + $subtract: [ + '$createdAt', + { $mod: ['$createdAt', resolutionWindowMs] } + ] + } + }, + count: { $sum: 1 } + } + }, + { + $match: { + count: { $gt: maxRequestsPerWindow } + } + }, + { + $project: { + header: '$_id.header', + until: { $add: [{ $toLong: '$$NOW' }, defaultBanTimeMs] } + } + }, + { + $project: { + _id: 0, + count: 0 + } + } + ] + } + }, + { + $lookup: { + from: 'request-log', + as: 'ipBased', + pipeline: [ + { + $match: { + $expr: { + $gte: [ + '$createdAt', + { $subtract: [{ $toLong: '$$NOW' }, calledEveryMs] } + ] + } + } + }, + { + $group: { + _id: { + ip: '$ip', + interval: { + $subtract: [ + '$createdAt', + { $mod: ['$createdAt', resolutionWindowMs] } + ] + } + }, + count: { $sum: 1 } + } + }, + { + $match: { + count: { $gt: maxRequestsPerWindow } + } + }, + { + $project: { + ip: '$_id.ip', + until: { $add: [{ $toLong: '$$NOW' }, defaultBanTimeMs] } + } + }, + { + $project: { + _id: 0, + count: 0 + } + } + ] + } + }, + { + $lookup: { + from: 'limited-log', + as: 'previous', + pipeline: [ + { + $match: { + $expr: { + $gte: [ + '$until', + { + $subtract: [ + { $toLong: '$$NOW' }, + defaultBanTimeMs * punishMultiplier + ] + } + ] + } + } + }, + { + $project: { + _id: 0 + } + } + ] + } + }, + { + $project: { + union: { $concatArrays: ['$headerBased', '$ipBased', '$previous'] } + } + }, + { + $unwind: { + path: '$union' + } + }, + { + $replaceRoot: { + newRoot: '$union' + } + }, + { + $group: { + _id: { + ip: '$ip', + header: '$header' + }, + count: { + $sum: 1 + }, + until: { + $max: '$until' + } + } + }, + { + $set: { + until: { + $cond: { + if: { $ne: ['$count', 1] }, + then: { + $max: [ + { + $add: [{ $toLong: '$$NOW' }, defaultBanTimeMs * punishMultiplier] + }, + '$until' + ] + }, + else: '$until' + } + }, + ip: '$_id.ip', + header: '$_id.header' + } + }, + { + $project: { + count: 0, + _id: 0 + } + }, + { + $out: 'limited-log' + } + ]; + + debug('aggregation pipeline: %O', pipeline); + + const cursor = db.collection('request-log').aggregate(pipeline); + + await cursor.next(); + await cursor.close(); + + log('execution complete'); + process.exit(0); + } catch (e) { + throw new AppError(`${e}`); + } +}; + +export default invoked().catch((e: Error) => { + log.error(e.message); + process.exit(2); +}); diff --git a/external-scripts/initialize-data-cache-20220809.json b/external-scripts/initialize-data-cache-20220809.json new file mode 100644 index 0000000..b624ab5 --- /dev/null +++ b/external-scripts/initialize-data-cache-20220809.json @@ -0,0 +1,149342 @@ +{ + "questions": [ + { + "_id": "62f321bb082fcc3049e8fee4", + "title": "Length of a JavaScript object", + "title-lowercase": "length of a javascript object", + "creator": "Gareth Simpson", + "createdAt": 1218138141000, + "status": "open", + "text": "

I have a JavaScript object. Is there a built-in or accepted best practice way to get the length of this object?

\n
const myObject = new Object();\nmyObject["firstname"] = "Gareth";\nmyObject["lastname"] = "Simpson";\nmyObject["age"] = 21;\n
\n", + "upvotes": 5325, + "upvoterUsernames": [], + "downvotes": 2479, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2582864, + "answers": 43, + "answerItems": [ + { + "_id": "62f321c7082fcc3049e90a2c", + "creator": "doekman", + "createdAt": 1218223125000, + "text": "

Here's how and don't forget to check that the property is not on the prototype chain:

\n\n
var element_count = 0;\nfor(var e in myArray)\n    if(myArray.hasOwnProperty(e))\n        element_count++;\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a2b", + "creator": "jj33", + "createdAt": 1218138739000, + "text": "

I'm not a JavaScript expert, but it looks like you would have to loop through the elements and count them since Object doesn't have a length method:

\n\n
var element_count = 0;\nfor (e in myArray) {  if (myArray.hasOwnProperty(e)) element_count++; }\n
\n\n

@palmsey: In fairness to the OP, the JavaScript documentation actually explicitly refer to using variables of type Object in this manner as \"associative arrays\".

\n", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270e082fcc3049e922a9", + "creator": "Lajos Mészáros", + "createdAt": 1381231977000, + "text": "Will not work, because it will count methods too, that are added through prototype.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a2e", + "creator": "James Coglan", + "createdAt": 1218270664000, + "text": "

Updated answer

\n

Here's an update as of 2016 and widespread deployment of ES5 and beyond. For IE9+ and all other modern ES5+ capable browsers, you can use Object.keys() so the above code just becomes:

\n
var size = Object.keys(myObj).length;\n
\n

This doesn't have to modify any existing prototype since Object.keys() is now built-in.

\n

Edit: Objects can have symbolic properties that can not be returned via Object.key method. So the answer would be incomplete without mentioning them.

\n

Symbol type was added to the language to create unique identifiers for object properties. The main benefit of the Symbol type is the prevention of overwrites.

\n

Object.keys or Object.getOwnPropertyNames does not work for symbolic properties. To return them you need to use Object.getOwnPropertySymbols.

\n

\r\n
\r\n
var person = {\n  [Symbol('name')]: 'John Doe',\n  [Symbol('age')]: 33,\n  \"occupation\": \"Programmer\"\n};\n\nconst propOwn = Object.getOwnPropertyNames(person);\nconsole.log(propOwn.length); // 1\n\nlet propSymb = Object.getOwnPropertySymbols(person);\nconsole.log(propSymb.length); // 2
\r\n
\r\n
\r\n

\n

Older answer

\n

The most robust answer (i.e. that captures the intent of what you're trying to do while causing the fewest bugs) would be:

\n

\r\n
\r\n
Object.size = function(obj) {\n  var size = 0,\n    key;\n  for (key in obj) {\n    if (obj.hasOwnProperty(key)) size++;\n  }\n  return size;\n};\n\n// Get the size of an object\nconst myObj = {}\nvar size = Object.size(myObj);
\r\n
\r\n
\r\n

\n

There's a sort of convention in JavaScript that you don't add things to Object.prototype, because it can break enumerations in various libraries. Adding methods to Object is usually safe, though.

\n
\n", + "upvotes": 3139, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3270e082fcc3049e922ab", + "creator": "Tres", + "createdAt": 1308881037000, + "text": "@vsync You are very correct. One should always implement necessary sanity checks :)", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922ad", + "creator": "alexserver", + "createdAt": 1363284463000, + "text": "@James Coglan why are you declaring the function within Object class and not just as a regular function ? i.e. function getSize(obj) {...} ?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922af", + "creator": "Clain Dsilva", + "createdAt": 1390918433000, + "text": "This method is however more buggy, especially when hasownprop seldom clears out when deleting object keys with delete keyword.", + "upvotes": 135, + "upvoterUsernames": [], + "downvotes": 135, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922b1", + "creator": "Muhammad Umer", + "createdAt": 1424823255000, + "text": "Why is everyone ignoring this: Object.keys(obj).length", + "upvotes": 240, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922b2", + "creator": "stonyau", + "createdAt": 1452319598000, + "text": "Re Object.keys(obj).length: That's because IE8 don't support this method.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922b4", + "creator": "Matthew Layton", + "createdAt": 1522831957000, + "text": "Not sure if it has been mentioned already because TL;DR but extending JavaScript's built in objects is frowned upon.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922b6", + "creator": "MadeInDreams", + "createdAt": 1607587064000, + "text": "I like that "while causing the fewest bugs"", + "upvotes": 4156, + "upvoterUsernames": [], + "downvotes": 4156, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a2d", + "creator": "Polsonby", + "createdAt": 1218234896000, + "text": "
\n
\n

@palmsey: In fairness to the OP, the JavaScript documentation actually explicitly refer to using variables of type Object in this manner as "associative arrays".

\n
\n
\n

And in fairness to @palmsey he was quite correct. They aren't associative arrays; they're definitely objects :) - doing the job of an associative array. But as regards to the wider point, you definitely seem to have the right of it according to this rather fine article I found:

\n

JavaScript “Associative Arrays” Considered Harmful

\n

But according to all this, the accepted answer itself is bad practice?

\n
\n
\n

Specify a prototype size() function for Object

\n
\n
\n

If anything else has been added to Object .prototype, then the suggested code will fail:

\n
<script type="text/javascript">\nObject.prototype.size = function () {\n  var len = this.length ? --this.length : -1;\n    for (var k in this)\n      len++;\n  return len;\n}\nObject.prototype.size2 = function () {\n  var len = this.length ? --this.length : -1;\n    for (var k in this)\n      len++;\n  return len;\n}\nvar myArray = new Object();\nmyArray["firstname"] = "Gareth";\nmyArray["lastname"] = "Simpson";\nmyArray["age"] = 21;\nalert("age is " + myArray["age"]);\nalert("length is " + myArray.size());\n</script>\n
\n

I don't think that answer should be the accepted one as it can't be trusted to work if you have any other code running in the same execution context. To do it in a robust fashion, surely you would need to define the size method within myArray and check for the type of the members as you iterate through them.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a2f", + "creator": "aeosynth", + "createdAt": 1301795064000, + "text": "

If you know you don't have to worry about hasOwnProperty checks, you can use the Object.keys() method in this way:

\n
Object.keys(myArray).length\n
\n", + "upvotes": 3462, + "upvoterUsernames": [], + "downvotes": 1464, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270f082fcc3049e922ba", + "creator": "KOVIKO", + "createdAt": 1328130748000, + "text": "@ripper234 That depends on your requirements.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922bc", + "creator": "Ben", + "createdAt": 1342146398000, + "text": "According to that table, IE8 supports 4 / 34 of the "standards". Wow.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922bd", + "creator": "mdup", + "createdAt": 1344857115000, + "text": "time to switch to firefox = unfortunately, you switching doesn't mean your website's users will...", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922bf", + "creator": "John Dvorak", + "createdAt": 1355380063000, + "text": "@ripper234 no IE support = time to polyfill", + "upvotes": 138, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922c1", + "creator": "GabLeRoux", + "createdAt": 1376863971000, + "text": "At least, it works in node.js server side :) Thanks for this answer.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922c3", + "creator": "Steel Brain", + "createdAt": 1421646607000, + "text": "creating a new array just to count the length of it? It's an overkill of resources", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922c4", + "creator": "Francisco Hodge", + "createdAt": 1432146766000, + "text": "@albanx That attitude won't get you far in web development. Websites are made for the visitors, not for the developers.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922c6", + "creator": "Oriol", + "createdAt": 1480374225000, + "text": "map creates an array with the same length. Did you mean filter?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a31", + "creator": "Jānis Elmeris", + "createdAt": 1311946914000, + "text": "

For some cases it is better to just store the size in a separate variable. Especially, if you're adding to the array by one element in one place and can easily increment the size. It would obviously work much faster if you need to check the size often.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a30", + "creator": "DanMan", + "createdAt": 1307807096000, + "text": "

To not mess with the prototype or other code, you could build and extend your own object:

\n\n
function Hash(){\n    var length=0;\n    this.add = function(key, val){\n         if(this[key] == undefined)\n         {\n           length++;\n         }\n         this[key]=val;\n    }; \n    this.length = function(){\n        return length;\n    };\n}\n\nmyArray = new Hash();\nmyArray.add(\"lastname\", \"Simpson\");\nmyArray.add(\"age\", 21);\nalert(myArray.length()); // will alert 2\n
\n\n

If you always use the add method, the length property will be correct. If you're worried that you or others forget about using it, you could add the property counter which the others have posted to the length method, too.

\n\n

Of course, you could always overwrite the methods. But even if you do, your code would probably fail noticeably, making it easy to debug. ;)

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270f082fcc3049e922c9", + "creator": "Emeka Mbah", + "createdAt": 1464810834000, + "text": "I think this is the best solution as it doesn't require looping with 'for' which could be costly if the array is big", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a32", + "creator": "Jerry", + "createdAt": 1314195988000, + "text": "

What about something like this --

\n\n
function keyValuePairs() {\n    this.length = 0;\n    function add(key, value) { this[key] = value; this.length++; }\n    function remove(key) { if (this.hasOwnProperty(key)) { delete this[key]; this.length--; }}\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a33", + "creator": "wade harrell", + "createdAt": 1321639601000, + "text": "

A variation on some of the above is:

\n\n
var objLength = function(obj){    \n    var key,len=0;\n    for(key in obj){\n        len += Number( obj.hasOwnProperty(key) );\n    }\n    return len;\n};\n
\n\n

It is a bit more elegant way to integrate hasOwnProp.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a34", + "creator": "ripper234", + "createdAt": 1341499140000, + "text": "

Updated: If you're using Underscore.js (recommended, it's lightweight!), then you can just do

\n\n
_.size({one : 1, two : 2, three : 3});\n=> 3\n
\n\n

If not, and you don't want to mess around with Object properties for whatever reason, and are already using jQuery, a plugin is equally accessible:

\n\n
$.assocArraySize = function(obj) {\n    // http://stackoverflow.com/a/6700/11236\n    var size = 0, key;\n    for (key in obj) {\n        if (obj.hasOwnProperty(key)) size++;\n    }\n    return size;\n};\n
\n", + "upvotes": 462, + "upvoterUsernames": [], + "downvotes": 155, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270f082fcc3049e922cd", + "creator": "Rayjax", + "createdAt": 1397204000000, + "text": "Underscore > (all -['lo-dash'])", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922cf", + "creator": "Minhaj", + "createdAt": 1446199367000, + "text": "underscorejs, things that should be under js :)", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922d1", + "creator": "NoobishPro", + "createdAt": 1486847093000, + "text": "Question: Why the extra if statement within the loop?", + "upvotes": 1221, + "upvoterUsernames": [], + "downvotes": 1221, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a36", + "creator": "Sherzod", + "createdAt": 1372290465000, + "text": "

Here's a different version of James Cogan's answer. Instead of passing an argument, just prototype out the Object class and make the code cleaner.

\n\n
Object.prototype.size = function () {\n    var size = 0,\n        key;\n    for (key in this) {\n        if (this.hasOwnProperty(key)) size++;\n    }\n    return size;\n};\n\nvar x = {\n    one: 1,\n    two: 2,\n    three: 3\n};\n\nx.size() === 3;\n
\n\n

jsfiddle example: http://jsfiddle.net/qar4j/1/

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270f082fcc3049e922d3", + "creator": "Dziad Borowy", + "createdAt": 1381747814000, + "text": "Object.prototype - bad idea.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3270f082fcc3049e922d5", + "creator": "Mohamad", + "createdAt": 1394024981000, + "text": "@tborychowski can you please explain why?", + "upvotes": 776, + "upvoterUsernames": [], + "downvotes": 776, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a35", + "creator": "Eric Anderson", + "createdAt": 1362418723000, + "text": "

Below is a version of James Coglan's answer in CoffeeScript for those who have abandoned straight JavaScript :)

\n\n
Object.size = (obj) ->\n  size = 0\n  size++ for own key of obj\n  size\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270f082fcc3049e922d8", + "creator": "Francisco Hodge", + "createdAt": 1449262909000, + "text": "The OP didn't ask for a CoffeeScript version, nor it is tagged as such. This is not a valid answer to the question.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a38", + "creator": "Joon", + "createdAt": 1378051926000, + "text": "

Here's the most cross-browser solution.

\n\n

This is better than the accepted answer because it uses native Object.keys if exists.\nThus, it is the fastest for all modern browsers.

\n\n
if (!Object.keys) {\n    Object.keys = function (obj) {\n        var arr = [],\n            key;\n        for (key in obj) {\n            if (obj.hasOwnProperty(key)) {\n                arr.push(key);\n            }\n        }\n        return arr;\n    };\n}\n\nObject.keys(obj).length;\n
\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a37", + "creator": "Ally", + "createdAt": 1375433372000, + "text": "

Here is a completely different solution that will only work in more modern browsers (Internet Explorer 9+, Chrome, Firefox 4+, Opera 11.60+, and Safari 5.1+)

\n

See this jsFiddle.

\n

Setup your associative array class

\n
/**\n * @constructor\n */\nAssociativeArray = function () {};\n\n// Make the length property work\nObject.defineProperty(AssociativeArray.prototype, "length", {\n    get: function () {\n        var count = 0;\n        for (var key in this) {\n            if (this.hasOwnProperty(key))\n                count++;\n        }\n        return count;\n    }\n});\n
\n

Now you can use this code as follows...

\n
var a1 = new AssociativeArray();\na1["prop1"] = "test";\na1["prop2"] = 1234;\na1["prop3"] = "something else";\nalert("Length of array is " + a1.length);\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a3a", + "creator": "Ron Sims II", + "createdAt": 1399661109000, + "text": "

Like most JavaScript problems, there are many solutions. You could extend the Object that for better or worse works like many other languages' Dictionary (+ first class citizens). Nothing wrong with that, but another option is to construct a new Object that meets your specific needs.

\n\n
function uberject(obj){\n    this._count = 0;\n    for(var param in obj){\n        this[param] = obj[param];\n        this._count++;\n    }\n}\n\nuberject.prototype.getLength = function(){\n    return this._count;\n};\n\nvar foo = new uberject({bar:123,baz:456});\nalert(foo.getLength());\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32710082fcc3049e922dc", + "creator": "zero_cool", + "createdAt": 1538433953000, + "text": "don't modify the prototype :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32710082fcc3049e922de", + "creator": "Ron Sims II", + "createdAt": 1538580902000, + "text": "Modifying the prototype of a factory function is the way you extend them.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a39", + "creator": "abo-elleef", + "createdAt": 1391928530000, + "text": "

If we have the hash

\n\n
\n

hash = {\"a\" : \"b\", \"c\": \"d\"};

\n
\n\n

we can get the length using the length of the keys which is the length of the hash:

\n\n
\n

keys(hash).length

\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32710082fcc3049e922e0", + "creator": "chim", + "createdAt": 1441274096000, + "text": "This is a great answer, however I can't find any documentation for this keys function. So I can't be confident on the cross browser support.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a3b", + "creator": "venkat7668", + "createdAt": 1404218434000, + "text": "

This method gets all your object's property names in an array, so you can get the length of that array which is equal to your object's keys' length.

\n\n
Object.getOwnPropertyNames({\"hi\":\"Hi\",\"msg\":\"Message\"}).length; // => 2\n
\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a3c", + "creator": "Eduardo Cuomo", + "createdAt": 1420571435000, + "text": "

Property

\n
Object.defineProperty(Object.prototype, 'length', {\n    get: function () {\n        var size = 0, key;\n        for (key in this)\n            if (this.hasOwnProperty(key))\n                size++;\n        return size;\n    }\n});\n
\n

Use

\n
var o = {a: 1, b: 2, c: 3};\nalert(o.length); // <-- 3\no['foo'] = 123;\nalert(o.length); // <-- 4\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a3d", + "creator": "stylesenberg", + "createdAt": 1428762797000, + "text": "

Simple solution:

\n\n
  var myObject = {};      // ... your object goes here.\n\n  var length = 0;\n\n  for (var property in myObject) {\n    if (myObject.hasOwnProperty(property)){\n      length += 1;\n    }\n  };\n\n  console.log(length);    // logs 0 in my example.\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a3e", + "creator": "pcnate", + "createdAt": 1442344504000, + "text": "

If you are using AngularJS 1.x you can do things the AngularJS way by creating a filter and using the code from any of the other examples such as the following:

\n\n
// Count the elements in an object\napp.filter('lengthOfObject', function() {\n  return function( obj ) {\n    var size = 0, key;\n    for (key in obj) {\n      if (obj.hasOwnProperty(key)) size++;\n    }\n   return size;\n }\n})\n
\n\n

Usage

\n\n

In your controller:

\n\n
$scope.filterResult = $filter('lengthOfObject')($scope.object)\n
\n\n

Or in your view:

\n\n
<any ng-expression=\"object | lengthOfObject\"></any>\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32710082fcc3049e922e3", + "creator": "Francisco Hodge", + "createdAt": 1449263227000, + "text": "The OP has not asked for a AngularJS version. This is not a valid answer to the question.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a40", + "creator": "John Slegers", + "createdAt": 1457356433000, + "text": "

If you don't care about supporting Internet Explorer 8 or lower, you can easily get the number of properties in an object by applying the following two steps:

\n\n
    \n
  1. Run either Object.keys() to get an array that contains the names of only those properties that are enumerable or Object.getOwnPropertyNames() if you want to also include the names of properties that are not enumerable.
  2. \n
  3. Get the .length property of that array.
  4. \n
\n\n
\n\n

If you need to do this more than once, you could wrap this logic in a function:

\n\n
function size(obj, enumerablesOnly) {\n    return enumerablesOnly === false ?\n        Object.getOwnPropertyNames(obj).length :\n        Object.keys(obj).length;\n}\n
\n\n

How to use this particular function:

\n\n
var myObj = Object.create({}, {\n    getFoo: {},\n    setFoo: {}\n});\nmyObj.Foo = 12;\n\nvar myArr = [1,2,5,4,8,15];\n\nconsole.log(size(myObj));        // Output : 1\nconsole.log(size(myObj, true));  // Output : 1\nconsole.log(size(myObj, false)); // Output : 3\nconsole.log(size(myArr));        // Output : 6\nconsole.log(size(myArr, true));  // Output : 6\nconsole.log(size(myArr, false)); // Output : 7\n
\n\n

See also this Fiddle for a demo.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a3f", + "creator": "Pian0_M4n", + "createdAt": 1452562663000, + "text": "

You can always do Object.getOwnPropertyNames(myObject).length to get the same result as [].length would give for normal array.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a41", + "creator": "Oriol", + "createdAt": 1463595367000, + "text": "

If you need an associative data structure that exposes its size, better use a map instead of an object.

\n

\r\n
\r\n
const myMap = new Map();\n\nmyMap.set(\"firstname\", \"Gareth\");\nmyMap.set(\"lastname\", \"Simpson\");\nmyMap.set(\"age\", 21);\n\nconsole.log(myMap.size); // 3
\r\n
\r\n
\r\n

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a42", + "creator": "Mahendra Kulkarni", + "createdAt": 1464670143000, + "text": "

Use:

\n\n

\r\n
\r\n
var myArray = new Object();\r\nmyArray[\"firstname\"] = \"Gareth\";\r\nmyArray[\"lastname\"] = \"Simpson\";\r\nmyArray[\"age\"] = 21;\r\nobj = Object.keys(myArray).length;\r\nconsole.log(obj)
\r\n
\r\n
\r\n

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a43", + "creator": "christian Nguyen", + "createdAt": 1470587328000, + "text": "

The solution work for many cases and cross browser:

\n\n

Code

\n\n
var getTotal = function(collection) {\n\n    var length = collection['length'];\n    var isArrayObject =  typeof length == 'number' && length >= 0 && length <= Math.pow(2,53) - 1; // Number.MAX_SAFE_INTEGER\n\n    if(isArrayObject) {\n        return collection['length'];\n    }\n\n    i= 0;\n    for(var key in collection) {\n        if (collection.hasOwnProperty(key)) {\n            i++;\n        }\n    }\n\n    return i;\n};\n
\n\n

Data Examples:

\n\n
// case 1\nvar a = new Object();\na[\"firstname\"] = \"Gareth\";\na[\"lastname\"] = \"Simpson\";\na[\"age\"] = 21;\n\n//case 2\nvar b = [1,2,3];\n\n// case 3\nvar c = {};\nc[0] = 1;\nc.two = 2;\n
\n\n

Usage

\n\n
getLength(a); // 3\ngetLength(b); // 3\ngetLength(c); // 2\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a44", + "creator": "Mystical", + "createdAt": 1474076900000, + "text": "

You can simply use Object.keys(obj).length on any object to get its length. Object.keys returns an array containing all of the object keys (properties) which can come in handy for finding the length of that object using the length of the corresponding array. You can even write a function for this. Let's get creative and write a method for it as well (along with a more convienient getter property):

\n\n

\r\n
\r\n
function objLength(obj)\r\n{\r\n  return Object.keys(obj).length;\r\n}\r\n\r\nconsole.log(objLength({a:1, b:\"summit\", c:\"nonsense\"}));\r\n\r\n// Works perfectly fine\r\nvar obj = new Object();\r\nobj['fish'] = 30;\r\nobj['nullified content'] = null;\r\nconsole.log(objLength(obj));\r\n\r\n// It also works your way, which is creating it using the Object constructor\r\nObject.prototype.getLength = function() {\r\n   return Object.keys(this).length;\r\n}\r\nconsole.log(obj.getLength());\r\n\r\n// You can also write it as a method, which is more efficient as done so above\r\n\r\nObject.defineProperty(Object.prototype, \"length\", {get:function(){\r\n    return Object.keys(this).length;\r\n}});\r\nconsole.log(obj.length);\r\n\r\n// probably the most effictive approach is done so and demonstrated above which sets a getter property called \"length\" for objects which returns the equivalent value of getLength(this) or this.getLength()
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922e6", + "creator": "Sebastian Simon", + "createdAt": 1589017560000, + "text": "Also, how is writing a method “more efficient”?", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 92, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a46", + "creator": "MacroMan", + "createdAt": 1499181639000, + "text": "

A nice way to achieve this (Internet Explorer 9+ only) is to define a magic getter on the length property:

\n
Object.defineProperty(Object.prototype, "length", {\n    get: function () {\n        return Object.keys(this).length;\n    }\n});\n
\n

And you can just use it like so:

\n
var myObj = { 'key': 'value' };\nmyObj.length;\n
\n

It would give 1.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922e7", + "creator": "MacroMan", + "createdAt": 1565688695000, + "text": "Arguments against prototype modification aside, I personally have NEVER had a bug caused by it and for me is one of the strong points of JavaScript.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a45", + "creator": "solanki...", + "createdAt": 1495460543000, + "text": "

We can find the length of Object by using:

\n

\r\n
\r\n
const myObject = {};\nconsole.log(Object.values(myObject).length);
\r\n
\r\n
\r\n

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922e9", + "creator": "JSG", + "createdAt": 1623016542000, + "text": "Theoretically, his would be slower than the "keys" method if you have long values as it is directly accessing the values then counting them.", + "upvotes": 1007, + "upvoterUsernames": [], + "downvotes": 1007, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a47", + "creator": "saurabhgoyal795", + "createdAt": 1516271366000, + "text": "

Simply use this to get the length:

\n\n
Object.keys(myObject).length\n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922ec", + "creator": "Patata", + "createdAt": 1518527068000, + "text": "please explain how your answer differs from stackoverflow.com/a/6700/8632727", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922ee", + "creator": "Barry Michael Doyle", + "createdAt": 1522830407000, + "text": "Good, it's always better to name your variables according to what they actually are. Makes your code more readable to other devs.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922f0", + "creator": "Yunnosch", + "createdAt": 1527230667000, + "text": "Before the "myArray" -> "myObject" edit, this was identical to the second-highest upvoted answer.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922f2", + "creator": "Alberto Perez", + "createdAt": 1566899222000, + "text": "Same as past responses.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a48", + "creator": "tdjprog", + "createdAt": 1522971902000, + "text": "
var myObject = new Object();\nmyObject[\"firstname\"] = \"Gareth\";\nmyObject[\"lastname\"] = \"Simpson\";\nmyObject[\"age\"] = 21;\n
\n\n
    \n
  1. Object.values(myObject).length
  2. \n
  3. Object.entries(myObject).length
  4. \n
  5. Object.keys(myObject).length
  6. \n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922f5", + "creator": "siluveru kiran kumar", + "createdAt": 1561101802000, + "text": "Which one is faster among the above 3.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922f7", + "creator": "tdjprog", + "createdAt": 1562015497000, + "text": "Object.values(myObject).length", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922f9", + "creator": "siluveru kiran kumar", + "createdAt": 1562050940000, + "text": "how can we say that it Object.values(myObject).length is faster is there any example Thanks, @tdjprog", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922fa", + "creator": "tdjprog", + "createdAt": 1562117105000, + "text": "just try this in console:var myObject = {}; for (let i=0; i<10000000; i++) myObject[i] = i;", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922fb", + "creator": "Peter Mortensen", + "createdAt": 1594407106000, + "text": "On explanation would be in order. You can edit your answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a4a", + "creator": "Mithu A Quayium", + "createdAt": 1527694900000, + "text": "

The simplest way is like this:

\n
Object.keys(myobject).length\n
\n

Where myobject is the object of what you want the length of.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e922fc", + "creator": "Pang", + "createdAt": 1542252124000, + "text": "This appears to be just a repeat of this existing answer.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e922fe", + "creator": "Mystical", + "createdAt": 1548044594000, + "text": "@Pang agreed, and it does not provide any further context like other answers do.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a4c", + "creator": "Kamil Kiełczewski", + "createdAt": 1550031319000, + "text": "

Object.keys does not return the right result in case of object inheritance. To properly count object properties, including inherited ones, use for-in. For example, by the following function (related question):

\n
var objLength = (o,i=0) => { for(p in o) i++; return i }\n
\n

\r\n
\r\n
var myObject = new Object();\nmyObject[\"firstname\"] = \"Gareth\";\nmyObject[\"lastname\"] = \"Simpson\";\nmyObject[\"age\"] = 21;\n\nvar child = Object.create(myObject);\nchild[\"sex\"] = \"male\";\n\nvar objLength = (o,i=0) => { for(p in o) i++; return i }\n\nconsole.log(\"Object.keys(myObject):\", Object.keys(myObject).length, \"(OK)\");\nconsole.log(\"Object.keys(child)   :\", Object.keys(child).length, \"(wrong)\");\nconsole.log(\"objLength(child)     :\", objLength(child), \"(OK)\");
\r\n
\r\n
\r\n

\n", + "upvotes": 804, + "upvoterUsernames": [], + "downvotes": 804, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a4b", + "creator": "PythonProgrammi", + "createdAt": 1528267884000, + "text": "

\r\n
\r\n
<script>\r\nmyObj = {\"key1\" : \"Hello\", \"key2\" : \"Goodbye\"};\r\nvar size = Object.keys(myObj).length;\r\nconsole.log(size);\r\n</script>\r\n\r\n<p id=\"myObj\">The number of <b>keys</b> in <b>myObj</b> are: <script>document.write(size)</script></p>
\r\n
\r\n
\r\n

\n\n

This works for me:

\n\n
var size = Object.keys(myObj).length;\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a49", + "creator": "njmwas", + "createdAt": 1523269449000, + "text": "

\r\n
\r\n
var myObject = new Object();\r\nmyObject[\"firstname\"] = \"Gareth\";\r\nmyObject[\"lastname\"] = \"Simpson\";\r\nmyObject[\"age\"] = 21;\r\n\r\nvar size = JSON.stringify(myObject).length;\r\n\r\ndocument.write(size);
\r\n
\r\n
\r\n

\n\n

\r\n
\r\n
JSON.stringify(myObject)
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32711082fcc3049e92300", + "creator": "oligofren", + "createdAt": 1537519854000, + "text": "the question resolved around the number of properties. your answer just shows what the length of one (out of many!) serialized forms is.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32711082fcc3049e92301", + "creator": "Peter Mortensen", + "createdAt": 1594407077000, + "text": "On explanation would be in order. You can edit your answer.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a4d", + "creator": "Paul", + "createdAt": 1565964040000, + "text": "

I had a similar need to calculate the bandwidth used by objects received over a websocket. Simply finding the length of the Stringified object was enough for me.

\n\n
websocket.on('message', data => {\n    dataPerSecond += JSON.stringify(data).length;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32712082fcc3049e92303", + "creator": "miike3459", + "createdAt": 1570884601000, + "text": "This does not answer the question.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a4e", + "creator": "shaheb", + "createdAt": 1567917953000, + "text": "

Use Object.keys(myObject).length to get the length of object/array

\n

\r\n
\r\n
var myObject = new Object();\nmyObject[\"firstname\"] = \"Gareth\";\nmyObject[\"lastname\"] = \"Simpson\";\nmyObject[\"age\"] = 21;\n\nconsole.log(Object.keys(myObject).length); //3
\r\n
\r\n
\r\n

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a50", + "creator": "Sheelpriy", + "createdAt": 1583395671000, + "text": "

With the ECMAScript 6 in-built Reflect object, you can easily count the properties of an object:

\n
Reflect.ownKeys(targetObject).length\n
\n

It will give you the length of the target object's own properties (important).

\n
Reflect.ownKeys(target)\n
\n
\n

Returns an array of the target object's own (not inherited) property\nkeys.

\n
\n

Now, what does that mean? To explain this, let's see this example.

\n
function Person(name, age){\n  this.name = name;\n  this.age = age;\n}\n\nPerson.prototype.getIntro= function() {\n  return `${this.name} is ${this.age} years old!!`\n}\n\nlet student = new Person('Anuj', 11);\n\nconsole.log(Reflect.ownKeys(student).length) // 2\nconsole.log(student.getIntro()) // Anuj is 11 years old!!\n
\n

You can see here, it returned only its own properties while the object is still inheriting the property from its parent.

\n

For more information, refer this: Reflect API

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a4f", + "creator": "Soura Ghosh", + "createdAt": 1578747244000, + "text": "
const myObject = new Object();\nmyObject[\"firstname\"] = \"Gareth\";\nmyObject[\"lastname\"] = \"Simpson\";\nmyObject[\"age\"] = 21;\n\nconsole.log(Object.keys(myObject).length)\n\n// o/p 3\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32712082fcc3049e92305", + "creator": "Peter Mortensen", + "createdAt": 1594406597000, + "text": "What does "o/p 3" mean?", + "upvotes": 1571, + "upvoterUsernames": [], + "downvotes": 1571, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92306", + "creator": "Peter Mortensen", + "createdAt": 1594406629000, + "text": "An explanation of this answer would be in order.", + "upvotes": 451, + "upvoterUsernames": [], + "downvotes": 451, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92307", + "creator": "Sebastian Simon", + "createdAt": 1615802824000, + "text": "@PeterMortensen “o/p” probably means “output”.", + "upvotes": 120, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92308", + "creator": "Soura Ghosh", + "createdAt": 1615880170000, + "text": "o/p means output", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a51", + "creator": "Shashwat Gupta", + "createdAt": 1602008672000, + "text": "

Simple one liner:

\n

\r\n
\r\n
console.log(Object.values({id:\"1\",age:23,role_number:90}).length);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a52", + "creator": "Rashed Rahat", + "createdAt": 1618648604000, + "text": "

Try: Object.values(theObject).length

\n

\r\n
\r\n
const myObject = new Object();\nmyObject[\"firstname\"] = \"Gareth\";\nmyObject[\"lastname\"] = \"Simpson\";\nmyObject[\"age\"] = 21;\nconsole.log(Object.values(myObject).length);
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a53", + "creator": "Coni", + "createdAt": 1634854196000, + "text": "
 vendor = {1: "", 2: ""}\n const keysArray = Object.keys(vendor)\n const objectLength = keysArray.length\n console.log(objectLength)\n Result 2\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a54", + "creator": "dazzafact", + "createdAt": 1639931612000, + "text": "

Here you can give any kind of varible array,object,string

\n
function length2(obj){\n    if (typeof obj==='object' && obj!== null){return Object.keys(obj).length;}\n   //if (Array.isArray){return obj.length;}\n    return obj.length;\n\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a55", + "creator": "Ran Turner", + "createdAt": 1650556821000, + "text": "

Using the Object.entries method to get length is one way of achieving it

\n

\r\n
\r\n
const objectLength = obj => Object.entries(obj).length;\n\nconst person = {\n    id: 1,\n    name: 'John',\n    age: 30\n}\n  \nconst car = {\n    type: 2,\n    color: 'red',\n}\n\nconsole.log(objectLength(person)); // 3\nconsole.log(objectLength(car)); // 2
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c6082fcc3049e909e6", + "creator": "Andrew Koster", + "createdAt": 1557516710000, + "text": "In the above example, myObject.length is undefined, at least in a browser environment. That's why it isn't valid, @AdrianM", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2588190, + "uvac": 2588233 + } + }, + { + "_id": "62f321b9082fcc3049e8feab", + "title": "What are the best answers and comments you can come up with?", + "title-lowercase": "what are the best answers and comments you can come up with?", + "creator": "Hordak", + "createdAt": 1218351922000, + "status": "protected", + "text": "**Hello, world!** What are some of the best random answers, question comments, and answer comments you can come up with? Post below.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1024, + "answers": 150, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e90063", + "creator": "Derek Park", + "createdAt": 1218352922000, + "text": "

string is an alias in C# for System.String.
\nSo technically, there is no difference. It's like int vs. System.Int32.

\n

As far as guidelines, it's generally recommended to use string any time you're referring to an object.

\n

e.g.

\n
string place = "world";\n
\n

Likewise, I think it's generally recommended to use String if you need to refer specifically to the class.

\n

e.g.

\n
string greet = String.Format("Hello {0}!", place);\n
\n

This is the style that Microsoft tends to use in their examples.

\n

It appears that the guidance in this area may have changed, as StyleCop now enforces the use of the C# specific aliases.

\n", + "upvotes": 11615, + "upvoterUsernames": [], + "downvotes": 4789, + "downvoterUsernames": [], + "commentItems": [ + { + "_id": "62f32a7a082fcc3049e931ab", + "creator": "Scott Stafford", + "createdAt": 1375971471000, + "text": "Who decided to call prepend "unshift"?", + "upvotes": 306, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93047", + "creator": "Strawberry", + "createdAt": 1375984842000, + "text": "In a practical sense, how can I make use this concept?", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 251, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9307a", + "creator": "Preston", + "createdAt": 1377831438000, + "text": "That's a hell of a source.", + "upvotes": 314, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93127", + "creator": "Michal Illich", + "createdAt": 1378053577000, + "text": "This answer is confusing - it mixes KNN (k nearest neighbours) and naive bayes.", + "upvotes": 391, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319a", + "creator": "Michal Illich", + "createdAt": 1378053577000, + "text": "This answer is confusing - it mixes KNN (k nearest neighbours) and naive bayes.", + "upvotes": 395, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb9", + "creator": "dokaspar", + "createdAt": 1378275133000, + "text": "but then shouldn't it rather be &2>&1?", + "upvotes": 671, + "upvoterUsernames": [], + "downvotes": 298, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eaa", + "creator": "Mxyk", + "createdAt": 1378486134000, + "text": "Why not spawn a child and waitpid so that, as the parent, you're not even sorting any socks yourself?", + "upvotes": 412, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930e7", + "creator": "endolith", + "createdAt": 1381974383000, + "text": "ValueError: Attempted relative import in non-package", + "upvotes": 477, + "upvoterUsernames": [], + "downvotes": 204, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90471", + "creator": "mlissner", + "createdAt": 1382221232000, + "text": "Just make sure you remember to provide out.pdf, or else it will overwrite the last file in your command, sigh.", + "upvotes": 809, + "upvoterUsernames": [], + "downvotes": 265, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a6", + "creator": "Jerry Tian", + "createdAt": 1382601954000, + "text": "Update, with OSX 10.9 Maverick, Maven is not installed by default any more.", + "upvotes": 634, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931f0", + "creator": "Jerry Tian", + "createdAt": 1382601954000, + "text": "Update, with OSX 10.9 Maverick, Maven is not installed by default any more.", + "upvotes": 737, + "upvoterUsernames": [], + "downvotes": 322, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930fe", + "creator": "thSoft", + "createdAt": 1382619071000, + "text": "An alliterating mnemonic that may help: Arguments are Actual. ;)", + "upvotes": 282, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918b2", + "creator": "Drazen Bjelovuk", + "createdAt": 1383522342000, + "text": "It seems to me that declarative programming is nothing more than a layer of abstraction.", + "upvotes": 448, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ecd", + "creator": "Dustin Graham", + "createdAt": 1383693685000, + "text": "I Googled it. This is where Google led me.", + "upvotes": 527, + "upvoterUsernames": [], + "downvotes": 169, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90460", + "creator": "Alex", + "createdAt": 1383749004000, + "text": "But what if the container is not the viewport (body) ?", + "upvotes": 1003, + "upvoterUsernames": [], + "downvotes": 263, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c4", + "creator": "rinogo", + "createdAt": 1383799462000, + "text": "@Michael - If having a NULL ID is valid in your schema, you might have bigger problems, wouldn't you agree? :)", + "upvotes": 496, + "upvoterUsernames": [], + "downvotes": 245, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90455", + "creator": "Rui Marques", + "createdAt": 1384426116000, + "text": "This is nice but the problem is that "visibility hidden" elements still occupy space while "display none" does not.", + "upvotes": 1434, + "upvoterUsernames": [], + "downvotes": 493, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90452", + "creator": "cptloop", + "createdAt": 1385468758000, + "text": "I google this every time. Related comic: xkcd.com/1168", + "upvotes": 2486, + "upvoterUsernames": [], + "downvotes": 714, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a3", + "creator": "Kamran Ahmed", + "createdAt": 1385634716000, + "text": "In jquery it'd be $("input[name=rate]:checked").val()", + "upvotes": 328, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93086", + "creator": "dinigo", + "createdAt": 1386680236000, + "text": "You don't even have to declare the alias. Just defining the function will do.", + "upvotes": 426, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92fea", + "creator": "youri", + "createdAt": 1386921835000, + "text": "Apache Camel homepage refers to this thread... They didn't manage to provide a short functional explanation of their own product.", + "upvotes": 343, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931da", + "creator": "youri", + "createdAt": 1386921835000, + "text": "Apache Camel homepage refers to this thread... They didn't manage to provide a short functional explanation of their own product.", + "upvotes": 567, + "upvoterUsernames": [], + "downvotes": 228, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e9b", + "creator": "Jonathan", + "createdAt": 1387086617000, + "text": "I realize this answer doesn't address the original question, but it answers the question I had when I found this on Google...", + "upvotes": 773, + "upvoterUsernames": [], + "downvotes": 383, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930fa", + "creator": "BrainSlugs83", + "createdAt": 1387408827000, + "text": "Holy crap. All that just to get Rounded corners?", + "upvotes": 414, + "upvoterUsernames": [], + "downvotes": 144, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930c2", + "creator": "Manav", + "createdAt": 1389070732000, + "text": "The "terse" flag to lsof produces output suitable for piping to a subsequent kill: lsof -t -i tcp:1234 | xargs kill", + "upvotes": 367, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93096", + "creator": "huysentruitw", + "createdAt": 1389167661000, + "text": "@maldy: isn't that the whole point of has**Own**Property ?", + "upvotes": 343, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9188b", + "creator": "tckmn", + "createdAt": 1389494727000, + "text": "@GeorgeJempty Not everyone wants to load a 5kb library for a simple key lookup ;)", + "upvotes": 677, + "upvoterUsernames": [], + "downvotes": 242, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9047a", + "creator": "Sneakyness", + "createdAt": 1390003329000, + "text": "It's pretty cool to answer a question on stackoverflow with code from stackoverflow, +1", + "upvotes": 663, + "upvoterUsernames": [], + "downvotes": 153, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931ae", + "creator": "mgalgs", + "createdAt": 1390327217000, + "text": "You should also consider using relative links", + "upvotes": 358, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930a4", + "creator": "rickcnagy", + "createdAt": 1391012089000, + "text": "another useful detail here: listone += listtwo results in listone == [1, 2, 3, 4, 5, 6]", + "upvotes": 400, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9300f", + "creator": "Rag", + "createdAt": 1392065373000, + "text": "Every time you assume characters are ASCII, God kills a kitten. :(", + "upvotes": 593, + "upvoterUsernames": [], + "downvotes": 272, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9312f", + "creator": "Matt Montag", + "createdAt": 1392102772000, + "text": "Python is so nice :). And to be redundant: this is called "interval comparison."", + "upvotes": 476, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319f", + "creator": "Matt Montag", + "createdAt": 1392102772000, + "text": "Python is so nice :). And to be redundant: this is called "interval comparison."", + "upvotes": 399, + "upvoterUsernames": [], + "downvotes": 142, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93094", + "creator": "slartibartfast", + "createdAt": 1393918302000, + "text": "("0000" + num).substr(-4,4); //short and sweet", + "upvotes": 400, + "upvoterUsernames": [], + "downvotes": 111, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93045", + "creator": "Mars Robertson", + "createdAt": 1393931159000, + "text": "For the record sessionStoragelocalStorage", + "upvotes": 591, + "upvoterUsernames": [], + "downvotes": 293, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93115", + "creator": "Robert", + "createdAt": 1394701150000, + "text": "This is an absolutely ridiculous answer. Why on earth would I need to do that? And you know what is even more ridiculous? It works.", + "upvotes": 489, + "upvoterUsernames": [], + "downvotes": 226, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9318e", + "creator": "Robert", + "createdAt": 1394701150000, + "text": "This is an absolutely ridiculous answer. Why on earth would I need to do that? And you know what is even more ridiculous? It works.", + "upvotes": 339, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045d", + "creator": "Muhammad Babar", + "createdAt": 1395381530000, + "text": "What about setting the parent layout to android:focusableInTouchMode="true"!", + "upvotes": 1043, + "upvoterUsernames": [], + "downvotes": 291, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930e5", + "creator": "rustyx", + "createdAt": 1396362784000, + "text": "It is also confusing for many developers as in many programming languages the prefix ! means not.", + "upvotes": 502, + "upvoterUsernames": [], + "downvotes": 228, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93119", + "creator": "awe", + "createdAt": 1396456394000, + "text": "It amazes me each time that jQuery does not have a shortcut for this like $('#test').id().", + "upvotes": 297, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93191", + "creator": "awe", + "createdAt": 1396456394000, + "text": "It amazes me each time that jQuery does not have a shortcut for this like $('#test').id().", + "upvotes": 315, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a8", + "creator": "Andrey", + "createdAt": 1396801577000, + "text": "@micapam The ln syntax is similar to the cp syntax, e.g. source destination.", + "upvotes": 735, + "upvoterUsernames": [], + "downvotes": 322, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931f1", + "creator": "Andrey", + "createdAt": 1396801577000, + "text": "@micapam The ln syntax is similar to the cp syntax, e.g. source destination.", + "upvotes": 693, + "upvoterUsernames": [], + "downvotes": 280, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9311b", + "creator": "Jack O'Connor", + "createdAt": 1397464882000, + "text": "Now that git fetch --unshallow exists (as in @sdram's answer), this answer is no longer the best one.", + "upvotes": 408, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93194", + "creator": "Jack O'Connor", + "createdAt": 1397464882000, + "text": "Now that git fetch --unshallow exists (as in @sdram's answer), this answer is no longer the best one.", + "upvotes": 275, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93125", + "creator": "bukzor", + "createdAt": 1399150395000, + "text": "The phrases "this is not generally what was intended" and "a way around this is" smell like they're documenting a design flaw.", + "upvotes": 456, + "upvoterUsernames": [], + "downvotes": 197, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93198", + "creator": "bukzor", + "createdAt": 1399150395000, + "text": "The phrases "this is not generally what was intended" and "a way around this is" smell like they're documenting a design flaw.", + "upvotes": 281, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92edb", + "creator": "Kokodoko", + "createdAt": 1399565362000, + "text": "After reading this entire page, I have come to the conclusion that your own simple for-loop is the simplest, most readable, and least error-prone.", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 301, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930eb", + "creator": "edsioufi", + "createdAt": 1400008783000, + "text": "This should be the answer to all Python relative imports questions. This should be in the docs, even.", + "upvotes": 337, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93112", + "creator": "Thomas", + "createdAt": 1401318363000, + "text": "Am I the only one finding that for the sake of testing we have to change the main code / functions signature is terrible?", + "upvotes": 327, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93020", + "creator": "user1303718", + "createdAt": 1401972254000, + "text": "Btw, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class)) works up to 10 time faster than TypeRefence.", + "upvotes": 426, + "upvoterUsernames": [], + "downvotes": 110, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9187a", + "creator": "sindrenm", + "createdAt": 1401992888000, + "text": "Just to nitpick some more, he didn't actually write “new line”, he wrote “newline”, which is correct.", + "upvotes": 860, + "upvoterUsernames": [], + "downvotes": 412, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931f7", + "creator": "sindrenm", + "createdAt": 1401992888000, + "text": "Just to nitpick some more, he didn't actually write “new line”, he wrote “newline”, which is correct.", + "upvotes": 833, + "upvoterUsernames": [], + "downvotes": 385, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eac", + "creator": "Bengt", + "createdAt": 1402062636000, + "text": "If it is safe and simple, why is there no go subcommand that does it?", + "upvotes": 719, + "upvoterUsernames": [], + "downvotes": 342, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9186d", + "creator": "radtek", + "createdAt": 1402328398000, + "text": "One important thing to note is that in JavaScript 0 = Sunday, Python starts with 0 = Monday. Something that I ran into, front-end vs back-end..", + "upvotes": 611, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93104", + "creator": "5lava", + "createdAt": 1402339393000, + "text": "Use --no-edit option. git commit --amend --reset-author --no-edit won't open an editor. Available since git 1.7.9.", + "upvotes": 323, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9303f", + "creator": "Ciro Santilli Путлер Капут 六四事", + "createdAt": 1404644149000, + "text": "There is one edge case: :empty.", + "upvotes": 332, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9304d", + "creator": "rvighne", + "createdAt": 1407285788000, + "text": "Ah! So prototype is not available on the instances themselves (or other objects), but only on the constructor functions.", + "upvotes": 429, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918ab", + "creator": "Philippe Leybaert", + "createdAt": 1407527215000, + "text": "> 0 is more readable IMHO. And there's no performance difference between the two.", + "upvotes": 483, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931f4", + "creator": "Philippe Leybaert", + "createdAt": 1407527215000, + "text": "> 0 is more readable IMHO. And there's no performance difference between the two.", + "upvotes": 765, + "upvoterUsernames": [], + "downvotes": 360, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9303a", + "creator": "Justin L.", + "createdAt": 1408041445000, + "text": "As the guy who implemented pushState in Firefox, I feel an odd mix of pride and revulsion at this hack. Well done, guys.", + "upvotes": 477, + "upvoterUsernames": [], + "downvotes": 175, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93015", + "creator": "Friedrich", + "createdAt": 1408094905000, + "text": "Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);", + "upvotes": 395, + "upvoterUsernames": [], + "downvotes": 75, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9186f", + "creator": "JaredCubilla", + "createdAt": 1408304750000, + "text": "I find it extremely ironic that your username is "Jason P". :)", + "upvotes": 756, + "upvoterUsernames": [], + "downvotes": 282, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930df", + "creator": "Dkyc", + "createdAt": 1413806394000, + "text": "This could be used as a workaround, but not an actual solution to the problem. What if we need to fetch lazily?", + "upvotes": 486, + "upvoterUsernames": [], + "downvotes": 211, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90467", + "creator": "Shawn J. Molloy", + "createdAt": 1414290029000, + "text": "@Gatada I'm glad my employer does not make me verify web code for the Nintendo 3DS browser.", + "upvotes": 994, + "upvoterUsernames": [], + "downvotes": 330, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930ef", + "creator": "TankorSmash", + "createdAt": 1415300699000, + "text": "@megido well -D force deletes, -d gives you a warning if it's not already merged in.", + "upvotes": 523, + "upvoterUsernames": [], + "downvotes": 251, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93043", + "creator": "botbot", + "createdAt": 1418461049000, + "text": "It was just too hard to add a str.length method. (raises his fist in anger)", + "upvotes": 451, + "upvoterUsernames": [], + "downvotes": 152, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9185d", + "creator": "akhil_mittal", + "createdAt": 1419828475000, + "text": "@G Rassovsky: All books which promise to teach X in Y hours. For example Learn C++ in 24 hours. I believe all such books are better avoided.", + "upvotes": 629, + "upvoterUsernames": [], + "downvotes": 121, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9302a", + "creator": "Joao Carlos", + "createdAt": 1420297845000, + "text": "If you, like me, make the mistake of doing (new Guid().toString()) you will get 0000-00000-00000-00000. You need to do Guid.NewGuid().toString()", + "upvotes": 361, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931bb", + "creator": "juanchopanza", + "createdAt": 1422624082000, + "text": "Try g++ instead of gcc. gcc is for C and will not give you access to the C++ standard library.", + "upvotes": 456, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed2", + "creator": "a coder", + "createdAt": 1423581261000, + "text": "Advice: stop using and supporting IE 8.", + "upvotes": 650, + "upvoterUsernames": [], + "downvotes": 295, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93037", + "creator": "Holger", + "createdAt": 1424379068000, + "text": "… or Comparator.comparingLong(File::lastModified).reversed()", + "upvotes": 314, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b4", + "creator": "Erich", + "createdAt": 1425072591000, + "text": "It's sad we have to do this instead of using our native exceptions.... oh Java, it gives and then takes away", + "upvotes": 448, + "upvoterUsernames": [], + "downvotes": 194, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93081", + "creator": "Boffin", + "createdAt": 1425942299000, + "text": "Number(x).toLocaleString()", + "upvotes": 419, + "upvoterUsernames": [], + "downvotes": 125, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9308c", + "creator": "sscirrus", + "createdAt": 1427220296000, + "text": "This didn't work for me on Mavericks: rm: /usr/local/var/postgres/postmaster.pid: No such file or directory", + "upvotes": 307, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918b4", + "creator": "Dave Cousineau", + "createdAt": 1427587492000, + "text": "@RoryO'Kane so everyone can play microsoft's audio format except microsoft? lol", + "upvotes": 433, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93131", + "creator": "Jonathon", + "createdAt": 1429261531000, + "text": "Feels like a design flaw to me.", + "upvotes": 335, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319c", + "creator": "Jonathon", + "createdAt": 1429261531000, + "text": "Feels like a design flaw to me.", + "upvotes": 438, + "upvoterUsernames": [], + "downvotes": 181, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93092", + "creator": "Tomáš Zato - Reinstate Monica", + "createdAt": 1429644590000, + "text": "No, I need the damn return value. To hell with echo.", + "upvotes": 376, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93009", + "creator": "Blender", + "createdAt": 1432422966000, + "text": "Python 3 renamed dict.iteritems -> dict.items.", + "upvotes": 524, + "upvoterUsernames": [], + "downvotes": 201, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931ea", + "creator": "Blender", + "createdAt": 1432422966000, + "text": "Python 3 renamed dict.iteritems -> dict.items.", + "upvotes": 615, + "upvoterUsernames": [], + "downvotes": 292, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9308e", + "creator": "Maroun", + "createdAt": 1433319358000, + "text": "Don't do weird things, you'll have no friends :(", + "upvotes": 494, + "upvoterUsernames": [], + "downvotes": 204, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9304b", + "creator": "maskacovnik", + "createdAt": 1434215877000, + "text": "rand() + rand() can owerflow", + "upvotes": 519, + "upvoterUsernames": [], + "downvotes": 223, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9300b", + "creator": "rybo111", + "createdAt": 1434231922000, + "text": "This attribute is also required: autocomplete="I told you I wanted this off, Google. But you would not listen."", + "upvotes": 512, + "upvoterUsernames": [], + "downvotes": 190, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c9", + "creator": "miguel", + "createdAt": 1434753245000, + "text": "It's so annoying that Google produces such shoddy code. We'll have to account for these framework bugs for years.", + "upvotes": 296, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91881", + "creator": "Synetech", + "createdAt": 1436220031000, + "text": "Except that is not CSS, that is JavaScript.", + "upvotes": 812, + "upvoterUsernames": [], + "downvotes": 368, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9312d", + "creator": "Sethi", + "createdAt": 1436363413000, + "text": "Further to @Sean's comment: ES6 syntax makes this super concise: var merged = [].concat(...arrays)", + "upvotes": 394, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a0", + "creator": "Sethi", + "createdAt": 1436363413000, + "text": "Further to @Sean's comment: ES6 syntax makes this super concise: var merged = [].concat(...arrays)", + "upvotes": 313, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9187f", + "creator": "thang", + "createdAt": 1438963084000, + "text": "cc is faster to type", + "upvotes": 551, + "upvoterUsernames": [], + "downvotes": 105, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931fa", + "creator": "thang", + "createdAt": 1438963084000, + "text": "cc is faster to type", + "upvotes": 610, + "upvoterUsernames": [], + "downvotes": 164, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9301a", + "creator": "Ram Rachum", + "createdAt": 1439044435000, + "text": ""The error is pretty clear." Today I learned there's a new meaning for the word "clear".", + "upvotes": 509, + "upvoterUsernames": [], + "downvotes": 192, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93082", + "creator": "JohnnyM", + "createdAt": 1439716962000, + "text": "Please add to the disclaimer that it will not hold true for nested objects or nested dicts or dicts in lists etc.", + "upvotes": 315, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93042", + "creator": "Álvaro González", + "createdAt": 1440271212000, + "text": "How can "please check logs" be the accepted answer to "why are my logs empty"?", + "upvotes": 318, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046d", + "creator": "nachocab", + "createdAt": 1442010630000, + "text": "@ericmjl yes df.rename(columns = {'$b':'B'}, inplace = True)", + "upvotes": 929, + "upvoterUsernames": [], + "downvotes": 375, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93090", + "creator": "PL_kolek", + "createdAt": 1442986283000, + "text": "(And now it takes 10 seconds to find this question in Google)", + "upvotes": 307, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90453", + "creator": "Mark Adler", + "createdAt": 1445013520000, + "text": "I am the reference, having been part of all of that. This post could be cited in Wikipedia as an original source.", + "upvotes": 3381, + "upvoterUsernames": [], + "downvotes": 1684, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90477", + "creator": "user233232", + "createdAt": 1445925814000, + "text": "So what is the solution?", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9307e", + "creator": "Christophe Roussy", + "createdAt": 1447937129000, + "text": "As usual: beware THE MONTH is ZERO-INDEXED ! So January is zero not one...", + "upvotes": 504, + "upvoterUsernames": [], + "downvotes": 209, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9311f", + "creator": "dr.dimitru", + "createdAt": 1448424025000, + "text": "Simpler: string[0].toUpperCase() + string.substring(1)", + "upvotes": 460, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93193", + "creator": "dr.dimitru", + "createdAt": 1448424025000, + "text": "Simpler: string[0].toUpperCase() + string.substring(1)", + "upvotes": 408, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045f", + "creator": "Dogweather", + "createdAt": 1452943572000, + "text": "This is ridiculously difficult.", + "upvotes": 901, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [] + }, + { + "_id": "62f32ade082fcc3049e9322b", + "creator": "Dogweather", + "createdAt": 1452943572000, + "text": "This is ridiculously difficult.", + "upvotes": 1050, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90476", + "creator": "Daniel Darabos", + "createdAt": 1454409536000, + "text": "This is terrible. Why don't they fix it? Ideally I should be able to search with a regex. Where do I go to protest this? :)", + "upvotes": 677, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb5", + "creator": "markj", + "createdAt": 1455794482000, + "text": "This just doesn't work. The resulting images are just useless gray shapes.", + "upvotes": 736, + "upvoterUsernames": [], + "downvotes": 361, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eae", + "creator": "lony", + "createdAt": 1456310124000, + "text": "Vim says E850: Invalid register name:(", + "upvotes": 511, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90474", + "creator": "Olle Härstedt", + "createdAt": 1456928370000, + "text": "There's a bug in your code: Tomato is a fruit.", + "upvotes": 907, + "upvoterUsernames": [], + "downvotes": 379, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab9082fcc3049e93227", + "creator": "Olle Härstedt", + "createdAt": 1456928370000, + "text": "There's a bug in your code: Tomato is a fruit.", + "upvotes": 620, + "upvoterUsernames": [], + "downvotes": 92, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91883", + "creator": "dwlz", + "createdAt": 1458933251000, + "text": "Well, that's annoying. :(", + "upvotes": 838, + "upvoterUsernames": [], + "downvotes": 395, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a7", + "creator": "kdlannoy", + "createdAt": 1461491933000, + "text": "can't you just use: array[::-1] ?", + "upvotes": 275, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d9", + "creator": "isabelle martz", + "createdAt": 1463061161000, + "text": "How am I supposed to query the DB if I'm not able to even connect to it??", + "upvotes": 298, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046c", + "creator": "Christophe Roussy", + "createdAt": 1463488404000, + "text": "401 'Unauthorized' should be 401 'Unauthenticated', problem solved !", + "upvotes": 1042, + "upvoterUsernames": [], + "downvotes": 481, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931d7", + "creator": "Johnny Everson", + "createdAt": 1463517437000, + "text": "one reason for not doing so is losing the discussion on the existing PR.", + "upvotes": 258, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e95", + "creator": "kevingilbert100", + "createdAt": 1468138866000, + "text": "Does anyone else just come to this post every time they want to see what the npm registry url is? haha", + "upvotes": 613, + "upvoterUsernames": [], + "downvotes": 217, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92fe8", + "creator": "Manu Masson", + "createdAt": 1469344839000, + "text": "I have the same problem and that did not fix it.", + "upvotes": 532, + "upvoterUsernames": [], + "downvotes": 193, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931db", + "creator": "Manu Masson", + "createdAt": 1469344839000, + "text": "I have the same problem and that did not fix it.", + "upvotes": 387, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a4", + "creator": "Davor Lucic", + "createdAt": 1470748612000, + "text": "Why is is so hard for React to generate unique keys itself?", + "upvotes": 773, + "upvoterUsernames": [], + "downvotes": 358, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931ef", + "creator": "Davor Lucic", + "createdAt": 1470748612000, + "text": "Why is is so hard for React to generate unique keys itself?", + "upvotes": 707, + "upvoterUsernames": [], + "downvotes": 292, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91888", + "creator": "Sina Madani", + "createdAt": 1471287964000, + "text": "I'm really confused by the diagram at the end. I thought I got it until I saw that image.", + "upvotes": 493, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ee9", + "creator": "Eduardo", + "createdAt": 1473806126000, + "text": "Nice that this Apple service is free, ... oh wait, actually we pay 99$ per year ...", + "upvotes": 646, + "upvoterUsernames": [], + "downvotes": 299, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9302d", + "creator": "demented hedgehog", + "createdAt": 1476316664000, + "text": "Just looking at this diagram tells me there's something fundamentally wrong with all this time stuff.", + "upvotes": 322, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a5", + "creator": "ruakh", + "createdAt": 1477980891000, + "text": "Have you examined the assembly code that GCC generates for your C++ program?", + "upvotes": 389, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ef1", + "creator": "joeytwiddle", + "createdAt": 1479199482000, + "text": "One way they could have avoided overcomplication would have been to stick with a popular and well-established format ... like Markdown. ;-)", + "upvotes": 647, + "upvoterUsernames": [], + "downvotes": 305, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931d9", + "creator": "joeytwiddle", + "createdAt": 1479199482000, + "text": "One way they could have avoided overcomplication would have been to stick with a popular and well-established format ... like Markdown. ;-)", + "upvotes": 542, + "upvoterUsernames": [], + "downvotes": 200, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92edd", + "creator": "Tien Do", + "createdAt": 1482809156000, + "text": "I always see this error if when I create a new Github repository with a README.md, then pull it to a local repository at the first time. So annoying.", + "upvotes": 406, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93049", + "creator": "Jean-François Corbett", + "createdAt": 1486042542000, + "text": "This is a fine if very un-pythonic solution. Consider using list comprehension.", + "upvotes": 380, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a0", + "creator": "fracz", + "createdAt": 1486679835000, + "text": "git config --global alias.adog "log --all --decorate --oneline --graph"", + "upvotes": 668, + "upvoterUsernames": [], + "downvotes": 249, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ebc", + "creator": "Frank Nocke", + "createdAt": 1489514025000, + "text": "Note, that things changed for the better in ES6, i.e. {[key]:someValueArray}", + "upvotes": 416, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93011", + "creator": "LarryBud", + "createdAt": 1489690012000, + "text": "I gotta say, to have a month number zero indexed is the dumbest thing I've seen in a while. '", + "upvotes": 331, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e9f", + "creator": "B T", + "createdAt": 1489710068000, + "text": "Or justify-content: flex-end", + "upvotes": 545, + "upvoterUsernames": [], + "downvotes": 156, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9300d", + "creator": "user9993", + "createdAt": 1490175666000, + "text": "Does not even answer the question yet the most upvoted answer?", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 227, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec1", + "creator": "Matej", + "createdAt": 1491934139000, + "text": "wouldn't mind a non-jquery answer for those who don't use it", + "upvotes": 372, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ffc", + "creator": "Lenar Hoyt", + "createdAt": 1494241030000, + "text": "BTW, this is a silly abbreviation means "get the current axes".", + "upvotes": 640, + "upvoterUsernames": [], + "downvotes": 312, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e6", + "creator": "Lenar Hoyt", + "createdAt": 1494241030000, + "text": "BTW, this is a silly abbreviation means "get the current axes".", + "upvotes": 377, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a1", + "creator": "Charlie Parker", + "createdAt": 1497550213000, + "text": "booooo why can't one rename a conda env?", + "upvotes": 460, + "upvoterUsernames": [], + "downvotes": 204, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ffe", + "creator": "Amalgovinus", + "createdAt": 1501011109000, + "text": "Not being able to see response data almost entirely kills the point of "preserve log"!", + "upvotes": 488, + "upvoterUsernames": [], + "downvotes": 160, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e5", + "creator": "Amalgovinus", + "createdAt": 1501011109000, + "text": "Not being able to see response data almost entirely kills the point of "preserve log"!", + "upvotes": 522, + "upvoterUsernames": [], + "downvotes": 194, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91874", + "creator": "Julien", + "createdAt": 1504772743000, + "text": "the image is the recipe, the container is the cake ;-) you can make as many cakes as you like with a given recipe", + "upvotes": 813, + "upvoterUsernames": [], + "downvotes": 350, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93028", + "creator": "Robin J", + "createdAt": 1517496416000, + "text": "small hint: parseInt(null) returns NaN but +null returns 0", + "upvotes": 563, + "upvoterUsernames": [], + "downvotes": 252, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff0", + "creator": "Daniel Kmak", + "createdAt": 1521204470000, + "text": "What if module doesn't have @types package?", + "upvotes": 552, + "upvoterUsernames": [], + "downvotes": 217, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931dd", + "creator": "Daniel Kmak", + "createdAt": 1521204470000, + "text": "What if module doesn't have @types package?", + "upvotes": 438, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed0", + "creator": "user959690", + "createdAt": 1523399865000, + "text": "That's too bad you had to bring in a library of classnames just to add two classes to an element :(", + "upvotes": 398, + "upvoterUsernames": [], + "downvotes": 42, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931d5", + "creator": "Caio", + "createdAt": 1537464819000, + "text": "You are not fixing the issue, you are just hiding them.", + "upvotes": 407, + "upvoterUsernames": [], + "downvotes": 158, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ee3", + "creator": "mvndaai", + "createdAt": 1544807854000, + "text": "I wish the Xcode update was part of the OS update. Something like this happens every time I upgrade. Annoying.", + "upvotes": 639, + "upvoterUsernames": [], + "downvotes": 290, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930cc", + "creator": "林果皞", + "createdAt": 1548320882000, + "text": "What's the point of this Android security feature if every developer going to add android:usesCleartextTraffic="true" ?", + "upvotes": 385, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ea6", + "creator": "Shiraz", + "createdAt": 1557924983000, + "text": "Nice explanation for other Lifecycle events, but this doesn't the question specifically about an alternative to componentWillMount().", + "upvotes": 599, + "upvoterUsernames": [], + "downvotes": 216, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93002", + "creator": "Jared Nedzel", + "createdAt": 1598028727000, + "text": "I appreciate the time and effort you put into your answer, but frankly this is still completely incomprehensible to me.", + "upvotes": 593, + "upvoterUsernames": [], + "downvotes": 269, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e9", + "creator": "Jared Nedzel", + "createdAt": 1598028727000, + "text": "I appreciate the time and effort you put into your answer, but frankly this is still completely incomprehensible to me.", + "upvotes": 588, + "upvoterUsernames": [], + "downvotes": 264, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9186b", + "creator": "drmrbrewer", + "createdAt": 1599824052000, + "text": "The new way looks unnecessarily complicated compared to the old way...", + "upvotes": 772, + "upvoterUsernames": [], + "downvotes": 293, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93084", + "creator": "wormsparty", + "createdAt": 1615292481000, + "text": "I'm coming from this answer, and now I'm in an infinite loop", + "upvotes": 437, + "upvoterUsernames": [], + "downvotes": 144, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93106", + "creator": "wormsparty", + "createdAt": 1615292595000, + "text": "I'm coming from this answer and now I'm in an infinite loop", + "upvotes": 286, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + } + ], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911ee", + "creator": "Pat Notz", + "createdAt": 1218563863000, + "text": "

For BSD or GNU grep you can use -B num to set how many lines before the match and -A num for the number of lines after the match.

\n\n
grep -B 3 -A 2 foo README.txt\n
\n\n

If you want the same number of lines before and after you can use -C num.

\n\n
grep -C 3 foo README.txt\n
\n\n

This will show 3 lines before and 3 lines after.

\n", + "upvotes": 10070, + "upvoterUsernames": [], + "downvotes": 4858, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91247", + "creator": "FlySwat", + "createdAt": 1219809582000, + "text": "

From an int:

\n
YourEnum foo = (YourEnum)yourInt;\n
\n

From a string:

\n
YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);\n\n// The foo.ToString().Contains(",") check is necessary for \n// enumerations marked with a [Flags] attribute.\nif (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))\n{\n    throw new InvalidOperationException(\n        $"{yourString} is not an underlying value of the YourEnum enumeration."\n    );\n}\n
\n

From a number:

\n
YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum), yourInt);\n
\n", + "upvotes": 6463, + "upvoterUsernames": [], + "downvotes": 2021, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90068", + "creator": "Mark Harrison", + "createdAt": 1220309974000, + "text": "

Assuming you're joining on columns with no duplicates, which is a very common case:

\n\n

Examples

\n

Suppose you have two tables, with a single column each, and data as follows:

\n
A    B\n-    -\n1    3\n2    4\n3    5\n4    6\n
\n

Note that (1,2) are unique to A, (3,4) are common, and (5,6) are unique to B.

\n

Inner join

\n

An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they have in common.

\n
select * from a INNER JOIN b on a.a = b.b;\nselect a.*, b.*  from a,b where a.a = b.b;\n\na | b\n--+--\n3 | 3\n4 | 4\n
\n

Left outer join

\n

A left outer join will give all rows in A, plus any common rows in B.

\n
select * from a LEFT OUTER JOIN b on a.a = b.b;\nselect a.*, b.*  from a,b where a.a = b.b(+);\n\na |  b\n--+-----\n1 | null\n2 | null\n3 |    3\n4 |    4\n
\n

Right outer join

\n

A right outer join will give all rows in B, plus any common rows in A.

\n
select * from a RIGHT OUTER JOIN b on a.a = b.b;\nselect a.*, b.*  from a,b where a.a(+) = b.b;\n\na    |  b\n-----+----\n3    |  3\n4    |  4\nnull |  5\nnull |  6\n
\n

Full outer join

\n

A full outer join will give you the union of A and B, i.e. all the rows in A and all the rows in B. If something in A doesn't have a corresponding datum in B, then the B portion is null, and vice versa.

\n
select * from a FULL OUTER JOIN b on a.a = b.b;\n\n a   |  b\n-----+-----\n   1 | null\n   2 | null\n   3 |    3\n   4 |    4\nnull |    6\nnull |    5\n
\n", + "upvotes": 9757, + "upvoterUsernames": [], + "downvotes": 3193, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006d", + "creator": "John Rutherford", + "createdAt": 1220631334000, + "text": "

Using regular expressions is probably the best way. You can see a bunch of tests here (taken from chromium)

\n
const validateEmail = (email) => {\n  return String(email)\n    .toLowerCase()\n    .match(\n      /^(([^<>()[\\]\\\\.,;:\\s@"]+(\\.[^<>()[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n    );\n};\n
\n

Here's the example of a regular expression that accepts unicode:

\n
const re =\n  /^(([^<>()[\\]\\.,;:\\s@\\"]+(\\.[^<>()[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\"]{2,})$/i;\n
\n

But keep in mind that one should not rely only upon JavaScript validation. JavaScript can easily be disabled. This should be validated on the server side as well.

\n

Here's an example of the above in action:

\n

\r\n
\r\n
const validateEmail = (email) => {\n  return email.match(\n    /^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n  );\n};\n\nconst validate = () => {\n  const $result = $('#result');\n  const email = $('#email').val();\n  $result.text('');\n\n  if (validateEmail(email)) {\n    $result.text(email + ' is valid :)');\n    $result.css('color', 'green');\n  } else {\n    $result.text(email + ' is not valid :(');\n    $result.css('color', 'red');\n  }\n  return false;\n}\n\n$('#email').on('input', validate);
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n\n<label for=\"email\">Enter an email address: </label>\n<input id=\"email\" />\n<h2 id=\"result\"></h2>
\r\n
\r\n
\r\n

\n", + "upvotes": 10036, + "upvoterUsernames": [], + "downvotes": 3788, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90077", + "creator": "ScArcher2", + "createdAt": 1220649352000, + "text": "
Map<String, String> map = ...\nfor (Map.Entry<String, String> entry : map.entrySet()) {\n    System.out.println(entry.getKey() + "/" + entry.getValue());\n}\n
\n

On Java 10+:

\n
for (var entry : map.entrySet()) {\n    System.out.println(entry.getKey() + "/" + entry.getValue());\n}\n
\n", + "upvotes": 8481, + "upvoterUsernames": [], + "downvotes": 2782, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90066", + "creator": "Tobi", + "createdAt": 1220989060000, + "text": "

For all unstaged files in current working directory use:

\n
git restore .\n
\n

For a specific file use:

\n
git restore path/to/file/to/revert\n
\n

That together with git switch replaces the overloaded git checkout (see here), and thus removes the argument disambiguation.

\n

If a file has both staged and unstaged changes, only the unstaged changes shown in git diff are reverted. Changes shown in git diff --staged stay intact.

\n

Before Git 2.23

\n

For all unstaged files in current working directory:

\n
git checkout -- .\n
\n

For a specific file:

\n
git checkout -- path/to/file/to/revert\n
\n

-- here to remove ambiguity (this is known as argument disambiguation).

\n", + "upvotes": 10571, + "upvoterUsernames": [], + "downvotes": 3946, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90062", + "creator": "Patrick", + "createdAt": 1221028085000, + "text": "
if not a:\n    print("List is empty")\n
\n

Using the implicit booleanness of the empty list is quite pythonic.

\n", + "upvotes": 6907, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90079", + "creator": "Grundlefleck", + "createdAt": 1221250026000, + "text": "

To check if a directory exists:

\n
if [ -d "$DIRECTORY" ]; then\n  echo "$DIRECTORY does exist."\nfi\n
\n

To check if a directory does not exist:

\n
if [ ! -d "$DIRECTORY" ]; then\n  echo "$DIRECTORY does not exist."\nfi\n
\n
\n

However, as Jon Ericson points out, subsequent commands may not work as intended if you do not take into account that a symbolic link to a directory will also pass this check.\nE.g. running this:

\n
ln -s "$ACTUAL_DIR" "$SYMLINK"\nif [ -d "$SYMLINK" ]; then \n  rmdir "$SYMLINK" \nfi\n
\n

Will produce the error message:

\n
rmdir: failed to remove `symlink': Not a directory\n
\n

So symbolic links may have to be treated differently, if subsequent commands expect directories:

\n
if [ -d "$LINK_OR_DIR" ]; then \n  if [ -L "$LINK_OR_DIR" ]; then\n    # It is a symlink!\n    # Symbolic link specific commands go here.\n    rm "$LINK_OR_DIR"\n  else\n    # It's a directory!\n    # Directory command goes here.\n    rmdir "$LINK_OR_DIR"\n  fi\nfi\n
\n
\n

Take particular note of the double-quotes used to wrap the variables. The reason for this is explained by 8jean in another answer.

\n

If the variables contain spaces or other unusual characters it will probably cause the script to fail.

\n", + "upvotes": 10719, + "upvoterUsernames": [], + "downvotes": 5080, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e0", + "creator": "Grundlefleck", + "createdAt": 1221250026000, + "text": "

To check if a directory exists:

\n
if [ -d "$DIRECTORY" ]; then\n  echo "$DIRECTORY does exist."\nfi\n
\n

To check if a directory does not exist:

\n
if [ ! -d "$DIRECTORY" ]; then\n  echo "$DIRECTORY does not exist."\nfi\n
\n
\n

However, as Jon Ericson points out, subsequent commands may not work as intended if you do not take into account that a symbolic link to a directory will also pass this check.\nE.g. running this:

\n
ln -s "$ACTUAL_DIR" "$SYMLINK"\nif [ -d "$SYMLINK" ]; then \n  rmdir "$SYMLINK" \nfi\n
\n

Will produce the error message:

\n
rmdir: failed to remove `symlink': Not a directory\n
\n

So symbolic links may have to be treated differently, if subsequent commands expect directories:

\n
if [ -d "$LINK_OR_DIR" ]; then \n  if [ -L "$LINK_OR_DIR" ]; then\n    # It is a symlink!\n    # Symbolic link specific commands go here.\n    rm "$LINK_OR_DIR"\n  else\n    # It's a directory!\n    # Directory command goes here.\n    rmdir "$LINK_OR_DIR"\n  fi\nfi\n
\n
\n

Take particular note of the double-quotes used to wrap the variables. The reason for this is explained by 8jean in another answer.

\n

If the variables contain spaces or other unusual characters it will probably cause the script to fail.

\n", + "upvotes": 6490, + "upvoterUsernames": [], + "downvotes": 851, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9120e", + "creator": "emk", + "createdAt": 1221571702000, + "text": "

First, clone a remote Git repository and cd into it:

\n
$ git clone git://example.com/myproject\n$ cd myproject\n
\n

Next, look at the local branches in your repository:

\n
$ git branch\n* master\n
\n

But there are other branches hiding in your repository! See these using the -a flag:

\n
$ git branch -a\n* master\n  remotes/origin/HEAD\n  remotes/origin/master\n  remotes/origin/v1.0-stable\n  remotes/origin/experimental\n
\n

To take a quick peek at an upstream branch, check it out directly:

\n
$ git checkout origin/experimental\n
\n

To work on that branch, create a local tracking branch, which is done automatically by:

\n
$ git checkout experimental\n\nBranch experimental set up to track remote branch experimental from origin.\nSwitched to a new branch 'experimental'\n
\n

Here, "new branch" simply means that the branch is taken from the index and created locally for you. As the previous line tells you, the branch is being set up to track the remote branch, which usually means the origin/branch_name branch.

\n

Your local branches should now show:

\n
$ git branch\n* experimental\n  master\n
\n

You can track more than one remote repository using git remote:

\n
$ git remote add win32 git://example.com/users/joe/myproject-win32-port\n$ git branch -a\n* master\n  remotes/origin/HEAD\n  remotes/origin/master\n  remotes/origin/v1.0-stable\n  remotes/origin/experimental\n  remotes/win32/master\n  remotes/win32/new-widgets\n
\n

At this point, things are getting pretty crazy, so run gitk to see what's going on:

\n
$ gitk --all &\n
\n", + "upvotes": 6755, + "upvoterUsernames": [], + "downvotes": 1748, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90070", + "creator": "rslite", + "createdAt": 1221656271000, + "text": "

If the reason you're checking is so you can do something like if file_exists: open_it(), it's safer to use a try around the attempt to open it. Checking and then opening risks the file being deleted or moved or something between when you check and when you try to open it.

\n\n

If you're not planning to open the file immediately, you can use os.path.isfile

\n\n
\n

Return True if path is an existing regular file. This follows symbolic links, so both islink() and isfile() can be true for the same path.

\n
\n\n
import os.path\nos.path.isfile(fname) \n
\n\n

if you need to be sure it's a file.

\n\n

Starting with Python 3.4, the pathlib module offers an object-oriented approach (backported to pathlib2 in Python 2.7):

\n\n
from pathlib import Path\n\nmy_file = Path(\"/path/to/file\")\nif my_file.is_file():\n    # file exists\n
\n\n

To check a directory, do:

\n\n
if my_file.is_dir():\n    # directory exists\n
\n\n

To check whether a Path object exists independently of whether is it a file or directory, use exists():

\n\n
if my_file.exists():\n    # path exists\n
\n\n

You can also use resolve(strict=True) in a try block:

\n\n
try:\n    my_abs_path = my_file.resolve(strict=True)\nexcept FileNotFoundError:\n    # doesn't exist\nelse:\n    # exists\n
\n", + "upvotes": 8242, + "upvoterUsernames": [], + "downvotes": 2127, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007a", + "creator": "David Cournapeau", + "createdAt": 1221701975000, + "text": "

Use the subprocess module in the standard library:

\n
import subprocess\nsubprocess.run(["ls", "-l"])\n
\n

The advantage of subprocess.run over os.system is that it is more flexible (you can get the stdout, stderr, the "real" status code, better error handling, etc...).

\n

Even the documentation for os.system recommends using subprocess instead:

\n
\n

The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function. See the Replacing Older Functions with the subprocess Module section in the subprocess documentation for some helpful recipes.

\n
\n

On Python 3.4 and earlier, use subprocess.call instead of .run:

\n
subprocess.call(["ls", "-l"])\n
\n", + "upvotes": 6121, + "upvoterUsernames": [], + "downvotes": 640, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e1", + "creator": "David Cournapeau", + "createdAt": 1221701975000, + "text": "

Use the subprocess module in the standard library:

\n
import subprocess\nsubprocess.run(["ls", "-l"])\n
\n

The advantage of subprocess.run over os.system is that it is more flexible (you can get the stdout, stderr, the "real" status code, better error handling, etc...).

\n

Even the documentation for os.system recommends using subprocess instead:

\n
\n

The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function. See the Replacing Older Functions with the subprocess Module section in the subprocess documentation for some helpful recipes.

\n
\n

On Python 3.4 and earlier, use subprocess.call instead of .run:

\n
subprocess.call(["ls", "-l"])\n
\n", + "upvotes": 6510, + "upvoterUsernames": [], + "downvotes": 1029, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91200", + "creator": "Ben Hoffstein", + "createdAt": 1221758417000, + "text": "

It's 2,147,483,647. Easiest way to memorize it is via a tattoo.

\n", + "upvotes": 8553, + "upvoterUsernames": [], + "downvotes": 3490, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911fa", + "creator": "jop", + "createdAt": 1221856638000, + "text": "
foreach (Suit suit in (Suit[]) Enum.GetValues(typeof(Suit)))\n{\n}\n
\n\n

Note: The cast to (Suit[]) is not strictly necessary, but it does make the code 0.5 ns faster.

\n", + "upvotes": 7179, + "upvoterUsernames": [], + "downvotes": 2055, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007e", + "creator": "Justin Poliey", + "createdAt": 1222086547000, + "text": "

You can apply this CSS to the inner <div>:

\n
#inner {\n  width: 50%;\n  margin: 0 auto;\n}\n
\n

Of course, you don't have to set the width to 50%. Any width less than the containing <div> will work. The margin: 0 auto is what does the actual centering.

\n

If you are targeting Internet Explorer 8 (and later), it might be better to have this instead:

\n
#inner {\n  display: table;\n  margin: 0 auto;\n}\n
\n

It will make the inner element center horizontally and it works without setting a specific width.

\n

Working example here:

\n

\r\n
\r\n
#inner {\n  display: table;\n  margin: 0 auto;\n  border: 1px solid black;\n}\n\n#outer {\n  border: 1px solid red;\n  width:100%\n}
\r\n
<div id=\"outer\">\n  <div id=\"inner\">Foo foo</div>\n</div>
\r\n
\r\n
\r\n

\n
\n

EDIT

\n

With flexbox it is very easy to style the div horizontally and vertically centered.

\n

\r\n
\r\n
#inner {  \n  border: 0.05em solid black;\n}\n\n#outer {\n  border: 0.05em solid red;\n  width:100%;\n  display: flex;\n  justify-content: center;\n}
\r\n
<div id=\"outer\">\n  <div id=\"inner\">Foo foo</div>\n</div>
\r\n
\r\n
\r\n

\n

To align the div vertically centered, use the property align-items: center.

\n", + "upvotes": 9455, + "upvoterUsernames": [], + "downvotes": 4102, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e5", + "creator": "Justin Poliey", + "createdAt": 1222086547000, + "text": "

You can apply this CSS to the inner <div>:

\n
#inner {\n  width: 50%;\n  margin: 0 auto;\n}\n
\n

Of course, you don't have to set the width to 50%. Any width less than the containing <div> will work. The margin: 0 auto is what does the actual centering.

\n

If you are targeting Internet Explorer 8 (and later), it might be better to have this instead:

\n
#inner {\n  display: table;\n  margin: 0 auto;\n}\n
\n

It will make the inner element center horizontally and it works without setting a specific width.

\n

Working example here:

\n

\r\n
\r\n
#inner {\n  display: table;\n  margin: 0 auto;\n  border: 1px solid black;\n}\n\n#outer {\n  border: 1px solid red;\n  width:100%\n}
\r\n
<div id=\"outer\">\n  <div id=\"inner\">Foo foo</div>\n</div>
\r\n
\r\n
\r\n

\n
\n

EDIT

\n

With flexbox it is very easy to style the div horizontally and vertically centered.

\n

\r\n
\r\n
#inner {  \n  border: 0.05em solid black;\n}\n\n#outer {\n  border: 0.05em solid red;\n  width:100%;\n  display: flex;\n  justify-content: center;\n}
\r\n
<div id=\"outer\">\n  <div id=\"inner\">Foo foo</div>\n</div>
\r\n
\r\n
\r\n

\n

To align the div vertically centered, use the property align-items: center.

\n", + "upvotes": 7287, + "upvoterUsernames": [], + "downvotes": 1934, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007d", + "creator": "CB Bailey", + "createdAt": 1222372588000, + "text": "

The easiest way would be to find the head commit of the branch as it was immediately before the rebase started in the reflog...

\n
git reflog\n
\n

and to reset the current branch to it (with the usual caveats about being absolutely sure before reseting with the --hard option).

\n

Suppose the old commit was HEAD@{2} in the ref log:

\n
git reset --hard HEAD@{2}\n
\n

In Windows, you may need to quote the reference:

\n
git reset --hard "HEAD@{2}"\n
\n

You can check the history of the candidate old head by just doing a git log HEAD@{2} (Windows: git log "HEAD@{2}").

\n

If you've not disabled per branch reflogs you should be able to simply do git reflog branchname@{1} as a rebase detaches the branch head before reattaching to the final head. I would double check this, though as I haven't verified this recently.

\n

Per default, all reflogs are activated for non-bare repositories:

\n
[core]\n    logAllRefUpdates = true\n
\n", + "upvotes": 6171, + "upvoterUsernames": [], + "downvotes": 800, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e4", + "creator": "CB Bailey", + "createdAt": 1222372588000, + "text": "

The easiest way would be to find the head commit of the branch as it was immediately before the rebase started in the reflog...

\n
git reflog\n
\n

and to reset the current branch to it (with the usual caveats about being absolutely sure before reseting with the --hard option).

\n

Suppose the old commit was HEAD@{2} in the ref log:

\n
git reset --hard HEAD@{2}\n
\n

In Windows, you may need to quote the reference:

\n
git reset --hard "HEAD@{2}"\n
\n

You can check the history of the candidate old head by just doing a git log HEAD@{2} (Windows: git log "HEAD@{2}").

\n

If you've not disabled per branch reflogs you should be able to simply do git reflog branchname@{1} as a rebase detaches the branch head before reattaching to the final head. I would double check this, though as I haven't verified this recently.

\n

Per default, all reflogs are activated for non-bare repositories:

\n
[core]\n    logAllRefUpdates = true\n
\n", + "upvotes": 5586, + "upvoterUsernames": [], + "downvotes": 215, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91241", + "creator": "bdukes", + "createdAt": 1222795214000, + "text": "

Empty string, undefined, null, ...

\n

To check for a truthy value:

\n
if (strValue) {\n    // strValue was non-empty string, true, 42, Infinity, [], ...\n}\n
\n

To check for a falsy value:

\n
if (!strValue) {\n    // strValue was empty string, false, 0, null, undefined, ...\n}\n
\n
\n

Empty string (only!)

\n

To check for exactly an empty string, compare for strict equality against "" using the === operator:

\n
if (strValue === "") {\n    // strValue was empty string\n}\n
\n

To check for not an empty string strictly, use the !== operator:

\n
if (strValue !== "") {\n    // strValue was not an empty string\n}\n
\n", + "upvotes": 5624, + "upvoterUsernames": [], + "downvotes": 1137, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91210", + "creator": "Tom", + "createdAt": 1222871952000, + "text": "
new ArrayList<>(Arrays.asList(array));\n
\n", + "upvotes": 7532, + "upvoterUsernames": [], + "downvotes": 2544, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004e", + "creator": "Tsvetomir Tsonev", + "createdAt": 1223386222000, + "text": "

Since the question refers to a single element, this code might be more suitable:

\n
// Checks CSS content for display:[none|block], ignores visibility:[true|false]\n$(element).is(":visible");\n\n// The same works with hidden\n$(element).is(":hidden");\n
\n

It is the same as twernt's suggestion, but applied to a single element; and it matches the algorithm recommended in the jQuery FAQ.

\n

We use jQuery's is() to check the selected element with another element, selector or any jQuery object. This method traverses along the DOM elements to find a match, which satisfies the passed parameter. It will return true if there is a match, otherwise return false.

\n", + "upvotes": 18937, + "upvoterUsernames": [], + "downvotes": 8890, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f32a0c082fcc3049e92fc5", + "creator": "Tsvetomir Tsonev", + "createdAt": 1223386222000, + "text": "

Since the question refers to a single element, this code might be more suitable:

\n
// Checks CSS content for display:[none|block], ignores visibility:[true|false]\n$(element).is(":visible");\n\n// The same works with hidden\n$(element).is(":hidden");\n
\n

It is the same as twernt's suggestion, but applied to a single element; and it matches the algorithm recommended in the jQuery FAQ.

\n

We use jQuery's is() to check the selected element with another element, selector or any jQuery object. This method traverses along the DOM elements to find a match, which satisfies the passed parameter. It will return true if there is a match, otherwise return false.

\n", + "upvotes": 13118, + "upvoterUsernames": [], + "downvotes": 3071, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90050", + "creator": "nickf", + "createdAt": 1224154733000, + "text": "

To remove a property from an object (mutating the object), you can do it like this:

\n
delete myObject.regex;\n// or,\ndelete myObject['regex'];\n// or,\nvar prop = "regex";\ndelete myObject[prop];\n
\n

Demo\n

\r\n
\r\n
var myObject = {\n    \"ircEvent\": \"PRIVMSG\",\n    \"method\": \"newURI\",\n    \"regex\": \"^http://.*\"\n};\ndelete myObject.regex;\n\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n

For anyone interested in reading more about it, Stack Overflow user kangax has written an incredibly in-depth blog post about the delete statement on their blog, Understanding delete. It is highly recommended.

\n

If you'd like a new object with all the keys of the original except some, you could use destructuring.

\n

Demo\n

\r\n
\r\n
let myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\"\n};\n\n// assign the key regex to the variable _ indicating it will be unused\nconst {regex: _, ...newObj} = myObject;\n\nconsole.log(newObj);   // has no 'regex' key\nconsole.log(myObject); // remains unchanged
\r\n
\r\n
\r\n

\n", + "upvotes": 11986, + "upvoterUsernames": [], + "downvotes": 2567, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006f", + "creator": "David Segonds", + "createdAt": 1224359870000, + "text": "

The official tutorial may be of some use to you.

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ClassPackageSubclass
(same pkg)
Subclass
(diff pkg)
World
public+++++
protected++++
no modifier+++
private+
\n
\n

+ : accessible
\nblank : not accessible

\n", + "upvotes": 11870, + "upvoterUsernames": [], + "downvotes": 5716, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005c", + "creator": "Greg Hewgill", + "createdAt": 1224373175000, + "text": "

Assuming the hash of the commit you want is c5f567:

\n
git checkout c5f567 -- file1/to/restore file2/to/restore\n
\n

The git checkout man page gives more information.

\n

If you want to revert to the commit before c5f567, append ~1 (where 1 is the number of commits you want to go back, it can be anything):

\n
git checkout c5f567~1 -- file1/to/restore file2/to/restore\n
\n

As a side note, I've always been uncomfortable with this command because it's used for both ordinary things (changing between branches) and unusual, destructive things (discarding changes in the working directory).

\n
\n

There is also a new git restore command that is specifically designed for restoring working copy files that have been modified. If your git is new enough you can use this command, but the documentation comes with a warning:

\n
\n

THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.

\n
\n", + "upvotes": 12057, + "upvoterUsernames": [], + "downvotes": 4765, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd6", + "creator": "Greg Hewgill", + "createdAt": 1224373175000, + "text": "

Assuming the hash of the commit you want is c5f567:

\n
git checkout c5f567 -- file1/to/restore file2/to/restore\n
\n

The git checkout man page gives more information.

\n

If you want to revert to the commit before c5f567, append ~1 (where 1 is the number of commits you want to go back, it can be anything):

\n
git checkout c5f567~1 -- file1/to/restore file2/to/restore\n
\n

As a side note, I've always been uncomfortable with this command because it's used for both ordinary things (changing between branches) and unusual, destructive things (discarding changes in the working directory).

\n
\n

There is also a new git restore command that is specifically designed for restoring working copy files that have been modified. If your git is new enough you can use this command, but the documentation comes with a warning:

\n
\n

THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.

\n
\n", + "upvotes": 7568, + "upvoterUsernames": [], + "downvotes": 276, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90078", + "creator": "daveb", + "createdAt": 1224581552000, + "text": "

Timestamp in milliseconds

\n

To get the number of milliseconds since Unix epoch, call Date.now:

\n
Date.now()\n
\n

Alternatively, use the unary operator + to call Date.prototype.valueOf:

\n
+ new Date()\n
\n

Alternatively, call valueOf directly:

\n
new Date().valueOf()\n
\n

To support IE8 and earlier (see compatibility table), create a shim for Date.now:

\n
if (!Date.now) {\n    Date.now = function() { return new Date().getTime(); }\n}\n
\n

Alternatively, call getTime directly:

\n
new Date().getTime()\n
\n
\n

Timestamp in seconds

\n

To get the number of seconds since Unix epoch, i.e. Unix timestamp:

\n
Math.floor(Date.now() / 1000)\n
\n

Alternatively, using bitwise-or to floor is slightly faster, but also less readable and may break in the future (see explanations 1, 2):

\n
Date.now() / 1000 | 0\n
\n
\n

Timestamp in milliseconds (higher resolution)

\n

Use performance.now:

\n

\r\n
\r\n
var isPerformanceSupported = (\n    window.performance &&\n    window.performance.now &&\n    window.performance.timing &&\n    window.performance.timing.navigationStart\n);\n\nvar timeStampInMs = (\n    isPerformanceSupported ?\n    window.performance.now() +\n    window.performance.timing.navigationStart :\n    Date.now()\n);\n\nconsole.log(timeStampInMs, Date.now());
\r\n
\r\n
\r\n

\n", + "upvotes": 7555, + "upvoterUsernames": [], + "downvotes": 1889, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911df", + "creator": "daveb", + "createdAt": 1224581552000, + "text": "

Timestamp in milliseconds

\n

To get the number of milliseconds since Unix epoch, call Date.now:

\n
Date.now()\n
\n

Alternatively, use the unary operator + to call Date.prototype.valueOf:

\n
+ new Date()\n
\n

Alternatively, call valueOf directly:

\n
new Date().valueOf()\n
\n

To support IE8 and earlier (see compatibility table), create a shim for Date.now:

\n
if (!Date.now) {\n    Date.now = function() { return new Date().getTime(); }\n}\n
\n

Alternatively, call getTime directly:

\n
new Date().getTime()\n
\n
\n

Timestamp in seconds

\n

To get the number of seconds since Unix epoch, i.e. Unix timestamp:

\n
Math.floor(Date.now() / 1000)\n
\n

Alternatively, using bitwise-or to floor is slightly faster, but also less readable and may break in the future (see explanations 1, 2):

\n
Date.now() / 1000 | 0\n
\n
\n

Timestamp in milliseconds (higher resolution)

\n

Use performance.now:

\n

\r\n
\r\n
var isPerformanceSupported = (\n    window.performance &&\n    window.performance.now &&\n    window.performance.timing &&\n    window.performance.timing.navigationStart\n);\n\nvar timeStampInMs = (\n    isPerformanceSupported ?\n    window.performance.now() +\n    window.performance.timing.navigationStart :\n    Date.now()\n);\n\nconsole.log(timeStampInMs, Date.now());
\r\n
\r\n
\r\n

\n", + "upvotes": 10994, + "upvoterUsernames": [], + "downvotes": 5328, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9123f", + "creator": "Adam Bellaire", + "createdAt": 1224766524000, + "text": "

You can use Marcus's answer (* wildcards) outside a case statement, too, if you use double brackets:

\n\n
string='My long string'\nif [[ $string == *\"My long\"* ]]; then\n  echo \"It's there!\"\nfi\n
\n\n

Note that spaces in the needle string need to be placed between double quotes, and the * wildcards should be outside. Also note that a simple comparison operator is used (i.e. ==), not the regex operator =~.

\n", + "upvotes": 8652, + "upvoterUsernames": [], + "downvotes": 4154, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90064", + "creator": "Eli", + "createdAt": 1225227702000, + "text": "

No.

\n

JSON is data-only. If you include a comment, then it must be data too.

\n

You could have a designated data element called "_comment" (or something) that should be ignored by apps that use the JSON data.

\n

You would probably be better having the comment in the processes that generates/receives the JSON, as they are supposed to know what the JSON data will be in advance, or at least the structure of it.

\n

But if you decided to:

\n
{\n   "_comment": "comment text goes here...",\n   "glossary": {\n      "title": "example glossary",\n      "GlossDiv": {\n         "title": "S",\n         "GlossList": {\n            "GlossEntry": {\n               "ID": "SGML",\n               "SortAs": "SGML",\n               "GlossTerm": "Standard Generalized Markup Language",\n               "Acronym": "SGML",\n               "Abbrev": "ISO 8879:1986",\n               "GlossDef": {\n                  "para": "A meta-markup language, used to create markup languages such as DocBook.",\n                  "GlossSeeAlso": ["GML", "XML"]\n               },\n               "GlossSee": "markup"\n            }\n         }\n      }\n   }\n}\n
\n", + "upvotes": 8632, + "upvoterUsernames": [], + "downvotes": 1944, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90075", + "creator": "kender", + "createdAt": 1225432945000, + "text": "

append appends a specified object at the end of the list:

\n
>>> x = [1, 2, 3]\n>>> x.append([4, 5])\n>>> print(x)\n[1, 2, 3, [4, 5]]\n
\n

extend extends the list by appending elements from the specified iterable:

\n
>>> x = [1, 2, 3]\n>>> x.extend([4, 5])\n>>> print(x)\n[1, 2, 3, 4, 5]\n
\n", + "upvotes": 6430, + "upvoterUsernames": [], + "downvotes": 679, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91234", + "creator": "guinaps", + "createdAt": 1225846262000, + "text": "

Do:

\n
var isTrueSet = (myValue === 'true');\n
\n

using the identity operator (===), which doesn't make any implicit type conversions when the compared variables have different types.

\n

This will set isTrueSet to a boolean true if the string is "true" and boolean false if it is string "false" or not set at all.

\n
\n

Don't:

\n

You should probably be cautious about using these two methods for your specific needs:

\n
var myBool = Boolean("false");  // == true\n\nvar myBool = !!"false";  // == true\n
\n

Any string which isn't the empty string will evaluate to true by using them. Although they're the cleanest methods I can think of concerning to boolean conversion, I think they're not what you're looking for.

\n", + "upvotes": 6384, + "upvoterUsernames": [], + "downvotes": 1836, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90067", + "creator": "Blair Conrad", + "createdAt": 1226084767000, + "text": "

On Python ≥ 3.5, use pathlib.Path.mkdir:

\n\n
from pathlib import Path\nPath(\"/my/directory\").mkdir(parents=True, exist_ok=True)\n
\n\n

For older versions of Python, I see two answers with good qualities, each with a small flaw, so I will give my take on it:

\n\n

Try os.path.exists, and consider os.makedirs for the creation.

\n\n
import os\nif not os.path.exists(directory):\n    os.makedirs(directory)\n
\n\n

As noted in comments and elsewhere, there's a race condition – if the directory is created between the os.path.exists and the os.makedirs calls, the os.makedirs will fail with an OSError. Unfortunately, blanket-catching OSError and continuing is not foolproof, as it will ignore a failure to create the directory due to other factors, such as insufficient permissions, full disk, etc.

\n\n

One option would be to trap the OSError and examine the embedded error code (see Is there a cross-platform way of getting information from Python’s OSError):

\n\n
import os, errno\n\ntry:\n    os.makedirs(directory)\nexcept OSError as e:\n    if e.errno != errno.EEXIST:\n        raise\n
\n\n

Alternatively, there could be a second os.path.exists, but suppose another created the directory after the first check, then removed it before the second one – we could still be fooled.

\n\n

Depending on the application, the danger of concurrent operations may be more or less than the danger posed by other factors such as file permissions. The developer would have to know more about the particular application being developed and its expected environment before choosing an implementation.

\n\n

Modern versions of Python improve this code quite a bit, both by exposing FileExistsError (in 3.3+)...

\n\n
try:\n    os.makedirs(\"path/to/directory\")\nexcept FileExistsError:\n    # directory already exists\n    pass\n
\n\n

...and by allowing a keyword argument to os.makedirs called exist_ok (in 3.2+).

\n\n
os.makedirs(\"path/to/directory\", exist_ok=True)  # succeeds even if directory exists.\n
\n", + "upvotes": 9837, + "upvoterUsernames": [], + "downvotes": 3217, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004c", + "creator": "Greg Hewgill", + "createdAt": 1226742760000, + "text": "

In the simplest terms, git pull does a git fetch followed by a git merge.

\n
\n

git fetch updates your remote-tracking branches under refs/remotes/<remote>/. This operation is safe to run at any time since it never changes any of your local branches under refs/heads.

\n

git pull brings a local branch up-to-date with its remote version, while also updating your other remote-tracking branches.

\n

From the Git documentation for git pull:

\n
\n

In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.

\n
\n", + "upvotes": 18696, + "upvoterUsernames": [], + "downvotes": 7610, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f32a0c082fcc3049e92fc3", + "creator": "Greg Hewgill", + "createdAt": 1226742760000, + "text": "

In the simplest terms, git pull does a git fetch followed by a git merge.

\n
\n

git fetch updates your remote-tracking branches under refs/remotes/<remote>/. This operation is safe to run at any time since it never changes any of your local branches under refs/heads.

\n

git pull brings a local branch up-to-date with its remote version, while also updating your other remote-tracking branches.

\n

From the Git documentation for git pull:

\n
\n

In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.

\n
\n", + "upvotes": 17055, + "upvoterUsernames": [], + "downvotes": 5969, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007b", + "creator": "Greg", + "createdAt": 1228304262000, + "text": "

The difference is that functionOne is a function expression and so only defined when that line is reached, whereas functionTwo is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

\n\n

For example, a function expression:

\n\n

\r\n
\r\n
// TypeError: functionOne is not a function\r\nfunctionOne();\r\n\r\nvar functionOne = function() {\r\n  console.log(\"Hello!\");\r\n};
\r\n
\r\n
\r\n

\n\n

And, a function declaration:

\n\n

\r\n
\r\n
// Outputs: \"Hello!\"\r\nfunctionTwo();\r\n\r\nfunction functionTwo() {\r\n  console.log(\"Hello!\");\r\n}
\r\n
\r\n
\r\n

\n\n

Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.

\n\n

\r\n
\r\n
'use strict';    \r\n{ // note this block!\r\n  function functionThree() {\r\n    console.log(\"Hello!\");\r\n  }\r\n}\r\nfunctionThree(); // ReferenceError
\r\n
\r\n
\r\n

\n", + "upvotes": 10294, + "upvoterUsernames": [], + "downvotes": 4822, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e2", + "creator": "Greg", + "createdAt": 1228304262000, + "text": "

The difference is that functionOne is a function expression and so only defined when that line is reached, whereas functionTwo is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

\n\n

For example, a function expression:

\n\n

\r\n
\r\n
// TypeError: functionOne is not a function\r\nfunctionOne();\r\n\r\nvar functionOne = function() {\r\n  console.log(\"Hello!\");\r\n};
\r\n
\r\n
\r\n

\n\n

And, a function declaration:

\n\n

\r\n
\r\n
// Outputs: \"Hello!\"\r\nfunctionTwo();\r\n\r\nfunction functionTwo() {\r\n  console.log(\"Hello!\");\r\n}
\r\n
\r\n
\r\n

\n\n

Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.

\n\n

\r\n
\r\n
'use strict';    \r\n{ // note this block!\r\n  function functionThree() {\r\n    console.log(\"Hello!\");\r\n  }\r\n}\r\nfunctionThree(); // ReferenceError
\r\n
\r\n
\r\n

\n", + "upvotes": 8038, + "upvoterUsernames": [], + "downvotes": 2566, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90046", + "creator": "genehack", + "createdAt": 1228689048000, + "text": "

Undo git add for uncommitted changes with:

\n
git reset <file>\n
\n

That will remove the file from the current index (the "about to be committed" list) without changing anything else.

\n
\n

To unstage all changes for all files:

\n
git reset\n
\n
\n

In old versions of Git, the above commands are equivalent to git reset HEAD <file> and git reset HEAD respectively, and will fail if HEAD is undefined (because you haven't yet made any commits in your repository) or ambiguous (because you created a branch called HEAD, which is a stupid thing that you shouldn't do). This was changed in Git 1.8.2, though, so in modern versions of Git you can use the commands above even prior to making your first commit:

\n
\n

"git reset" (without options or parameters) used to error out when\nyou do not have any commits in your history, but it now gives you\nan empty index (to match non-existent commit you are not even on).

\n
\n

Documentation: git reset

\n", + "upvotes": 16994, + "upvoterUsernames": [], + "downvotes": 4412, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90061", + "creator": "Bart Kiers", + "createdAt": 1230890105000, + "text": "

The notion that regex doesn't support inverse matching is not entirely true. You can mimic this behavior by using negative look-arounds:

\n
^((?!hede).)*$\n
\n

Non-capturing variant:

\n
^(?:(?!:hede).)*$\n
\n

The regex above will match any string, or line without a line break, not containing the (sub)string 'hede'. As mentioned, this is not something regex is "good" at (or should do), but still, it is possible.

\n

And if you need to match line break chars as well, use the DOT-ALL modifier (the trailing s in the following pattern):

\n
/^((?!hede).)*$/s\n
\n

or use it inline:

\n
/(?s)^((?!hede).)*$/\n
\n

(where the /.../ are the regex delimiters, i.e., not part of the pattern)

\n

If the DOT-ALL modifier is not available, you can mimic the same behavior with the character class [\\s\\S]:

\n
/^((?!hede)[\\s\\S])*$/\n
\n

Explanation

\n

A string is just a list of n characters. Before, and after each character, there's an empty string. So a list of n characters will have n+1 empty strings. Consider the string "ABhedeCD":

\n
    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐\nS = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│\n    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘\n    \nindex    0      1      2      3      4      5      6      7\n
\n

where the e's are the empty strings. The regex (?!hede). looks ahead to see if there's no substring "hede" to be seen, and if that is the case (so something else is seen), then the . (dot) will match any character except a line break. Look-arounds are also called zero-width-assertions because they don't consume any characters. They only assert/validate something.

\n

So, in my example, every empty string is first validated to see if there's no "hede" up ahead, before a character is consumed by the . (dot). The regex (?!hede). will do that only once, so it is wrapped in a group, and repeated zero or more times: ((?!hede).)*. Finally, the start- and end-of-input are anchored to make sure the entire input is consumed: ^((?!hede).)*$

\n

As you can see, the input "ABhedeCD" will fail because on e3, the regex (?!hede) fails (there is "hede" up ahead!).

\n", + "upvotes": 9713, + "upvoterUsernames": [], + "downvotes": 2797, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91216", + "creator": "Paul Stephenson", + "createdAt": 1231403984000, + "text": "

You can use a global variable within other functions by declaring it as global within each function that assigns a value to it:

\n
globvar = 0\n\ndef set_globvar_to_one():\n    global globvar    # Needed to modify global copy of globvar\n    globvar = 1\n\ndef print_globvar():\n    print(globvar)     # No need for global declaration to read value of globvar\n\nset_globvar_to_one()\nprint_globvar()       # Prints 1\n
\n

Since it's unclear whether globvar = 1 is creating a local variable or changing a global variable, Python defaults to creating a local variable, and makes you explicitly choose the other behavior with the global keyword.

\n

See other answers if you want to share a global variable across modules.

\n", + "upvotes": 8785, + "upvoterUsernames": [], + "downvotes": 3861, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91246", + "creator": "Ryan McGeary", + "createdAt": 1231419742000, + "text": "

Preferred Way (because it's a plumbing command; meant to be programmatic):

\n\n
$ git diff-tree --no-commit-id --name-only -r bd61ad98\nindex.html\njavascript/application.js\njavascript/ie6.js\n
\n\n

Another Way (less preferred for scripts, because it's a porcelain command; meant to be user-facing)

\n\n
$ git show --pretty=\"\" --name-only bd61ad98    \nindex.html\njavascript/application.js\njavascript/ie6.js\n
\n\n
\n\n\n", + "upvotes": 5069, + "upvoterUsernames": [], + "downvotes": 601, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006b", + "creator": "Xian", + "createdAt": 1231453508000, + "text": "

Modern jQuery

\n\n

Use .prop():

\n\n
$('.myCheckbox').prop('checked', true);\n$('.myCheckbox').prop('checked', false);\n
\n\n

DOM API

\n\n

If you're working with just one element, you can always just access the underlying HTMLInputElement and modify its .checked property:

\n\n
$('.myCheckbox')[0].checked = true;\n$('.myCheckbox')[0].checked = false;\n
\n\n

The benefit to using the .prop() and .attr() methods instead of this is that they will operate on all matched elements.

\n\n

jQuery 1.5.x and below

\n\n

The .prop() method is not available, so you need to use .attr().

\n\n
$('.myCheckbox').attr('checked', true);\n$('.myCheckbox').attr('checked', false);\n
\n\n

Note that this is the approach used by jQuery's unit tests prior to version 1.6 and is preferable to using $('.myCheckbox').removeAttr('checked'); since the latter will, if the box was initially checked, change the behaviour of a call to .reset() on any form that contains it – a subtle but probably unwelcome behaviour change.

\n\n

For more context, some incomplete discussion of the changes to the handling of the checked attribute/property in the transition from 1.5.x to 1.6 can be found in the version 1.6 release notes and the Attributes vs. Properties section of the .prop() documentation.

\n", + "upvotes": 11386, + "upvoterUsernames": [], + "downvotes": 5058, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004b", + "creator": "Gumbo", + "createdAt": 1232897249000, + "text": "

For JSON text:

\n
application/json\n
\n
\n

The MIME media type for JSON text is application/json. The default encoding is UTF-8. (Source: RFC 4627)

\n
\n

For JSONP (runnable JavaScript) with callback:

\n
application/javascript\n
\n

Here are some blog posts that were mentioned in the relevant comments:

\n\n", + "upvotes": 21009, + "upvoterUsernames": [], + "downvotes": 9733, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f32a0c082fcc3049e92fc2", + "creator": "Gumbo", + "createdAt": 1232897249000, + "text": "

For JSON text:

\n
application/json\n
\n
\n

The MIME media type for JSON text is application/json. The default encoding is UTF-8. (Source: RFC 4627)

\n
\n

For JSONP (runnable JavaScript) with callback:

\n
application/javascript\n
\n

Here are some blog posts that were mentioned in the relevant comments:

\n\n", + "upvotes": 14799, + "upvoterUsernames": [], + "downvotes": 3523, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90043", + "creator": "Ryan McGeary", + "createdAt": 1233635049000, + "text": "

One does not simply redirect using jQuery

\n\n

jQuery is not necessary, and window.location.replace(...) will best simulate an HTTP redirect.

\n\n

window.location.replace(...) is better than using window.location.href, because replace() does not keep the originating page in the session history, meaning the user won't get stuck in a never-ending back-button fiasco.

\n\n

If you want to simulate someone clicking on a link, use\n location.href

\n\n

If you want to simulate an HTTP redirect, use location.replace

\n\n

For example:

\n\n
// similar behavior as an HTTP redirect\nwindow.location.replace(\"http://stackoverflow.com\");\n\n// similar behavior as clicking on a link\nwindow.location.href = \"http://stackoverflow.com\";\n
\n", + "upvotes": 24727, + "upvoterUsernames": [], + "downvotes": 8764, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90071", + "creator": "Greg Hewgill", + "createdAt": 1233701304000, + "text": "

The syntax is:

\n
a[start:stop]  # items start through stop-1\na[start:]      # items start through the rest of the array\na[:stop]       # items from the beginning through stop-1\na[:]           # a copy of the whole array\n
\n

There is also the step value, which can be used with any of the above:

\n
a[start:stop:step] # start through not past stop, by step\n
\n

The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default).

\n

The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So:

\n
a[-1]    # last item in the array\na[-2:]   # last two items in the array\na[:-2]   # everything except the last two items\n
\n

Similarly, step may be a negative number:

\n
a[::-1]    # all items in the array, reversed\na[1::-1]   # the first two items, reversed\na[:-3:-1]  # the last two items, reversed\na[-3::-1]  # everything except the last two items, reversed\n
\n

Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen.

\n

Relationship with the slice object

\n

A slice object can represent a slicing operation, i.e.:

\n
a[start:stop:step]\n
\n

is equivalent to:

\n
a[slice(start, stop, step)]\n
\n

Slice objects also behave slightly differently depending on the number of arguments, similarly to range(), i.e. both slice(stop) and slice(start, stop[, step]) are supported.\nTo skip specifying a given argument, one might use None, so that e.g. a[start:] is equivalent to a[slice(start, None)] or a[::-1] is equivalent to a[slice(None, None, -1)].

\n

While the :-based notation is very helpful for simple slicing, the explicit use of slice() objects simplifies the programmatic generation of slicing.

\n", + "upvotes": 9102, + "upvoterUsernames": [], + "downvotes": 3019, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90055", + "creator": "Mike Hordecki", + "createdAt": 1233960756000, + "text": "

Use the built-in function enumerate():

\n
for idx, x in enumerate(xs):\n    print(idx, x)\n
\n

It is non-pythonic to manually index via for i in range(len(xs)): x = xs[i] or manually manage an additional state variable.

\n

Check out PEP 279 for more.

\n", + "upvotes": 8485, + "upvoterUsernames": [], + "downvotes": 537, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dcf", + "creator": "Mike Hordecki", + "createdAt": 1233960756000, + "text": "

Use the built-in function enumerate():

\n
for idx, x in enumerate(xs):\n    print(idx, x)\n
\n

It is non-pythonic to manually index via for i in range(len(xs)): x = xs[i] or manually manage an additional state variable.

\n

Check out PEP 279 for more.

\n", + "upvotes": 13068, + "upvoterUsernames": [], + "downvotes": 5119, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91226", + "creator": "CB Bailey", + "createdAt": 1235206804000, + "text": "

This answer only applies to Git version 1.x. For Git version 2.x, see other answers.

\n
\n

Summary:

\n\n
\n

Detail:

\n

git add -A is equivalent to git add .; git add -u.

\n

The important point about git add . is that it looks at the working tree and adds all those paths to the staged changes if they are either changed or are new and not ignored, it does not stage any 'rm' actions.

\n

git add -u looks at all the already tracked files and stages the changes to those files if they are different or if they have been removed. It does not add any new files, it only stages changes to already tracked files.

\n

git add -A is a handy shortcut for doing both of those.

\n

You can test the differences out with something like this (note that for Git version 2.x your output for git add . git status will be different):

\n
git init\necho Change me > change-me\necho Delete me > delete-me\ngit add change-me delete-me\ngit commit -m initial\n\necho OK >> change-me\nrm delete-me\necho Add me > add-me\n\ngit status\n# Changed but not updated:\n#   modified:   change-me\n#   deleted:    delete-me\n# Untracked files:\n#   add-me\n\ngit add .\ngit status\n\n# Changes to be committed:\n#   new file:   add-me\n#   modified:   change-me\n# Changed but not updated:\n#   deleted:    delete-me\n\ngit reset\n\ngit add -u\ngit status\n\n# Changes to be committed:\n#   modified:   change-me\n#   deleted:    delete-me\n# Untracked files:\n#   add-me\n\ngit reset\n\ngit add -A\ngit status\n\n# Changes to be committed:\n#   new file:   add-me\n#   modified:   change-me\n#   deleted:    delete-me\n
\n", + "upvotes": 7140, + "upvoterUsernames": [], + "downvotes": 2434, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006e", + "creator": "tvanfosson", + "createdAt": 1235572376000, + "text": "

You want the splice function on the native array object.

\n

arr.splice(index, 0, item); will insert item into arr at the specified index (deleting 0 items first, that is, it's just an insert).

\n

In this example we will create an array and add an element to it into index 2:

\n

\r\n
\r\n
var arr = [];\narr[0] = \"Jani\";\narr[1] = \"Hege\";\narr[2] = \"Stale\";\narr[3] = \"Kai Jim\";\narr[4] = \"Borge\";\n\nconsole.log(arr.join()); // Jani,Hege,Stale,Kai Jim,Borge\narr.splice(2, 0, \"Lene\");\nconsole.log(arr.join()); // Jani,Hege,Lene,Stale,Kai Jim,Borge
\r\n
\r\n
\r\n

\n", + "upvotes": 6221, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911ec", + "creator": "Aaron Maenpaa", + "createdAt": 1236083178000, + "text": "

Decode the bytes object to produce a string:

\n
>>> b"abcde".decode("utf-8") \n'abcde'\n
\n

The above example assumes that the bytes object is in UTF-8, because it is a common encoding. However, you should use the encoding your data is actually in!

\n", + "upvotes": 6218, + "upvoterUsernames": [], + "downvotes": 980, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90069", + "creator": "Devin Jeanpierre", + "createdAt": 1236214774000, + "text": "

Python 3.7+ or CPython 3.6

\n

Dicts preserve insertion order in Python 3.7+. Same in CPython 3.6, but it's an implementation detail.

\n
>>> x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}\n>>> {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}\n{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}\n
\n

or

\n
>>> dict(sorted(x.items(), key=lambda item: item[1]))\n{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}\n
\n

Older Python

\n

It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.

\n

For instance,

\n
import operator\nx = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}\nsorted_x = sorted(x.items(), key=operator.itemgetter(1))\n
\n

sorted_x will be a list of tuples sorted by the second element in each tuple. dict(sorted_x) == x.

\n

And for those wishing to sort on keys instead of values:

\n
import operator\nx = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}\nsorted_x = sorted(x.items(), key=operator.itemgetter(0))\n
\n

In Python3 since unpacking is not allowed we can use

\n
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}\nsorted_x = sorted(x.items(), key=lambda kv: kv[1])\n
\n

If you want the output as a dict, you can use collections.OrderedDict:

\n
import collections\n\nsorted_dict = collections.OrderedDict(sorted_x)\n
\n", + "upvotes": 11380, + "upvoterUsernames": [], + "downvotes": 4898, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911f6", + "creator": "John Feminella", + "createdAt": 1236869401000, + "text": "

The test command (written as [ here) has a "not" logical operator, ! (exclamation mark):

\n
if [ ! -f /tmp/foo.txt ]; then\n    echo "File not found!"\nfi\n
\n", + "upvotes": 8413, + "upvoterUsernames": [], + "downvotes": 3272, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91212", + "creator": "levik", + "createdAt": 1238047933000, + "text": "

You can use the for-in loop as shown by others. However, you also have to make sure that the key you get is an actual property of an object, and doesn't come from the prototype.

\n\n

Here is the snippet:\n

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\n\r\nfor (var key in p) {\r\n    if (p.hasOwnProperty(key)) {\r\n        console.log(key + \" -> \" + p[key]);\r\n    }\r\n}
\r\n
\r\n
\r\n

\n\n

For-of with Object.keys() alternative:

\n\n

\r\n
\r\n
var p = {\r\n    0: \"value1\",\r\n    \"b\": \"value2\",\r\n    key: \"value3\"\r\n};\r\n\r\nfor (var key of Object.keys(p)) {\r\n    console.log(key + \" -> \" + p[key])\r\n}
\r\n
\r\n
\r\n

\n\n

Notice the use of for-of instead of for-in, if not used it will return undefined on named properties, and Object.keys() ensures the use of only the object's own properties without the whole prototype-chain properties

\n\n

Using the new Object.entries() method:

\n\n

Note: This method is not supported natively by Internet Explorer. You may consider using a Polyfill for older browsers.

\n\n
const p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\nfor (let [key, value] of Object.entries(p)) {\n  console.log(`${key}: ${value}`);\n}\n
\n", + "upvotes": 6378, + "upvoterUsernames": [], + "downvotes": 1396, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90041", + "creator": "Esko Luontola", + "createdAt": 1243620822000, + "text": "

Undo a commit & redo

\n
$ git commit -m "Something terribly misguided" # (0: Your Accident)\n$ git reset HEAD~                              # (1)\n[ edit files as necessary ]                    # (2)\n$ git add .                                    # (3)\n$ git commit -c ORIG_HEAD                      # (4)\n
\n
    \n
  1. git reset is the command responsible for the undo. It will undo your last commit while leaving your working tree (the state of your files on disk) untouched. You'll need to add them again before you can commit them again).
  2. \n
  3. Make corrections to working tree files.
  4. \n
  5. git add anything that you want to include in your new commit.
  6. \n
  7. Commit the changes, reusing the old commit message. reset copied the old head to .git/ORIG_HEAD; commit with -c ORIG_HEAD will open an editor, which initially contains the log message from the old commit and allows you to edit it. If you do not need to edit the message, you could use the -C option.
  8. \n
\n

Alternatively, to edit the previous commit (or just its commit message), commit --amend will add changes within the current index to the previous commit.

\n

To remove (not revert) a commit that has been pushed to the server, rewriting history with git push origin main --force[-with-lease] is necessary. It's almost always a bad idea to use --force; prefer --force-with-lease instead, and as noted in the git manual:

\n
\n

You should understand the implications of rewriting history if you [rewrite history] has already been published.

\n
\n
\n

Further Reading

\n

You can use git reflog to determine the SHA-1 for the commit to which you wish to revert. Once you have this value, use the sequence of commands as explained above.

\n
\n

HEAD~ is the same as HEAD~1. The article What is the HEAD in git? is helpful if you want to uncommit multiple commits.

\n", + "upvotes": 35068, + "upvoterUsernames": [], + "downvotes": 8260, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91220", + "creator": "Jamie Flournoy", + "createdAt": 1243807840000, + "text": "

Another way to make a directory stay (almost) empty (in the repository) is to create a .gitignore file inside that directory that contains these four lines:

\n\n
# Ignore everything in this directory\n*\n# Except this file\n!.gitignore\n
\n\n

Then you don't have to get the order right the way that you have to do in m104's solution.

\n\n

This also gives the benefit that files in that directory won't show up as \"untracked\" when you do a git status.

\n\n

Making @GreenAsJade's comment persistent:

\n\n
\n

I think it's worth noting that this solution does precisely what the question asked for, but is not perhaps what many people looking at this question will have been looking for. This solution guarantees that the directory remains empty. It says \"I truly never want files checked in here\". As opposed to \"I don't have any files to check in here, yet, but I need the directory here, files may be coming later\".

\n
\n", + "upvotes": 6331, + "upvoterUsernames": [], + "downvotes": 1516, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90065", + "creator": "Alex Martelli", + "createdAt": 1244147821000, + "text": "

Given a list of lists l,

\n
flat_list = [item for sublist in l for item in sublist]\n
\n

which means:

\n
flat_list = []\nfor sublist in l:\n    for item in sublist:\n        flat_list.append(item)\n
\n

is faster than the shortcuts posted so far. (l is the list to flatten.)

\n

Here is the corresponding function:

\n
def flatten(l):\n    return [item for sublist in l for item in sublist]\n
\n

As evidence, you can use the timeit module in the standard library:

\n
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'\n10000 loops, best of 3: 143 usec per loop\n$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'\n1000 loops, best of 3: 969 usec per loop\n$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'\n1000 loops, best of 3: 1.1 msec per loop\n
\n

Explanation: the shortcuts based on + (including the implied use in sum) are, of necessity, O(L**2) when there are L sublists -- as the intermediate result list keeps getting longer, at each step a new intermediate result list object gets allocated, and all the items in the previous intermediate result must be copied over (as well as a few new ones added at the end). So, for simplicity and without actual loss of generality, say you have L sublists of I items each: the first I items are copied back and forth L-1 times, the second I items L-2 times, and so on; total number of copies is I times the sum of x for x from 1 to L excluded, i.e., I * (L**2)/2.

\n

The list comprehension just generates one list, once, and copies each item over (from its original place of residence to the result list) also exactly once.

\n", + "upvotes": 8861, + "upvoterUsernames": [], + "downvotes": 2223, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005e", + "creator": "Steve Harrison", + "createdAt": 1245659438000, + "text": "

The basic solution is:

\n

\r\n
\r\n
function capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\nconsole.log(capitalizeFirstLetter('foo')); // Foo
\r\n
\r\n
\r\n

\n

Some other answers modify String.prototype (this answer used to as well), but I would advise against this now due to maintainability (hard to find out where the function is being added to the prototype and could cause conflicts if other code uses the same name / a browser adds a native function with that same name in future).

\n

...and then, there is so much more to this question when you consider internationalisation, as this astonishingly good answer (buried below) shows.

\n

If you want to work with Unicode code points instead of code units (for example to handle Unicode characters outside of the Basic Multilingual Plane) you can leverage the fact that String#[@iterator] works with code points, and you can use toLocaleUpperCase to get locale-correct uppercasing:

\n

\r\n
\r\n
const capitalizeFirstLetter = ([ first, ...rest ], locale = navigator.language) =>\n  first === undefined ? '' : first.toLocaleUpperCase(locale) + rest.join('')\n\nconsole.log(\n  capitalizeFirstLetter(''), // [empty string]\n  capitalizeFirstLetter('foo'), // Foo\n  capitalizeFirstLetter(\"𐐶𐐲𐑌𐐼𐐲𐑉\"), // \"𐐎𐐲𐑌𐐼𐐲𐑉\" (correct!)\n  capitalizeFirstLetter(\"italya\", 'tr') // İtalya\" (correct in Turkish Latin!)\n)
\r\n
\r\n
\r\n

\n

For even more internationalization options, please see the original answer below.

\n", + "upvotes": 13321, + "upvoterUsernames": [], + "downvotes": 6128, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd8", + "creator": "Steve Harrison", + "createdAt": 1245659438000, + "text": "

The basic solution is:

\n

\r\n
\r\n
function capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\nconsole.log(capitalizeFirstLetter('foo')); // Foo
\r\n
\r\n
\r\n

\n

Some other answers modify String.prototype (this answer used to as well), but I would advise against this now due to maintainability (hard to find out where the function is being added to the prototype and could cause conflicts if other code uses the same name / a browser adds a native function with that same name in future).

\n

...and then, there is so much more to this question when you consider internationalisation, as this astonishingly good answer (buried below) shows.

\n

If you want to work with Unicode code points instead of code units (for example to handle Unicode characters outside of the Basic Multilingual Plane) you can leverage the fact that String#[@iterator] works with code points, and you can use toLocaleUpperCase to get locale-correct uppercasing:

\n

\r\n
\r\n
const capitalizeFirstLetter = ([ first, ...rest ], locale = navigator.language) =>\n  first === undefined ? '' : first.toLocaleUpperCase(locale) + rest.join('')\n\nconsole.log(\n  capitalizeFirstLetter(''), // [empty string]\n  capitalizeFirstLetter('foo'), // Foo\n  capitalizeFirstLetter(\"𐐶𐐲𐑌𐐼𐐲𐑉\"), // \"𐐎𐐲𐑌𐐼𐐲𐑉\" (correct!)\n  capitalizeFirstLetter(\"italya\", 'tr') // İtalya\" (correct in Turkish Latin!)\n)
\r\n
\r\n
\r\n

\n

For even more internationalization options, please see the original answer below.

\n", + "upvotes": 13036, + "upvoterUsernames": [], + "downvotes": 5843, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90083", + "creator": "harto", + "createdAt": 1246404507000, + "text": "

If you're only interested in the keys, you can iterate through the keySet() of the map:

\n\n
Map<String, Object> map = ...;\n\nfor (String key : map.keySet()) {\n    // ...\n}\n
\n\n

If you only need the values, use values():

\n\n
for (Object value : map.values()) {\n    // ...\n}\n
\n\n

Finally, if you want both the key and value, use entrySet():

\n\n
for (Map.Entry<String, Object> entry : map.entrySet()) {\n    String key = entry.getKey();\n    Object value = entry.getValue();\n    // ...\n}\n
\n\n

One caveat: if you want to remove items mid-iteration, you'll need to do so via an Iterator (see karim79's answer). However, changing item values is OK (see Map.Entry).

\n", + "upvotes": 10024, + "upvoterUsernames": [], + "downvotes": 4757, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911ea", + "creator": "harto", + "createdAt": 1246404507000, + "text": "

If you're only interested in the keys, you can iterate through the keySet() of the map:

\n\n
Map<String, Object> map = ...;\n\nfor (String key : map.keySet()) {\n    // ...\n}\n
\n\n

If you only need the values, use values():

\n\n
for (Object value : map.values()) {\n    // ...\n}\n
\n\n

Finally, if you want both the key and value, use entrySet():

\n\n
for (Map.Entry<String, Object> entry : map.entrySet()) {\n    String key = entry.getKey();\n    Object value = entry.getValue();\n    // ...\n}\n
\n\n

One caveat: if you want to remove items mid-iteration, you'll need to do so via an Iterator (see karim79's answer). However, changing item values is OK (see Map.Entry).

\n", + "upvotes": 5577, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91243", + "creator": "cloudhead", + "createdAt": 1246847802000, + "text": "

You can use:

\n
git add --patch <filename>\n
\n

or for short:

\n
git add -p <filename>\n
\n

Git will break down your file into what it thinks are sensible "hunks" (portions of the file). It will then prompt you with this question:

\n
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?\n
\n

Here is a description of each option:

\n\n

If the file is not in the repository yet, you can first do git add -N <filename>. Afterwards you can go on with git add -p <filename>.

\n

Afterwards, you can use:

\n\n

Note this is far different than the git format-patch command, whose purpose is to parse commit data into a .patch files.

\n

Reference for future: Git Tools - Interactive Staging

\n", + "upvotes": 8663, + "upvoterUsernames": [], + "downvotes": 4185, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911f2", + "creator": "Ates Goral", + "createdAt": 1247068270000, + "text": "

Checking for undefined-ness is not an accurate way of testing whether a key exists. What if the key exists but the value is actually undefined?

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(obj[\"key\"] !== undefined); // false, but the key exists!
\r\n
\r\n
\r\n

\n

You should instead use the in operator:

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(\"key\" in obj); // true, regardless of the actual value
\r\n
\r\n
\r\n

\n

If you want to check if a key doesn't exist, remember to use parenthesis:

\n

\r\n
\r\n
var obj = { not_key: undefined };\nconsole.log(!(\"key\" in obj)); // true if \"key\" doesn't exist in object\nconsole.log(!\"key\" in obj);   // Do not do this! It is equivalent to \"false in obj\"
\r\n
\r\n
\r\n

\n

Or, if you want to particularly test for properties of the object instance (and not inherited properties), use hasOwnProperty:

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(obj.hasOwnProperty(\"key\")); // true
\r\n
\r\n
\r\n

\n

For performance comparison between the methods that are in, hasOwnProperty and key is undefined, see this benchmark:

\n

\"Benchmark

\n", + "upvotes": 6625, + "upvoterUsernames": [], + "downvotes": 1447, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9122a", + "creator": "Reto Meier", + "createdAt": 1247226720000, + "text": "

You can force Android to hide the virtual keyboard using the InputMethodManager, calling hideSoftInputFromWindow, passing in the token of the window containing your focused view.

\n
// Check if no view has focus:\nView view = this.getCurrentFocus();\nif (view != null) {  \n    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);\n    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);\n}\n
\n

This will force the keyboard to be hidden in all situations. In some cases, you will want to pass in InputMethodManager.HIDE_IMPLICIT_ONLY as the second parameter to ensure you only hide the keyboard when the user didn't explicitly force it to appear (by holding down the menu).

\n

Note: If you want to do this in Kotlin, use:\ncontext?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

\n

Kotlin Syntax

\n
// Only runs if there is a view that is currently focused\nthis.currentFocus?.let { view ->\n    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager\n    imm?.hideSoftInputFromWindow(view.windowToken, 0)\n}\n
\n", + "upvotes": 6644, + "upvoterUsernames": [], + "downvotes": 2004, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911f4", + "creator": "Wogan", + "createdAt": 1247628951000, + "text": "

It's easy enough to write your own comparison function:

\n
function compare( a, b ) {\n  if ( a.last_nom < b.last_nom ){\n    return -1;\n  }\n  if ( a.last_nom > b.last_nom ){\n    return 1;\n  }\n  return 0;\n}\n\nobjs.sort( compare );\n
\n

Or inline (c/o Marco Demaio):

\n
objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))\n
\n

Or simplified for numeric (c/o Andre Figueiredo):

\n
objs.sort((a,b) => a.last_nom - b.last_nom); // b - a for reverse sort\n
\n", + "upvotes": 8896, + "upvoterUsernames": [], + "downvotes": 3722, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91238", + "creator": "trobrock", + "createdAt": 1247772770000, + "text": "

To untrack a single file that has already been added/initialized to your repository, i.e., stop tracking the file but not delete it from your system use: git rm --cached filename

\n

To untrack every file that is now in your .gitignore:

\n

First commit any outstanding code changes, and then, run this command:

\n
git rm -r --cached .\n
\n

This removes any changed files from the index(staging area), then just run:

\n
git add .\n
\n

Commit it:

\n
git commit -m ".gitignore is now working"\n
\n
\n

To undo git rm --cached filename, use git add filename.

\n
\n

Make sure to commit all your important changes before running git add .\nOtherwise, you will lose any changes to other files.

\n
\n
\n

Please be careful, when you push this to a repository and pull from somewhere else into a state where those files are still tracked, the files will be DELETED

\n
\n", + "upvotes": 7902, + "upvoterUsernames": [], + "downvotes": 3377, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9120a", + "creator": "bdonlan", + "createdAt": 1247842646000, + "text": "

The git rm documentation states:

\n
\n

When --cached is given, the staged content has to match either the tip of the branch or the file on disk, allowing the file to be removed from just the index.

\n
\n

So, for a single file:

\n
git rm --cached file_to_remove.txt\n
\n

and for a single directory:

\n
git rm --cached -r directory_to_remove\n
\n", + "upvotes": 8227, + "upvoterUsernames": [], + "downvotes": 3204, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91208", + "creator": "Sean Bright", + "createdAt": 1247853289000, + "text": "

As of August 2020: Modern browsers have support for the String.replaceAll() method defined by the ECMAScript 2021 language specification.

\n
\n

For older/legacy browsers:

\n
function escapeRegExp(string) {\n  return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n\nfunction replaceAll(str, find, replace) {\n  return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);\n}\n
\n

Here is how this answer evolved:

\n
str = str.replace(/abc/g, '');\n
\n

In response to comment "what's if 'abc' is passed as a variable?":

\n
var find = 'abc';\nvar re = new RegExp(find, 'g');\n\nstr = str.replace(re, '');\n
\n

In response to Click Upvote's comment, you could simplify it even more:

\n
function replaceAll(str, find, replace) {\n  return str.replace(new RegExp(find, 'g'), replace);\n}\n
\n

Note: Regular expressions contain special (meta) characters, and as such it is dangerous to blindly pass an argument in the find function above without pre-processing it to escape those characters. This is covered in the Mozilla Developer Network's JavaScript Guide on Regular Expressions, where they present the following utility function (which has changed at least twice since this answer was originally written, so make sure to check the MDN site for potential updates):

\n
function escapeRegExp(string) {\n  return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n
\n

So in order to make the replaceAll() function above safer, it could be modified to the following if you also include escapeRegExp:

\n
function replaceAll(str, find, replace) {\n  return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);\n}\n
\n", + "upvotes": 9564, + "upvoterUsernames": [], + "downvotes": 4531, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005b", + "creator": "CB Bailey", + "createdAt": 1250196059000, + "text": "

.gitignore will prevent untracked files from being added (without an add -f) to the set of files tracked by Git. However, Git will continue to track any files that are already being tracked.

\n

To stop tracking a file, we must remove it from the index:

\n
git rm --cached <file>\n
\n

To remove a folder and all files in the folder recursively:

\n
git rm -r --cached <folder>\n
\n

The removal of the file from the head revision will happen on the next commit.

\n

WARNING: While this will not remove the physical file from your local machine, it will remove the files from other developers' machines on their next git pull.

\n", + "upvotes": 11594, + "upvoterUsernames": [], + "downvotes": 3813, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd5", + "creator": "CB Bailey", + "createdAt": 1250196059000, + "text": "

.gitignore will prevent untracked files from being added (without an add -f) to the set of files tracked by Git. However, Git will continue to track any files that are already being tracked.

\n

To stop tracking a file, we must remove it from the index:

\n
git rm --cached <file>\n
\n

To remove a folder and all files in the folder recursively:

\n
git rm -r --cached <folder>\n
\n

The removal of the file from the head revision will happen on the next commit.

\n

WARNING: While this will not remove the physical file from your local machine, it will remove the files from other developers' machines on their next git pull.

\n", + "upvotes": 9356, + "upvoterUsernames": [], + "downvotes": 1575, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007f", + "creator": "Pascal MARTIN", + "createdAt": 1251303339000, + "text": "

Update for ES6 modules

\n

Inside native ECMAScript modules (with import and export statements) and ES6 classes, strict mode is always enabled and cannot be disabled.

\n

Original answer

\n

This article about Javascript Strict Mode might interest you: John Resig - ECMAScript 5 Strict Mode, JSON, and More

\n

To quote some interesting parts:

\n
\n

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a "strict" operating context. This strict context prevents certain actions from being taken and throws more exceptions.

\n
\n

And:

\n
\n

Strict mode helps out in a couple ways:

\n\n
\n

Also note you can apply "strict mode" to the whole file... Or you can use it only for a specific function (still quoting from John Resig's article):

\n
// Non-strict code...\n\n(function(){\n  "use strict";\n\n  // Define your library strictly...\n})();\n\n// Non-strict code...\n
\n

Which might be helpful if you have to mix old and new code ;-)

\n

So, I suppose it's a bit like the "use strict" you can use in Perl (hence the name?): it helps you make fewer errors, by detecting more things that could lead to breakages.

\n

Strict mode is now supported by all major browsers.

\n", + "upvotes": 6198, + "upvoterUsernames": [], + "downvotes": 846, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e6", + "creator": "Pascal MARTIN", + "createdAt": 1251303339000, + "text": "

Update for ES6 modules

\n

Inside native ECMAScript modules (with import and export statements) and ES6 classes, strict mode is always enabled and cannot be disabled.

\n

Original answer

\n

This article about Javascript Strict Mode might interest you: John Resig - ECMAScript 5 Strict Mode, JSON, and More

\n

To quote some interesting parts:

\n
\n

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a "strict" operating context. This strict context prevents certain actions from being taken and throws more exceptions.

\n
\n

And:

\n
\n

Strict mode helps out in a couple ways:

\n\n
\n

Also note you can apply "strict mode" to the whole file... Or you can use it only for a specific function (still quoting from John Resig's article):

\n
// Non-strict code...\n\n(function(){\n  "use strict";\n\n  // Define your library strictly...\n})();\n\n// Non-strict code...\n
\n

Which might be helpful if you have to mix old and new code ;-)

\n

So, I suppose it's a bit like the "use strict" you can use in Perl (hence the name?): it helps you make fewer errors, by detecting more things that could lead to breakages.

\n

Strict mode is now supported by all major browsers.

\n", + "upvotes": 6454, + "upvoterUsernames": [], + "downvotes": 1102, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9120c", + "creator": "gahooa", + "createdAt": 1251344644000, + "text": "

Careful: git reset --hard WILL DELETE YOUR WORKING DIRECTORY CHANGES. Be sure to stash any local changes you want to keep before running this command.

\n\n

Assuming you are sitting on that commit, then this command will wack it...

\n\n
git reset --hard HEAD~1\n
\n\n

The HEAD~1 means the commit before head.

\n\n

Or, you could look at the output of git log, find the commit id of the commit you want to back up to, and then do this:

\n\n
git reset --hard <sha1-commit-id>\n
\n\n
\n\n

If you already pushed it, you will need to do a force push to get rid of it...

\n\n
git push origin HEAD --force\n
\n\n

However, if others may have pulled it, then you would be better off starting a new branch. Because when they pull, it will just merge it into their work, and you will get it pushed back up again.

\n\n

If you already pushed, it may be better to use git revert, to create a \"mirror image\" commit that will undo the changes. However, both commits will be in the log.

\n\n
\n\n

FYI -- git reset --hard HEAD is great if you want to get rid of WORK IN PROGRESS. It will reset you back to the most recent commit, and erase all the changes in your working tree and index.

\n\n
\n\n

Lastly, if you need to find a commit that you \"deleted\", it is typically present in git reflog unless you have garbage collected your repository.

\n", + "upvotes": 7257, + "upvoterUsernames": [], + "downvotes": 2236, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911f8", + "creator": "codeape", + "createdAt": 1253820924000, + "text": "

Modern browsers have Array#includes, which does exactly that and is widely supported by everyone except IE:

\n

\r\n
\r\n
console.log(['joe', 'jane', 'mary'].includes('jane')); //true
\r\n
\r\n
\r\n

\n

You can also use Array#indexOf, which is less direct, but doesn't require polyfills for outdated browsers.

\n

\r\n
\r\n
console.log(['joe', 'jane', 'mary'].indexOf('jane') >= 0); //true
\r\n
\r\n
\r\n

\n
\n

Many frameworks also offer similar methods:

\n\n

Notice that some frameworks implement this as a function, while others add the function to the array prototype.

\n", + "upvotes": 9650, + "upvoterUsernames": [], + "downvotes": 4514, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91245", + "creator": "Greg Hewgill", + "createdAt": 1255730020000, + "text": "

Try:

\n
git config core.fileMode false\n
\n

From git-config(1):

\n
\n
core.fileMode\n    Tells Git if the executable bit of files in the working tree\n    is to be honored.\n\n    Some filesystems lose the executable bit when a file that is\n    marked as executable is checked out, or checks out a\n    non-executable file with executable bit on. git-clone(1)\n    or git-init(1) probe the filesystem to see if it handles the \n    executable bit correctly and this variable is automatically\n    set as necessary.\n\n    A repository, however, may be on a filesystem that handles\n    the filemode correctly, and this variable is set to true when\n    created, but later may be made accessible from another\n    environment that loses the filemode (e.g. exporting ext4\n    via CIFS mount, visiting a Cygwin created repository with Git\n    for Windows or Eclipse). In such a case it may be necessary\n    to set this variable to false. See git-update-index(1).\n\n    The default is true (when core.filemode is not specified\n    in the config file).\n
\n
\n

The -c flag can be used to set this option for one-off commands:

\n
git -c core.fileMode=false diff\n
\n

Typing the -c core.fileMode=false can be bothersome and so you can set this flag for all git repos or just for one git repo:

\n
# this will set your the flag for your user for all git repos (modifies `$HOME/.gitconfig`)\ngit config --global core.fileMode false\n\n# this will set the flag for one git repo (modifies `$current_git_repo/.git/config`)\ngit config core.fileMode false\n
\n

Additionally, git clone and git init explicitly set core.fileMode to true in the repo config as discussed in Git global core.fileMode false overridden locally on clone

\n

Warning

\n

core.fileMode is not the best practice and should be used carefully. This setting only covers the executable bit of mode and never the read/write bits. In many cases you think you need this setting because you did something like chmod -R 777, making all your files executable. But in most projects most files don't need and should not be executable for security reasons.

\n

The proper way to solve this kind of situation is to handle folder and file permission separately, with something like:

\n
find . -type d -exec chmod a+rwx {} \\; # Make folders traversable and read/write\nfind . -type f -exec chmod a+rw {} \\;  # Make files read/write\n
\n

If you do that, you'll never need to use core.fileMode, except in very rare environment.

\n", + "upvotes": 8798, + "upvoterUsernames": [], + "downvotes": 4326, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91222", + "creator": "Chris B.", + "createdAt": 1256152221000, + "text": "

in tests for the existence of a key in a dict:

\n
d = {"key1": 10, "key2": 23}\n\nif "key1" in d:\n    print("this will execute")\n\nif "nonexistent key" in d:\n    print("this will not")\n
\n
\n

Use dict.get() to provide a default value when the key does not exist:

\n
d = {}\n\nfor i in range(10):\n    d[i] = d.get(i, 0) + 1\n
\n
\n

To provide a default value for every key, either use dict.setdefault() on each assignment:

\n
d = {}\n\nfor i in range(10):\n    d[i] = d.setdefault(i, 0) + 1\n
\n

or use defaultdict from the collections module:

\n
from collections import defaultdict\n\nd = defaultdict(int)\n\nfor i in range(10):\n    d[i] += 1\n
\n", + "upvotes": 5153, + "upvoterUsernames": [], + "downvotes": 340, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90051", + "creator": "Dan Moulding", + "createdAt": 1256607870000, + "text": "

Setting your branch to exactly match the remote branch can be done in two steps:

\n
git fetch origin\ngit reset --hard origin/master\n
\n

If you want to save your current branch's state before doing this (just in case), you can do:

\n
git commit -a -m "Saving my work, just in case"\ngit branch my-saved-work\n
\n

Now your work is saved on the branch "my-saved-work" in case you decide you want it back (or want to look at it later or diff it against your updated branch).

\n

Note that the first example assumes that the remote repo's name is "origin" and that the branch named "master" in the remote repo matches the currently checked-out branch in your local repo.

\n

BTW, this situation that you're in looks an awful lot like a common case where a push has been done into the currently checked out branch of a non-bare repository. Did you recently push into your local repo? If not, then no worries -- something else must have caused these files to unexpectedly end up modified. Otherwise, you should be aware that it's not recommended to push into a non-bare repository (and not into the currently checked-out branch, in particular).

\n", + "upvotes": 13926, + "upvoterUsernames": [], + "downvotes": 5154, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005a", + "creator": "sykora", + "createdAt": 1256613315000, + "text": "

Moving to an existing branch

\n

If you want to move your commits to an existing branch, it will look like this:

\n
git checkout existingbranch\ngit merge master\ngit checkout master\ngit reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.\ngit checkout existingbranch\n
\n

You can store uncommitted edits to your stash before doing this, using git stash. Once complete, you can retrieve the stashed uncommitted edits with git stash pop

\n

Moving to a new branch

\n

WARNING: This method works because you are creating a new branch with the first command: git branch newbranch. If you want to move commits to an existing branch you need to merge your changes into the existing branch before executing git reset --hard HEAD~3 (see Moving to an existing branch above). If you don't merge your changes first, they will be lost.

\n

Unless there are other circumstances involved, this can be easily done by branching and rolling back.

\n\n
# Note: Any changes not committed will be lost.\ngit branch newbranch      # Create a new branch, saving the desired commits\ngit checkout master       # checkout master, this is the place you want to go back\ngit reset --hard HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)\ngit checkout newbranch    # Go to the new branch that still has the desired commits\n
\n

But do make sure how many commits to go back. Alternatively, you can instead of HEAD~3, simply provide the hash of the commit (or the reference like origin/master) you want to "revert back to" on the master (/current) branch, e.g:

\n
git reset --hard a1b2c3d4\n
\n

*1 You will only be "losing" commits from the master branch, but don't worry, you'll have those commits in newbranch!

\n

Lastly, you may need to force push your latest changes to main repo:

\n
git push origin master --force\n
\n

WARNING: With Git version 2.0 and later, if you later git rebase the new branch upon the original (master) branch, you may need an explicit --no-fork-point option during the rebase to avoid losing the carried-over commits. Having branch.autosetuprebase always set makes this more likely. See John Mellor's answer for details.

\n", + "upvotes": 10357, + "upvoterUsernames": [], + "downvotes": 2550, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd4", + "creator": "sykora", + "createdAt": 1256613315000, + "text": "

Moving to an existing branch

\n

If you want to move your commits to an existing branch, it will look like this:

\n
git checkout existingbranch\ngit merge master\ngit checkout master\ngit reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.\ngit checkout existingbranch\n
\n

You can store uncommitted edits to your stash before doing this, using git stash. Once complete, you can retrieve the stashed uncommitted edits with git stash pop

\n

Moving to a new branch

\n

WARNING: This method works because you are creating a new branch with the first command: git branch newbranch. If you want to move commits to an existing branch you need to merge your changes into the existing branch before executing git reset --hard HEAD~3 (see Moving to an existing branch above). If you don't merge your changes first, they will be lost.

\n

Unless there are other circumstances involved, this can be easily done by branching and rolling back.

\n\n
# Note: Any changes not committed will be lost.\ngit branch newbranch      # Create a new branch, saving the desired commits\ngit checkout master       # checkout master, this is the place you want to go back\ngit reset --hard HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)\ngit checkout newbranch    # Go to the new branch that still has the desired commits\n
\n

But do make sure how many commits to go back. Alternatively, you can instead of HEAD~3, simply provide the hash of the commit (or the reference like origin/master) you want to "revert back to" on the master (/current) branch, e.g:

\n
git reset --hard a1b2c3d4\n
\n

*1 You will only be "losing" commits from the master branch, but don't worry, you'll have those commits in newbranch!

\n

Lastly, you may need to force push your latest changes to main repo:

\n
git push origin master --force\n
\n

WARNING: With Git version 2.0 and later, if you later git rebase the new branch upon the original (master) branch, you may need an explicit --no-fork-point option during the rebase to avoid losing the carried-over commits. Having branch.autosetuprebase always set makes this more likely. See John Mellor's answer for details.

\n", + "upvotes": 13311, + "upvoterUsernames": [], + "downvotes": 5504, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004f", + "creator": "Charles Salvia", + "createdAt": 1256799609000, + "text": "

--> is not an operator. It is in fact two separate operators, -- and >.

\n

The conditional's code decrements x, while returning x's original (not decremented) value, and then compares the original value with 0 using the > operator.

\n

To better understand, the statement could be written as follows:

\n
while( (x--) > 0 )\n
\n", + "upvotes": 12326, + "upvoterUsernames": [], + "downvotes": 2826, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91204", + "creator": "Daniel G", + "createdAt": 1258009626000, + "text": "

Use the + operator to combine the lists:

\n
listone = [1, 2, 3]\nlisttwo = [4, 5, 6]\n\njoinedlist = listone + listtwo\n
\n

Output:

\n
>>> joinedlist\n[1, 2, 3, 4, 5, 6]\n
\n", + "upvotes": 6864, + "upvoterUsernames": [], + "downvotes": 1814, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004d", + "creator": "hallski", + "createdAt": 1258986368000, + "text": "

The answer has been split depending on whether there is one remote repository configured or multiple. The reason for this is that for the single remote case, some of the commands can be simplified as there is less ambiguity.

\n

Updated for Git 2.23: For older versions, see the section at the end.

\n

With One Remote

\n

In both cases, start by fetching from the remote repository to make sure you have all the latest changes downloaded.

\n
$ git fetch\n
\n

This will fetch all of the remote branches for you. You can see the branches available for checkout with:

\n
$ git branch -v -a\n\n...\nremotes/origin/test\n
\n

The branches that start with remotes/* can be thought of as read only copies of the remote branches. To work on a branch you need to create a local branch from it. This is done with the Git command switch (since Git 2.23) by giving it the name of the remote branch (minus the remote name):

\n
$ git switch test\n
\n

In this case Git is guessing (can be disabled with --no-guess) that you are trying to checkout and track the remote branch with the same name.

\n

With Multiple Remotes

\n

In the case where multiple remote repositories exist, the remote repository needs to be explicitly named.

\n

As before, start by fetching the latest remote changes:

\n
$ git fetch origin\n
\n

This will fetch all of the remote branches for you. You can see the branches available for checkout with:

\n
$ git branch -v -a\n
\n

With the remote branches in hand, you now need to check out the branch you are interested in with -c to create a new local branch:

\n
$ git switch -c test origin/test\n
\n

For more information about using git switch:

\n
$ man git-switch\n
\n

I also created the image below for you to share the differences, look at how to fetch works, and also how it's different to pull:

\n

\"enter

\n

Prior to Git 2.23

\n

git switch was added in Git 2.23, prior to this git checkout was used to switch branches.

\n

To checkout out with only a single remote repository:

\n
git checkout test\n
\n

if there there are multiple remote repositories configured it becomes a bit longer

\n
git checkout -b test <name of remote>/test\n
\n", + "upvotes": 10998, + "upvoterUsernames": [], + "downvotes": 131, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f32a0c082fcc3049e92fc4", + "creator": "hallski", + "createdAt": 1258986368000, + "text": "

The answer has been split depending on whether there is one remote repository configured or multiple. The reason for this is that for the single remote case, some of the commands can be simplified as there is less ambiguity.

\n

Updated for Git 2.23: For older versions, see the section at the end.

\n

With One Remote

\n

In both cases, start by fetching from the remote repository to make sure you have all the latest changes downloaded.

\n
$ git fetch\n
\n

This will fetch all of the remote branches for you. You can see the branches available for checkout with:

\n
$ git branch -v -a\n\n...\nremotes/origin/test\n
\n

The branches that start with remotes/* can be thought of as read only copies of the remote branches. To work on a branch you need to create a local branch from it. This is done with the Git command switch (since Git 2.23) by giving it the name of the remote branch (minus the remote name):

\n
$ git switch test\n
\n

In this case Git is guessing (can be disabled with --no-guess) that you are trying to checkout and track the remote branch with the same name.

\n

With Multiple Remotes

\n

In the case where multiple remote repositories exist, the remote repository needs to be explicitly named.

\n

As before, start by fetching the latest remote changes:

\n
$ git fetch origin\n
\n

This will fetch all of the remote branches for you. You can see the branches available for checkout with:

\n
$ git branch -v -a\n
\n

With the remote branches in hand, you now need to check out the branch you are interested in with -c to create a new local branch:

\n
$ git switch -c test origin/test\n
\n

For more information about using git switch:

\n
$ man git-switch\n
\n

I also created the image below for you to share the differences, look at how to fetch works, and also how it's different to pull:

\n

\"enter

\n

Prior to Git 2.23

\n

git switch was added in Git 2.23, prior to this git checkout was used to switch branches.

\n

To checkout out with only a single remote repository:

\n
git checkout test\n
\n

if there there are multiple remote repositories configured it becomes a bit longer

\n
git checkout -b test <name of remote>/test\n
\n", + "upvotes": 15103, + "upvoterUsernames": [], + "downvotes": 4236, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90045", + "creator": "Fabien Ménager", + "createdAt": 1259067936000, + "text": "

ECMAScript 6 introduced String.prototype.includes:

\n

\r\n
\r\n
const string = \"foo\";\nconst substring = \"oo\";\n\nconsole.log(string.includes(substring)); // true
\r\n
\r\n
\r\n

\n

includes doesn’t have Internet Explorer support, though. In ECMAScript 5 or older environments, use String.prototype.indexOf, which returns -1 when a substring cannot be found:

\n

\r\n
\r\n
var string = \"foo\";\nvar substring = \"oo\";\n\nconsole.log(string.indexOf(substring) !== -1); // true
\r\n
\r\n
\r\n

\n", + "upvotes": 20070, + "upvoterUsernames": [], + "downvotes": 4734, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91214", + "creator": "B Bycroft", + "createdAt": 1261043335000, + "text": "

With Python 2.6+ you can do:

\n\n
echo '{"foo": "lorem", "bar": "ipsum"}' | python -m json.tool\n
\n

or, if the JSON is in a file, you can do:

\n
python -m json.tool my_json.json\n
\n

if the JSON is from an internet source such as an API, you can use

\n
curl http://my_url/ | python -m json.tool\n
\n

For convenience in all of these cases you can make an alias:

\n
alias prettyjson='python -m json.tool'\n
\n
\n

For even more convenience with a bit more typing to get it ready:

\n
prettyjson_s() {\n    echo "$1" | python -m json.tool\n}\n\nprettyjson_f() {\n    python -m json.tool "$1"\n}\n\nprettyjson_w() {\n    curl "$1" | python -m json.tool\n}\n
\n

for all the above cases. You can put this in .bashrc and it will be available every time in shell. Invoke it like prettyjson_s '{"foo": "lorem", "bar": "ipsum"}'.

\n

Note that as @pnd pointed out in the comments below, in Python 3.5+ the JSON object is no longer sorted by default. To sort, add the --sort-keys flag to the end. I.e. ... | python -m json.tool --sort-keys.

\n", + "upvotes": 9482, + "upvoterUsernames": [], + "downvotes": 4508, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911f0", + "creator": "Asaph", + "createdAt": 1263512425000, + "text": "\n\n

Each YouTube video has four generated images. They are predictably formatted as follows:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/0.jpg\nhttps://img.youtube.com/vi/<insert-youtube-video-id-here>/1.jpg\nhttps://img.youtube.com/vi/<insert-youtube-video-id-here>/2.jpg\nhttps://img.youtube.com/vi/<insert-youtube-video-id-here>/3.jpg\n
\n\n

The first one in the list is a full size image and others are thumbnail images. The default thumbnail image (i.e., one of 1.jpg, 2.jpg, 3.jpg) is:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/default.jpg\n
\n\n

For the high quality version of the thumbnail use a URL similar to this:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/hqdefault.jpg\n
\n\n

There is also a medium quality version of the thumbnail, using a URL similar to the HQ:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/mqdefault.jpg\n
\n\n

For the standard definition version of the thumbnail, use a URL similar to this:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/sddefault.jpg\n
\n\n

For the maximum resolution version of the thumbnail use a URL similar to this:

\n\n
https://img.youtube.com/vi/<insert-youtube-video-id-here>/maxresdefault.jpg\n
\n\n

All of the above URLs are available over HTTP too. Additionally, the slightly shorter hostname i3.ytimg.com works in place of img.youtube.com in the example URLs above.

\n\n

Alternatively, you can use the YouTube Data API (v3) to get thumbnail images.

\n", + "upvotes": 8906, + "upvoterUsernames": [], + "downvotes": 3716, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911fc", + "creator": "broofa", + "createdAt": 1264167620000, + "text": "

[Edited 2021-10-16 to reflect latest best-practices for producing RFC4122-compliant UUIDs]

\n

Most readers here will want to use the uuid module. It is well-tested and supported.

\n

The crypto.randomUUID() function is an emerging standard that is supported in Node.js and an increasing number of browsers.

\n

If neither of those work for you, there is this method (based on the original answer to this question):\n

\r\n
\r\n
function uuidv4() {\n  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>\n    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)\n  );\n}\n\nconsole.log(uuidv4());
\r\n
\r\n
\r\n

\n

Note: The use of any UUID generator that relies on Math.random() is strongly discouraged (including snippets featured in previous versions of this answer) for reasons best-explained here. TL;DR: Math.random()-based solutions do not provide good uniqueness guarantees.

\n", + "upvotes": 8636, + "upvoterUsernames": [], + "downvotes": 3516, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91227", + "creator": "Dan Moulding", + "createdAt": 1266462412000, + "text": "

Given a branch foo and a remote upstream:

\n

As of Git 1.8.0:

\n
git branch -u upstream/foo\n
\n

Or, if local branch foo is not the current branch:

\n
git branch -u upstream/foo foo\n
\n

Or, if you like to type longer commands, these are equivalent to the above two:

\n
git branch --set-upstream-to=upstream/foo\n\ngit branch --set-upstream-to=upstream/foo foo\n
\n

As of Git 1.7.0 (before 1.8.0):

\n
git branch --set-upstream foo upstream/foo\n
\n

Notes:

\n\n
\n

See also: Why do I need to do `--set-upstream` all the time?

\n", + "upvotes": 5025, + "upvoterUsernames": [], + "downvotes": 376, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90074", + "creator": "Robin Day", + "createdAt": 1267108795000, + "text": "
UPDATE\n    Table_A\nSET\n    Table_A.col1 = Table_B.col1,\n    Table_A.col2 = Table_B.col2\nFROM\n    Some_Table AS Table_A\n    INNER JOIN Other_Table AS Table_B\n        ON Table_A.id = Table_B.id\nWHERE\n    Table_A.col3 = 'cool'\n
\n", + "upvotes": 10381, + "upvoterUsernames": [], + "downvotes": 4546, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90084", + "creator": "Marcin Gil", + "createdAt": 1267817641000, + "text": "

With git reflog check which commit is one prior the merge (git reflog will be a better option than git log). Then you can reset it using:

\n\n
git reset --hard commit_sha\n
\n\n

There's also another way:

\n\n
git reset --hard HEAD~1\n
\n\n

It will get you back 1 commit.

\n\n

Be aware that any modified and uncommitted/unstashed files will be reset to their unmodified state. To keep them either stash changes away or see --merge option below.

\n\n
\n\n

As @Velmont suggested below in his answer, in this direct case using:

\n\n
git reset --hard ORIG_HEAD\n
\n\n

might yield better results, as it should preserve your changes. ORIG_HEAD will point to a commit directly before merge has occurred, so you don't have to hunt for it yourself.

\n\n
\n\n

A further tip is to use the --merge switch instead of --hard since it doesn't reset files unnecessarily:

\n\n
git reset --merge ORIG_HEAD\n
\n\n
\n

--merge

\n \n

Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added).

\n
\n", + "upvotes": 10301, + "upvoterUsernames": [], + "downvotes": 5035, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911eb", + "creator": "Marcin Gil", + "createdAt": 1267817641000, + "text": "

With git reflog check which commit is one prior the merge (git reflog will be a better option than git log). Then you can reset it using:

\n\n
git reset --hard commit_sha\n
\n\n

There's also another way:

\n\n
git reset --hard HEAD~1\n
\n\n

It will get you back 1 commit.

\n\n

Be aware that any modified and uncommitted/unstashed files will be reset to their unmodified state. To keep them either stash changes away or see --merge option below.

\n\n
\n\n

As @Velmont suggested below in his answer, in this direct case using:

\n\n
git reset --hard ORIG_HEAD\n
\n\n

might yield better results, as it should preserve your changes. ORIG_HEAD will point to a commit directly before merge has occurred, so you don't have to hunt for it yourself.

\n\n
\n\n

A further tip is to use the --merge switch instead of --hard since it doesn't reset files unnecessarily:

\n\n
git reset --merge ORIG_HEAD\n
\n\n
\n

--merge

\n \n

Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added).

\n
\n", + "upvotes": 6380, + "upvoterUsernames": [], + "downvotes": 1114, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90052", + "creator": "hobbs", + "createdAt": 1268398550000, + "text": "

You can

\n
git remote set-url origin new.git.url/here\n
\n

(see git help remote) or you can edit .git/config and change the URLs there. You're not in any danger of losing history unless you do something very silly (and if you're worried, just make a copy of your repo, since your repo is your history.)

\n", + "upvotes": 15752, + "upvoterUsernames": [], + "downvotes": 6987, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9123d", + "creator": "rjh", + "createdAt": 1271700701000, + "text": "

It prevents JSON hijacking, a major JSON security issue that is formally fixed in all major browsers since 2011 with ECMAScript 5.

\n\n

Contrived example: say Google has a URL like mail.google.com/json?action=inbox which returns the first 50 messages of your inbox in JSON format. Evil websites on other domains can't make AJAX requests to get this data due to the same-origin policy, but they can include the URL via a <script> tag. The URL is visited with your cookies, and by overriding the global array constructor or accessor methods they can have a method called whenever an object (array or hash) attribute is set, allowing them to read the JSON content.

\n\n

The while(1); or &&&BLAH&&& prevents this: an AJAX request at mail.google.com will have full access to the text content, and can strip it away. But a <script> tag insertion blindly executes the JavaScript without any processing, resulting in either an infinite loop or a syntax error.

\n\n

This does not address the issue of cross-site request forgery.

\n", + "upvotes": 4627, + "upvoterUsernames": [], + "downvotes": 125, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91236", + "creator": "Amber", + "createdAt": 1276576306000, + "text": "

Interactive rebase off of a point earlier in the history than the commit you need to modify (git rebase -i <earliercommit>). In the list of commits being rebased, change the text from pick to edit next to the hash of the one you want to modify. Then when git prompts you to change the commit, use this:

\n\n
git commit --amend --author=\"Author Name <email@address.com>\" --no-edit\n
\n\n
\n\n

For example, if your commit history is A-B-C-D-E-F with F as HEAD, and you want to change the author of C and D, then you would...

\n\n
    \n
  1. Specify git rebase -i B (here is an example of what you will see after executing the git rebase -i B command)\n\n
      \n
    • if you need to edit A, use git rebase -i --root
    • \n
  2. \n
  3. Change the lines for both C and D from pick to edit
  4. \n
  5. Exit the editor (for vim, this would be pressing Esc and then typing :wq).
  6. \n
  7. Once the rebase started, it would first pause at C
  8. \n
  9. You would git commit --amend --author=\"Author Name <email@address.com>\"
  10. \n
  11. Then git rebase --continue
  12. \n
  13. It would pause again at D
  14. \n
  15. Then you would git commit --amend --author=\"Author Name <email@address.com>\" again
  16. \n
  17. git rebase --continue
  18. \n
  19. The rebase would complete.
  20. \n
  21. Use git push -f to update your origin with the updated commits.
  22. \n
\n", + "upvotes": 5132, + "upvoterUsernames": [], + "downvotes": 598, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90072", + "creator": "pycruft", + "createdAt": 1278622871000, + "text": "

os.listdir() returns everything inside a directory -- including both files and directories.

\n

os.path's isfile() can be used to only list files:

\n
from os import listdir\nfrom os.path import isfile, join\nonlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]\n
\n

Alternatively, os.walk() yields two lists for each directory it visits -- one for files and one for dirs. If you only want the top directory you can break the first time it yields:

\n
from os import walk\n\nf = []\nfor (dirpath, dirnames, filenames) in walk(mypath):\n    f.extend(filenames)\n    break\n
\n

or, shorter:

\n
from os import walk\n\nfilenames = next(walk(mypath), (None, None, []))[2]  # [] if no file\n
\n", + "upvotes": 9158, + "upvoterUsernames": [], + "downvotes": 3274, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006a", + "creator": "sberry", + "createdAt": 1279664973000, + "text": "

key is just a variable name.

\n\n
for key in d:\n
\n\n

will simply loop over the keys in the dictionary, rather than the keys and values. To loop over both key and value you can use the following:

\n\n

For Python 3.x:

\n\n
for key, value in d.items():\n
\n\n

For Python 2.x:

\n\n
for key, value in d.iteritems():\n
\n\n

To test for yourself, change the word key to poop.

\n\n

In Python 3.x, iteritems() was replaced with simply items(), which returns a set-like view backed by the dict, like iteritems() but even better. \nThis is also available in 2.7 as viewitems().

\n\n

The operation items() will work for both 2 and 3, but in 2 it will return a list of the dictionary's (key, value) pairs, which will not reflect changes to the dict that happen after the items() call. If you want the 2.x behavior in 3.x, you can call list(d.items()).

\n", + "upvotes": 7589, + "upvoterUsernames": [], + "downvotes": 1155, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90057", + "creator": "Michael Mrozek", + "createdAt": 1281322581000, + "text": "

Use the in operator:

\n
if "blah" not in somestring: \n    continue\n
\n", + "upvotes": 9771, + "upvoterUsernames": [], + "downvotes": 1882, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd1", + "creator": "Michael Mrozek", + "createdAt": 1281322581000, + "text": "

Use the in operator:

\n
if "blah" not in somestring: \n    continue\n
\n", + "upvotes": 13097, + "upvoterUsernames": [], + "downvotes": 5208, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90060", + "creator": "JaredPar", + "createdAt": 1288803016000, + "text": "

To obtain only the remote URL:

\n
git config --get remote.origin.url\n
\n

If you require full output, and you are on a network that can reach the remote repo where the origin resides:

\n
git remote show origin\n
\n

When using git clone (from GitHub, or any source repository for that matter) the default name for the source of the clone is "origin". Using git remote show will display the information about this remote name. The first few lines should show:

\n
C:\\Users\\jaredpar\\VsVim> git remote show origin\n* remote origin\n  Fetch URL: git@github.com:jaredpar/VsVim.git\n  Push  URL: git@github.com:jaredpar/VsVim.git\n  HEAD branch: master\n  Remote branches:\n
\n

If you want to use the value in the script, you would use the first command listed in this answer.

\n", + "upvotes": 8037, + "upvoterUsernames": [], + "downvotes": 1065, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9123c", + "creator": "codaddict", + "createdAt": 1289799696000, + "text": "
foo=\"Hello\"\nfoo=\"${foo} World\"\necho \"${foo}\"\n> Hello World\n
\n\n

In general to concatenate two variables you can just write them one after another:

\n\n
a='Hello'\nb='World'\nc=\"${a} ${b}\"\necho \"${c}\"\n> Hello World\n
\n", + "upvotes": 8441, + "upvoterUsernames": [], + "downvotes": 3935, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90059", + "creator": "codaddict", + "createdAt": 1291641358000, + "text": "

Now with PHP 8 you can do this using str_contains:

\n
if (str_contains('How are you', 'are')) { \n    echo 'true';\n}\n
\n

RFC

\n

Before PHP 8

\n

You can use the strpos() function which is used to find the occurrence of one string inside another one:

\n
$a = 'How are you?';\n\nif (strpos($a, 'are') !== false) {\n    echo 'true';\n}\n
\n

Note that the use of !== false is deliberate (neither != false nor === true will return the desired result); strpos() returns either the offset at which the needle string begins in the haystack string, or the boolean false if the needle isn't found. Since 0 is a valid offset and 0 is "falsey", we can't use simpler constructs like !strpos($a, 'are').

\n", + "upvotes": 10517, + "upvoterUsernames": [], + "downvotes": 2697, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd3", + "creator": "codaddict", + "createdAt": 1291641358000, + "text": "

Now with PHP 8 you can do this using str_contains:

\n
if (str_contains('How are you', 'are')) { \n    echo 'true';\n}\n
\n

RFC

\n

Before PHP 8

\n

You can use the strpos() function which is used to find the occurrence of one string inside another one:

\n
$a = 'How are you?';\n\nif (strpos($a, 'are') !== false) {\n    echo 'true';\n}\n
\n

Note that the use of !== false is deliberate (neither != false nor === true will return the desired result); strpos() returns either the offset at which the needle string begins in the haystack string, or the boolean false if the needle isn't found. Since 0 is a valid offset and 0 is "falsey", we can't use simpler constructs like !strpos($a, 'are').

\n", + "upvotes": 10524, + "upvoterUsernames": [], + "downvotes": 2704, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90054", + "creator": "Blowsie", + "createdAt": 1291973302000, + "text": "

UPDATE January, 2017:

\n

According to Can I use, the user-select is currently supported in all browsers except Internet Explorer 9 and its earlier versions (but sadly still needs a vendor prefix).

\n
\n

These are all of the available correct CSS variations:

\n

\r\n
\r\n
.noselect {\n  -webkit-touch-callout: none; /* iOS Safari */\n    -webkit-user-select: none; /* Safari */\n     -khtml-user-select: none; /* Konqueror HTML */\n       -moz-user-select: none; /* Old versions of Firefox */\n        -ms-user-select: none; /* Internet Explorer/Edge */\n            user-select: none; /* Non-prefixed version, currently\n                                  supported by Chrome, Edge, Opera and Firefox */\n}
\r\n
<p>\n  Selectable text.\n</p>\n<p class=\"noselect\">\n  Unselectable text.\n</p>
\r\n
\r\n
\r\n

\n
\n

Note that user-select is in standardization process (currently in a W3C working draft). It is not guaranteed to work everywhere and there might be differences in implementation among browsers. Also, browsers can drop support for it in the future.

\n
\n

More information can be found in Mozilla Developer Network documentation.

\n

The values of this attribute are none, text, toggle, element, elements, all and inherit.

\n", + "upvotes": 13791, + "upvoterUsernames": [], + "downvotes": 5477, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90053", + "creator": "Jorge E. Cardona", + "createdAt": 1293773771000, + "text": "
git clone -b <branch> <remote_repo>\n
\n\n

Example:

\n\n
git clone -b my-branch git@github.com:user/myproject.git\n
\n\n

With Git 1.7.10 and later, add --single-branch to prevent fetching of all branches. Example, with OpenCV 2.4 branch:

\n\n
git clone -b opencv-2.4 --single-branch https://github.com/Itseez/opencv.git\n
\n", + "upvotes": 12820, + "upvoterUsernames": [], + "downvotes": 4366, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9121c", + "creator": "Trevor", + "createdAt": 1299253230000, + "text": "

To push a single tag:

\n
git push origin <tag_name>\n
\n

And the following command should push all tags (not recommended):

\n
# not recommended\ngit push --tags\n
\n", + "upvotes": 7639, + "upvoterUsernames": [], + "downvotes": 2803, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91218", + "creator": "Chris Johnsen", + "createdAt": 1299298757000, + "text": "

You can do this fairly easily without git rebase or git merge --squash. In this example, we'll squash the last 3 commits.

\n\n

If you want to write the new commit message from scratch, this suffices:

\n\n
git reset --soft HEAD~3 &&\ngit commit\n
\n\n

If you want to start editing the new commit message with a concatenation of the existing commit messages (i.e. similar to what a pick/squash/squash/…/squash git rebase -i instruction list would start you with), then you need to extract those messages and pass them to git commit:

\n\n
git reset --soft HEAD~3 && \ngit commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"\n
\n\n

Both of those methods squash the last three commits into a single new commit in the same way. The soft reset just re-points HEAD to the last commit that you do not want to squash. Neither the index nor the working tree are touched by the soft reset, leaving the index in the desired state for your new commit (i.e. it already has all the changes from the commits that you are about to “throw away”).

\n", + "upvotes": 5021, + "upvoterUsernames": [], + "downvotes": 106, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005d", + "creator": "Adam Franco", + "createdAt": 1301442358000, + "text": "

You can push an 'empty' reference to the remote tag name:

\n
git push origin :tagname\n
\n

Or, more expressively, use the --delete option (or -d if your git version is older than 1.8.0):

\n
git push --delete origin tagname\n
\n

Note that git has tag namespace and branch namespace so you may use the same name for a branch and for a tag. If you want to make sure that you cannot accidentally remove the branch instead of the tag, you can specify full ref which will never delete a branch:

\n
git push origin :refs/tags/tagname\n
\n

If you also need to delete the local tag, use:

\n
git tag --delete tagname\n
\n
\n

Background

\n

Pushing a branch, tag, or other ref to a remote repository involves specifying "which repo, what source, what destination?"

\n
git push remote-repo source-ref:destination-ref\n
\n

A real world example where you push your master branch to the origin's master branch is:

\n
git push origin refs/heads/master:refs/heads/master\n
\n

Which because of default paths, can be shortened to:

\n
git push origin master:master\n
\n

Tags work the same way:

\n
git push origin refs/tags/release-1.0:refs/tags/release-1.0\n
\n

Which can also be shortened to:

\n
git push origin release-1.0:release-1.0\n
\n

By omitting the source ref (the part before the colon), you push 'nothing' to the destination, deleting the ref on the remote end.

\n", + "upvotes": 7593, + "upvoterUsernames": [], + "downvotes": 372, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd7", + "creator": "Adam Franco", + "createdAt": 1301442358000, + "text": "

You can push an 'empty' reference to the remote tag name:

\n
git push origin :tagname\n
\n

Or, more expressively, use the --delete option (or -d if your git version is older than 1.8.0):

\n
git push --delete origin tagname\n
\n

Note that git has tag namespace and branch namespace so you may use the same name for a branch and for a tag. If you want to make sure that you cannot accidentally remove the branch instead of the tag, you can specify full ref which will never delete a branch:

\n
git push origin :refs/tags/tagname\n
\n

If you also need to delete the local tag, use:

\n
git tag --delete tagname\n
\n
\n

Background

\n

Pushing a branch, tag, or other ref to a remote repository involves specifying "which repo, what source, what destination?"

\n
git push remote-repo source-ref:destination-ref\n
\n

A real world example where you push your master branch to the origin's master branch is:

\n
git push origin refs/heads/master:refs/heads/master\n
\n

Which because of default paths, can be shortened to:

\n
git push origin master:master\n
\n

Tags work the same way:

\n
git push origin refs/tags/release-1.0:refs/tags/release-1.0\n
\n

Which can also be shortened to:

\n
git push origin release-1.0:release-1.0\n
\n

By omitting the source ref (the part before the colon), you push 'nothing' to the destination, deleting the ref on the remote end.

\n", + "upvotes": 8219, + "upvoterUsernames": [], + "downvotes": 998, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9007c", + "creator": "jAndy", + "createdAt": 1301671064000, + "text": "

You can just check if the variable has a truthy value or not. That means

\n
if( value ) {\n}\n
\n

will evaluate to true if value is not:

\n\n

The above list represents all possible falsy values in ECMA-/Javascript. Find it in the specification at the ToBoolean section.

\n

Furthermore, if you do not know whether a variable exists (that means, if it was declared) you should check with the typeof operator. For instance

\n
if( typeof foo !== 'undefined' ) {\n    // foo could get resolved and it's defined\n}\n
\n

If you can be sure that a variable is declared at least, you should directly check if it has a truthy value like shown above.

\n", + "upvotes": 8502, + "upvoterUsernames": [], + "downvotes": 3087, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e3", + "creator": "jAndy", + "createdAt": 1301671064000, + "text": "

You can just check if the variable has a truthy value or not. That means

\n
if( value ) {\n}\n
\n

will evaluate to true if value is not:

\n\n

The above list represents all possible falsy values in ECMA-/Javascript. Find it in the specification at the ToBoolean section.

\n

Furthermore, if you do not know whether a variable exists (that means, if it was declared) you should check with the typeof operator. For instance

\n
if( typeof foo !== 'undefined' ) {\n    // foo could get resolved and it's defined\n}\n
\n

If you can be sure that a variable is declared at least, you should directly check if it has a truthy value like shown above.

\n", + "upvotes": 10052, + "upvoterUsernames": [], + "downvotes": 4637, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91249", + "creator": "Rob Hruska", + "createdAt": 1302200981000, + "text": "
String myString = "1234";\nint foo = Integer.parseInt(myString);\n
\n

If you look at the Java documentation you'll notice the "catch" is that this function can throw a NumberFormatException, which you can handle:

\n
int foo;\ntry {\n   foo = Integer.parseInt(myString);\n}\ncatch (NumberFormatException e) {\n   foo = 0;\n}\n
\n

(This treatment defaults a malformed number to 0, but you can do something else if you like.)

\n

Alternatively, you can use an Ints method from the Guava library, which in combination with Java 8's Optional, makes for a powerful and concise way to convert a string into an int:

\n
import com.google.common.primitives.Ints;\n\nint foo = Optional.ofNullable(myString)\n .map(Ints::tryParse)\n .orElse(0)\n
\n", + "upvotes": 5446, + "upvoterUsernames": [], + "downvotes": 1025, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90044", + "creator": "Tom Wadley", + "createdAt": 1303597430000, + "text": "

Find the index of the array element you want to remove using indexOf, and then remove that index with splice.

\n
\n

The splice() method changes the contents of an array by removing\nexisting elements and/or adding new elements.

\n
\n

\r\n
\r\n
const array = [2, 5, 9];\n\nconsole.log(array);\n\nconst index = array.indexOf(5);\nif (index > -1) { // only splice array when item is found\n  array.splice(index, 1); // 2nd parameter means remove one item only\n}\n\n// array = [2, 9]\nconsole.log(array); 
\r\n
\r\n
\r\n

\n

The second parameter of splice is the number of elements to remove. Note that splice modifies the array in place and returns a new array containing the elements that have been removed.

\n
\n

For the reason of completeness, here are functions. The first function removes only a single occurrence (i.e. removing the first match of 5 from [2,5,9,1,5,8,5]), while the second function removes all occurrences:

\n

\r\n
\r\n
function removeItemOnce(arr, value) {\n  var index = arr.indexOf(value);\n  if (index > -1) {\n    arr.splice(index, 1);\n  }\n  return arr;\n}\n\nfunction removeItemAll(arr, value) {\n  var i = 0;\n  while (i < arr.length) {\n    if (arr[i] === value) {\n      arr.splice(i, 1);\n    } else {\n      ++i;\n    }\n  }\n  return arr;\n}\n// Usage\nconsole.log(removeItemOnce([2,5,9,1,5,8,5], 5))\nconsole.log(removeItemAll([2,5,9,1,5,8,5], 5))
\r\n
\r\n
\r\n

\n

In TypeScript, these functions can stay type-safe with a type parameter:

\n
function removeItem<T>(arr: Array<T>, value: T): Array<T> { \n  const index = arr.indexOf(value);\n  if (index > -1) {\n    arr.splice(index, 1);\n  }\n  return arr;\n}\n
\n", + "upvotes": 19966, + "upvoterUsernames": [], + "downvotes": 4573, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90056", + "creator": "Daniel Ruoso", + "createdAt": 1307134229000, + "text": "

In Git 1.7.0 and later, you can checkout a new branch:

\n\n
git checkout -b <branch>\n
\n\n

Edit files, add and commit. Then push with the -u (short for --set-upstream) option:

\n\n
git push -u origin <branch>\n
\n\n

Git will set up the tracking information during the push.

\n", + "upvotes": 9868, + "upvoterUsernames": [], + "downvotes": 1972, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd0", + "creator": "Daniel Ruoso", + "createdAt": 1307134229000, + "text": "

In Git 1.7.0 and later, you can checkout a new branch:

\n\n
git checkout -b <branch>\n
\n\n

Edit files, add and commit. Then push with the -u (short for --set-upstream) option:

\n\n
git push -u origin <branch>\n
\n\n

Git will set up the tracking information during the push.

\n", + "upvotes": 12719, + "upvoterUsernames": [], + "downvotes": 4823, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91224", + "creator": "mechanical_meat", + "createdAt": 1308930994000, + "text": "

From Python Documentation:

\n\n
\n

An except clause may name multiple exceptions as a parenthesized tuple, for example

\n
\n\n
except (IDontLikeYouException, YouAreBeingMeanException) as e:\n    pass\n
\n\n

Or, for Python 2 only:

\n\n
except (IDontLikeYouException, YouAreBeingMeanException), e:\n    pass\n
\n\n

Separating the exception from the variable with a comma will still work in Python 2.6 and 2.7, but is now deprecated and does not work in Python 3; now you should be using as.

\n", + "upvotes": 6266, + "upvoterUsernames": [], + "downvotes": 1517, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90042", + "creator": "siride", + "createdAt": 1309922502000, + "text": "

To rename a branch while pointed to any branch:

\n
git branch -m <oldname> <newname>\n
\n

To rename the current branch:

\n
git branch -m <newname>\n
\n

-m is short for --move.

\n
\n

To push the local branch and reset the upstream branch:

\n
git push origin -u <newname>\n
\n

To delete the remote branch:

\n
git push origin --delete <oldname>\n
\n
\n

To create a git rename alias:

\n
git config --global alias.rename 'branch -m'\n
\n
\n

On Windows or another case-insensitive filesystem, use -M if there are only capitalization changes in the name. Otherwise, Git will throw a "branch already exists" error.

\n
git branch -M <newname>\n
\n", + "upvotes": 20320, + "upvoterUsernames": [], + "downvotes": 2994, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f32a4e082fcc3049e93077", + "creator": "siride", + "createdAt": 1309922502000, + "text": "

To rename a branch while pointed to any branch:

\n
git branch -m <oldname> <newname>\n
\n

To rename the current branch:

\n
git branch -m <newname>\n
\n

-m is short for --move.

\n
\n

To push the local branch and reset the upstream branch:

\n
git push origin -u <newname>\n
\n

To delete the remote branch:

\n
git push origin --delete <oldname>\n
\n
\n

To create a git rename alias:

\n
git config --global alias.rename 'branch -m'\n
\n
\n

On Windows or another case-insensitive filesystem, use -M if there are only capitalization changes in the name. Otherwise, Git will throw a "branch already exists" error.

\n
git branch -M <newname>\n
\n", + "upvotes": 22940, + "upvoterUsernames": [], + "downvotes": 5614, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9124d", + "creator": "Steve", + "createdAt": 1310550756000, + "text": "

Assuming you have a button with the id button, try this example:

\n
$("#button").click(function() {\n    $([document.documentElement, document.body]).animate({\n        scrollTop: $("#elementtoScrollToID").offset().top\n    }, 2000);\n});\n
\n

I got the code from the article Smoothly scroll to an element without a jQuery plugin. And I have tested it on the example below.

\n

\r\n
\r\n
<html>\n    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js\"></script>\n    <script>\n        $(document).ready(function (){\n            $(\"#click\").click(function (){\n                $('html, body').animate({\n                    scrollTop: $(\"#div1\").offset().top\n                }, 2000);\n            });\n        });\n    </script>\n    <div id=\"div1\" style=\"height: 1000px; width 100px\">\n        Test\n    </div>\n    <br/>\n    <div id=\"div2\" style=\"height: 1000px; width 100px\">\n        Test 2\n    </div>\n    <button id=\"click\">Click me</button>\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 8163, + "upvoterUsernames": [], + "downvotes": 3749, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9004a", + "creator": "Jon Skeet", + "createdAt": 1311755470000, + "text": "

It's a time zone change on December 31st in Shanghai.

\n\n

See this page for details of 1927 in Shanghai. Basically at midnight at the end of 1927, the clocks went back 5 minutes and 52 seconds. So \"1927-12-31 23:54:08\" actually happened twice, and it looks like Java is parsing it as the later possible instant for that local date/time - hence the difference.

\n\n

Just another episode in the often weird and wonderful world of time zones.

\n\n

EDIT: Stop press! History changes...

\n\n

The original question would no longer demonstrate quite the same behaviour, if rebuilt with version 2013a of TZDB. In 2013a, the result would be 358 seconds, with a transition time of 23:54:03 instead of 23:54:08.

\n\n

I only noticed this because I'm collecting questions like this in Noda Time, in the form of unit tests... The test has now been changed, but it just goes to show - not even historical data is safe.

\n\n

EDIT: History has changed again...

\n\n

In TZDB 2014f, the time of the change has moved to 1900-12-31, and it's now a mere 343 second change (so the time between t and t+1 is 344 seconds, if you see what I mean).

\n\n

EDIT: To answer a question around a transition at 1900... it looks like the Java timezone implementation treats all time zones as simply being in their standard time for any instant before the start of 1900 UTC:

\n\n
import java.util.TimeZone;\n\npublic class Test {\n    public static void main(String[] args) throws Exception {\n        long startOf1900Utc = -2208988800000L;\n        for (String id : TimeZone.getAvailableIDs()) {\n            TimeZone zone = TimeZone.getTimeZone(id);\n            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {\n                System.out.println(id);\n            }\n        }\n    }\n}\n
\n\n

The code above produces no output on my Windows machine. So any time zone which has any offset other than its standard one at the start of 1900 will count that as a transition. TZDB itself has some data going back earlier than that, and doesn't rely on any idea of a \"fixed\" standard time (which is what getRawOffset assumes to be a valid concept) so other libraries needn't introduce this artificial transition.

\n", + "upvotes": 18611, + "upvoterUsernames": [], + "downvotes": 6957, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90049", + "creator": "Ryan Lundy", + "createdAt": 1311891740000, + "text": "

Undoing a commit is a little scary if you don't know how it works. But it's actually amazingly easy if you do understand. I'll show you the 4 different ways you can undo a commit.

\n

Say you have this, where C is your HEAD and (F) is the state of your files.

\n
   (F)\nA-B-C\n    ↑\n  master\n
\n

Option 1: git reset --hard

\n

You want to destroy commit C and also throw away any uncommitted changes. You do this:

\n
git reset --hard HEAD~1\n
\n

The result is:

\n
 (F)\nA-B\n  ↑\nmaster\n
\n

Now B is the HEAD. Because you used --hard, your files are reset to their state at commit B.

\n

Option 2: git reset

\n

Maybe commit C wasn't a disaster, but just a bit off. You want to undo the commit but keep your changes for a bit of editing before you do a better commit. Starting again from here, with C as your HEAD:

\n
   (F)\nA-B-C\n    ↑\n  master\n
\n

Do this, leaving off the --hard:

\n
git reset HEAD~1\n
\n

In this case the result is:

\n
   (F)\nA-B-C\n  ↑\nmaster\n
\n

In both cases, HEAD is just a pointer to the latest commit. When you do a git reset HEAD~1, you tell Git to move the HEAD pointer back one commit. But (unless you use --hard) you leave your files as they were. So now git status shows the changes you had checked into C. You haven't lost a thing!

\n

Option 3: git reset --soft

\n

For the lightest touch, you can even undo your commit but leave your files and your index:

\n
git reset --soft HEAD~1\n
\n

This not only leaves your files alone, it even leaves your index alone. When you do git status, you'll see that the same files are in the index as before. In fact, right after this command, you could do git commit and you'd be redoing the same commit you just had.

\n

Option 4: you did git reset --hard and need to get that code back

\n

One more thing: Suppose you destroy a commit as in the first example, but then discover you needed it after all? Tough luck, right?

\n

Nope, there's still a way to get it back. Type this

\n
git reflog\n
\n

and you'll see a list of (partial) commit shas (that is, hashes) that you've moved around in. Find the commit you destroyed, and do this:

\n
git checkout -b someNewBranchName shaYouDestroyed\n
\n

You've now resurrected that commit. Commits don't actually get destroyed in Git for some 90 days, so you can usually go back and rescue one you didn't mean to get rid of.

\n", + "upvotes": 15277, + "upvoterUsernames": [], + "downvotes": 3258, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9121e", + "creator": "JPReddy", + "createdAt": 1312439081000, + "text": "

A clear explanation from Daniel Irvine [original link]:

\n
\n

There's a problem with 401 Unauthorized, the HTTP status code for authentication errors. And that’s just it: it’s for authentication, not authorization.\nReceiving a 401 response is the server telling you, “you aren’t\nauthenticated–either not authenticated at all or authenticated\nincorrectly–but please reauthenticate and try again.” To help you out,\nit will always include a WWW-Authenticate header that describes how\nto authenticate.

\n

This is a response generally returned by your web server, not your web\napplication.

\n

It’s also something very temporary; the server is asking you to try\nagain.

\n

So, for authorization I use the 403 Forbidden response. It’s\npermanent, it’s tied to my application logic, and it’s a more concrete\nresponse than a 401.

\n

Receiving a 403 response is the server telling you, “I’m sorry. I know\nwho you are–I believe who you say you are–but you just don’t have\npermission to access this resource. Maybe if you ask the system\nadministrator nicely, you’ll get permission. But please don’t bother\nme again until your predicament changes.”

\n

In summary, a 401 Unauthorized response should be used for missing\nor bad authentication, and a 403 Forbidden response should be used\nafterwards, when the user is authenticated but isn’t authorized to\nperform the requested operation on the given resource.

\n
\n

Another nice pictorial format of how http status codes should be used.

\n", + "upvotes": 9196, + "upvoterUsernames": [], + "downvotes": 4361, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9124b", + "creator": "RichieHindle", + "createdAt": 1312895259000, + "text": "\n\n
\n\n

Path objects from the Python 3.4+ pathlib module also expose these instance methods:

\n\n\n", + "upvotes": 5228, + "upvoterUsernames": [], + "downvotes": 812, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90080", + "creator": "Sean Patrick Floyd", + "createdAt": 1314177149000, + "text": "

You need to set your content-type to application/json. But -d (or --data) sends the Content-Type application/x-www-form-urlencoded, which is not accepted on Spring's side.

\n

Looking at the curl man page, I think you can use -H (or --header):

\n
-H "Content-Type: application/json"\n
\n

Full example:

\n
curl --header "Content-Type: application/json" \\\n  --request POST \\\n  --data '{"username":"xyz","password":"xyz"}' \\\n  http://localhost:3000/api/login\n
\n

(-H is short for --header, -d for --data)

\n

Note that -request POST is optional if you use -d, as the -d flag implies a POST request.

\n
\n

On Windows, things are slightly different. See the comment thread.

\n", + "upvotes": 9426, + "upvoterUsernames": [], + "downvotes": 4099, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e7", + "creator": "Sean Patrick Floyd", + "createdAt": 1314177149000, + "text": "

You need to set your content-type to application/json. But -d (or --data) sends the Content-Type application/x-www-form-urlencoded, which is not accepted on Spring's side.

\n

Looking at the curl man page, I think you can use -H (or --header):

\n
-H "Content-Type: application/json"\n
\n

Full example:

\n
curl --header "Content-Type: application/json" \\\n  --request POST \\\n  --data '{"username":"xyz","password":"xyz"}' \\\n  http://localhost:3000/api/login\n
\n

(-H is short for --header, -d for --data)

\n

Note that -request POST is optional if you use -d, as the -d flag implies a POST request.

\n
\n

On Windows, things are slightly different. See the comment thread.

\n", + "upvotes": 7283, + "upvoterUsernames": [], + "downvotes": 1956, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9006c", + "creator": "user123444555621", + "createdAt": 1314529011000, + "text": "

Pretty-printing is implemented natively in JSON.stringify(). The third argument enables pretty printing and sets the spacing to use:

\n\n
var str = JSON.stringify(obj, null, 2); // spacing level = 2\n
\n\n

If you need syntax highlighting, you might use some regex magic like so:

\n\n
function syntaxHighlight(json) {\n    if (typeof json != 'string') {\n         json = JSON.stringify(json, undefined, 2);\n    }\n    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n    return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n        var cls = 'number';\n        if (/^\"/.test(match)) {\n            if (/:$/.test(match)) {\n                cls = 'key';\n            } else {\n                cls = 'string';\n            }\n        } else if (/true|false/.test(match)) {\n            cls = 'boolean';\n        } else if (/null/.test(match)) {\n            cls = 'null';\n        }\n        return '<span class=\"' + cls + '\">' + match + '</span>';\n    });\n}\n
\n\n

See in action here: jsfiddle

\n\n

Or a full snippet provided below:

\n\n

\r\n
\r\n
function output(inp) {\r\n    document.body.appendChild(document.createElement('pre')).innerHTML = inp;\r\n}\r\n\r\nfunction syntaxHighlight(json) {\r\n    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\r\n    return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\r\n        var cls = 'number';\r\n        if (/^\"/.test(match)) {\r\n            if (/:$/.test(match)) {\r\n                cls = 'key';\r\n            } else {\r\n                cls = 'string';\r\n            }\r\n        } else if (/true|false/.test(match)) {\r\n            cls = 'boolean';\r\n        } else if (/null/.test(match)) {\r\n            cls = 'null';\r\n        }\r\n        return '<span class=\"' + cls + '\">' + match + '</span>';\r\n    });\r\n}\r\n\r\nvar obj = {a:1, 'b':'foo', c:[false,'false',null, 'null', {d:{e:1.3e5,f:'1.3e5'}}]};\r\nvar str = JSON.stringify(obj, undefined, 4);\r\n\r\noutput(str);\r\noutput(syntaxHighlight(str));
\r\n
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }\r\n.string { color: green; }\r\n.number { color: darkorange; }\r\n.boolean { color: blue; }\r\n.null { color: magenta; }\r\n.key { color: red; }
\r\n
\r\n
\r\n

\n", + "upvotes": 11301, + "upvoterUsernames": [], + "downvotes": 5009, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91202", + "creator": "Mark Longair", + "createdAt": 1314712886000, + "text": "

In your local clone of your forked repository, you can add the original GitHub repository as a "remote". ("Remotes" are like nicknames for the URLs of repositories - origin is one, for example.) Then you can fetch all the branches from that upstream repository, and rebase your work to continue working on the upstream version. In terms of commands that might look like:

\n
# Add the remote, call it "upstream":\n\ngit remote add upstream https://github.com/whoever/whatever.git\n\n# Fetch all the branches of that remote into remote-tracking branches\n\ngit fetch upstream\n\n# Make sure that you're on your master branch:\n\ngit checkout master\n\n# Rewrite your master branch so that any commits of yours that\n# aren't already in upstream/master are replayed on top of that\n# other branch:\n\ngit rebase upstream/master\n
\n

If you don't want to rewrite the history of your master branch, (for example because other people may have cloned it) then you should replace the last command with git merge upstream/master. However, for making further pull requests that are as clean as possible, it's probably better to rebase.

\n
\n

If you've rebased your branch onto upstream/master you may need to force the push in order to push it to your own forked repository on GitHub. You'd do that with:

\n
git push -f origin master\n
\n

You only need to use the -f the first time after you've rebased.

\n", + "upvotes": 9707, + "upvoterUsernames": [], + "downvotes": 4645, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90082", + "creator": "baisong", + "createdAt": 1317139631000, + "text": "

Maybe you just need to commit. I ran into this when I did:

\n
mkdir repo && cd repo\ngit remote add origin /path/to/origin.git\ngit add .\n
\n

Oops! Never committed!

\n
git push -u origin master\nerror: src refspec master does not match any.\n
\n

All I had to do was:

\n
git commit -m "initial commit"\ngit push origin main\n
\n

Success!

\n", + "upvotes": 7467, + "upvoterUsernames": [], + "downvotes": 2188, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e9", + "creator": "baisong", + "createdAt": 1317139631000, + "text": "

Maybe you just need to commit. I ran into this when I did:

\n
mkdir repo && cd repo\ngit remote add origin /path/to/origin.git\ngit add .\n
\n

Oops! Never committed!

\n
git push -u origin master\nerror: src refspec master does not match any.\n
\n

All I had to do was:

\n
git commit -m "initial commit"\ngit push origin main\n
\n

Success!

\n", + "upvotes": 7237, + "upvoterUsernames": [], + "downvotes": 1958, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90058", + "creator": "dash", + "createdAt": 1322690018000, + "text": "

It’s a holdover from the Netscape days:

\n
\n

Missing digits are treated as 0[...]. An incorrect digit is simply interpreted as 0. For example the values #F0F0F0, F0F0F0, F0F0F, #FxFxFx and FxFxFx are all the same.

\n
\n

It is from the blog post A little rant about Microsoft Internet Explorer's color parsing which covers it in great detail, including varying lengths of color values, etc.

\n

If we apply the rules in turn from the blog post, we get the following:

\n
    \n
  1. Replace all nonvalid hexadecimal characters with 0’s:

    \n
    chucknorris becomes c00c0000000\n
    \n
  2. \n
  3. Pad out to the next total number of characters divisible by 3 (11 → 12):

    \n
    c00c 0000 0000\n
    \n
  4. \n
  5. Split into three equal groups, with each component representing the corresponding colour component of an RGB colour:

    \n
    RGB (c00c, 0000, 0000)\n
    \n
  6. \n
  7. Truncate each of the arguments from the right down to two characters.

    \n
  8. \n
\n

Which, finally, gives the following result:

\n
RGB (c0, 00, 00) = #C00000 or RGB(192, 0, 0)\n
\n

Here’s an example demonstrating the bgcolor attribute in action, to produce this “amazing” colour swatch:

\n

\r\n
\r\n
<table>\n  <tr>\n    <td bgcolor=\"chucknorris\" cellpadding=\"8\" width=\"100\" align=\"center\">chuck norris</td>\n    <td bgcolor=\"mrt\"         cellpadding=\"8\" width=\"100\" align=\"center\" style=\"color:#ffffff\">Mr T</td>\n    <td bgcolor=\"ninjaturtle\" cellpadding=\"8\" width=\"100\" align=\"center\" style=\"color:#ffffff\">ninjaturtle</td>\n  </tr>\n  <tr>\n    <td bgcolor=\"sick\"  cellpadding=\"8\" width=\"100\" align=\"center\">sick</td>\n    <td bgcolor=\"crap\"  cellpadding=\"8\" width=\"100\" align=\"center\">crap</td>\n    <td bgcolor=\"grass\" cellpadding=\"8\" width=\"100\" align=\"center\">grass</td>\n  </tr>\n</table>
\r\n
\r\n
\r\n

\n

This also answers the other part of the question: Why does bgcolor="chucknorr" produce a yellow colour? Well, if we apply the rules, the string is:

\n
c00c00000 => c00 c00 000 => c0 c0 00 [RGB(192, 192, 0)]\n
\n

Which gives a light yellow gold colour. As the string starts off as 9 characters, we keep the second ‘C’ this time around, hence it ends up in the final colour value.

\n

I originally encountered this when someone pointed out that you could do color="crap" and, well, it comes out brown.

\n", + "upvotes": 15228, + "upvoterUsernames": [], + "downvotes": 7347, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd2", + "creator": "dash", + "createdAt": 1322690018000, + "text": "

It’s a holdover from the Netscape days:

\n
\n

Missing digits are treated as 0[...]. An incorrect digit is simply interpreted as 0. For example the values #F0F0F0, F0F0F0, F0F0F, #FxFxFx and FxFxFx are all the same.

\n
\n

It is from the blog post A little rant about Microsoft Internet Explorer's color parsing which covers it in great detail, including varying lengths of color values, etc.

\n

If we apply the rules in turn from the blog post, we get the following:

\n
    \n
  1. Replace all nonvalid hexadecimal characters with 0’s:

    \n
    chucknorris becomes c00c0000000\n
    \n
  2. \n
  3. Pad out to the next total number of characters divisible by 3 (11 → 12):

    \n
    c00c 0000 0000\n
    \n
  4. \n
  5. Split into three equal groups, with each component representing the corresponding colour component of an RGB colour:

    \n
    RGB (c00c, 0000, 0000)\n
    \n
  6. \n
  7. Truncate each of the arguments from the right down to two characters.

    \n
  8. \n
\n

Which, finally, gives the following result:

\n
RGB (c0, 00, 00) = #C00000 or RGB(192, 0, 0)\n
\n

Here’s an example demonstrating the bgcolor attribute in action, to produce this “amazing” colour swatch:

\n

\r\n
\r\n
<table>\n  <tr>\n    <td bgcolor=\"chucknorris\" cellpadding=\"8\" width=\"100\" align=\"center\">chuck norris</td>\n    <td bgcolor=\"mrt\"         cellpadding=\"8\" width=\"100\" align=\"center\" style=\"color:#ffffff\">Mr T</td>\n    <td bgcolor=\"ninjaturtle\" cellpadding=\"8\" width=\"100\" align=\"center\" style=\"color:#ffffff\">ninjaturtle</td>\n  </tr>\n  <tr>\n    <td bgcolor=\"sick\"  cellpadding=\"8\" width=\"100\" align=\"center\">sick</td>\n    <td bgcolor=\"crap\"  cellpadding=\"8\" width=\"100\" align=\"center\">crap</td>\n    <td bgcolor=\"grass\" cellpadding=\"8\" width=\"100\" align=\"center\">grass</td>\n  </tr>\n</table>
\r\n
\r\n
\r\n

\n

This also answers the other part of the question: Why does bgcolor="chucknorr" produce a yellow colour? Well, if we apply the rules, the string is:

\n
c00c00000 => c00 c00 000 => c0 c0 00 [RGB(192, 192, 0)]\n
\n

Which gives a light yellow gold colour. As the string starts off as 9 characters, we keep the second ‘C’ this time around, hence it ends up in the final colour value.

\n

I originally encountered this when someone pointed out that you could do color="crap" and, well, it comes out brown.

\n", + "upvotes": 15359, + "upvoterUsernames": [], + "downvotes": 7478, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9122c", + "creator": "Jon Skeet", + "createdAt": 1326723976000, + "text": "

Strings are immutable. That means once you've created the String, if another process can dump memory, there's no way (aside from reflection) you can get rid of the data before garbage collection kicks in.

\n\n

With an array, you can explicitly wipe the data after you're done with it. You can overwrite the array with anything you like, and the password won't be present anywhere in the system, even before garbage collection.

\n\n

So yes, this is a security concern - but even using char[] only reduces the window of opportunity for an attacker, and it's only for this specific type of attack.

\n\n

As noted in the comments, it's possible that arrays being moved by the garbage collector will leave stray copies of the data in memory. I believe this is implementation-specific - the garbage collector may clear all memory as it goes, to avoid this sort of thing. Even if it does, there's still the time during which the char[] contains the actual characters as an attack window.

\n", + "upvotes": 7621, + "upvoterUsernames": [], + "downvotes": 2985, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90048", + "creator": "RNA", + "createdAt": 1326758578000, + "text": "
\n

⚠ Warning:

\n

Any uncommitted local changes to tracked files will be lost.

\n

Any local files that are not tracked by Git will not be affected.

\n
\n
\n

First, update all origin/<branch> refs to latest:

\n
git fetch --all\n
\n

Backup your current branch (e.g. master):

\n
git branch backup-master\n
\n

Jump to the latest commit on origin/master and checkout those files:

\n
git reset --hard origin/master\n
\n

Explanation:

\n

git fetch downloads the latest from remote without trying to merge or rebase anything.

\n

git reset resets the master branch to what you just fetched. The --hard option changes all the files in your working tree to match the files in origin/master.

\n
\n

Maintain current local commits

\n

[*]: It's worth noting that it is possible to maintain current local commits by creating a branch from master before resetting:

\n
git checkout master\ngit branch new-branch-to-save-current-commits\ngit fetch --all\ngit reset --hard origin/master\n
\n

After this, all of the old commits will be kept in new-branch-to-save-current-commits.

\n

Uncommitted changes

\n

Uncommitted changes, however (even staged), will be lost. Make sure to stash and commit anything you need. For that you can run the following:

\n
git stash\n
\n

And then to reapply these uncommitted changes:

\n
git stash pop\n
\n", + "upvotes": 16067, + "upvoterUsernames": [], + "downvotes": 3798, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91232", + "creator": "quickshiftin", + "createdAt": 1329185101000, + "text": "

The keys need to be read-writable only by you:

\n
chmod 600 ~/.ssh/id_rsa\n
\n

Alternatively, the keys can be only readable by you (this also blocks your write access):

\n
chmod 400 ~/.ssh/id_rsa\n
\n

600 appears to be better in most cases, because you don't need to change file permissions later to edit it. (See the comments for more nuances)

\n

The relevant portion from the manpage (man ssh)

\n
\n
 ~/.ssh/id_rsa\n         Contains the private key for authentication.  These files contain sensitive \n         data and should be readable by the user but not\n         accessible by others (read/write/execute).  ssh will simply ignore a private \n         key file if it is              \n         accessible by others.  It is possible to specify a\n         passphrase when generating the key which will be used to encrypt the sensitive \n         part of this file using 3DES.\n\n ~/.ssh/identity.pub\n ~/.ssh/id_dsa.pub\n ~/.ssh/id_ecdsa.pub\n ~/.ssh/id_rsa.pub\n         Contains the public key for authentication.  These files are not sensitive and \n         can (but need not) be readable by anyone.\n
\n
\n", + "upvotes": 6337, + "upvoterUsernames": [], + "downvotes": 1738, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9123a", + "creator": "Daniel Fischer", + "createdAt": 1340805285000, + "text": "

Branch prediction.

\n

With a sorted array, the condition data[c] >= 128 is first false for a streak of values, then becomes true for all later values. That's easy to predict. With an unsorted array, you pay for the branching cost.

\n", + "upvotes": 9015, + "upvoterUsernames": [], + "downvotes": 4494, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90076", + "creator": "Gryphius", + "createdAt": 1341292899000, + "text": "
scp -r user@your.server.example.com:/path/to/foo /home/user/Desktop/\n
\n

By not including the trailing '/' at the end of foo, you will copy the directory itself (including contents), rather than only the contents of the directory.

\n

From man scp (See online manual)

\n
\n

-r Recursively copy entire directories

\n
\n", + "upvotes": 10779, + "upvoterUsernames": [], + "downvotes": 5075, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90073", + "creator": "dirvine", + "createdAt": 1344257176000, + "text": "

Hit the Esc key to enter "Normal mode". Then you can type : to enter "Command-line mode". A colon (:) will appear at the bottom of the screen and you can type in one of the following commands. To execute a command, press the Enter key.

\n\n

You can also exit Vim directly from "Normal mode" by typing ZZ to save and quit (same as :x) or ZQ to just quit (same as :q!). (Note that case is important here. ZZ and zz do not mean the same thing.)

\n

Vim has extensive help - that you can access with the :help command - where you can find answers to all your questions and a tutorial for beginners.

\n", + "upvotes": 6102, + "upvoterUsernames": [], + "downvotes": 224, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9121a", + "creator": "Brian Ustas", + "createdAt": 1344273631000, + "text": "

Use Math.round() :

\n
Math.round(num * 100) / 100\n
\n

Or to be more specific and to ensure things like 1.005 round correctly, use Number.EPSILON :

\n
Math.round((num + Number.EPSILON) * 100) / 100\n
\n", + "upvotes": 9231, + "upvoterUsernames": [], + "downvotes": 4358, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911fe", + "creator": "Jistanidiot", + "createdAt": 1346070826000, + "text": "

To display only the name of the current branch you're on:

\n
git rev-parse --abbrev-ref HEAD\n
\n

Reference: Show just the current branch in Git

\n", + "upvotes": 9379, + "upvoterUsernames": [], + "downvotes": 4281, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9122e", + "creator": "waitingkuo", + "createdAt": 1368169678000, + "text": "

DataFrame.iterrows is a generator which yields both the index and row (as a Series):

\n
import pandas as pd\n\ndf = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})\ndf = df.reset_index()  # make sure indexes pair with number of rows\n\nfor index, row in df.iterrows():\n    print(row['c1'], row['c2'])\n
\n\n
10 100\n11 110\n12 120\n
\n", + "upvotes": 8821, + "upvoterUsernames": [], + "downvotes": 4192, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90047", + "creator": "rakib_", + "createdAt": 1370506895000, + "text": "

Do the following:

\n
grep -rnw '/path/to/somewhere/' -e 'pattern'\n
\n\n

Along with these, --exclude, --include, --exclude-dir flags could be used for efficient searching:

\n\n
grep --include=\\*.{c,h} -rnw '/path/to/somewhere/' -e "pattern"\n
\n\n
grep --exclude=\\*.o -rnw '/path/to/somewhere/' -e "pattern"\n
\n\n
grep --exclude-dir={dir1,dir2,*.dst} -rnw '/path/to/somewhere/' -e "pattern"\n
\n

This works very well for me, to achieve almost the same purpose like yours.

\n

For more options, see man grep.

\n", + "upvotes": 24554, + "upvoterUsernames": [], + "downvotes": 12116, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91228", + "creator": "bansi", + "createdAt": 1373935681000, + "text": "

Try:

\n
mysql -u username -p database_name < file.sql\n
\n

Check MySQL Options.

\n

Note 1: It is better to use the full path of the SQL file file.sql.

\n

Note 2: Use -R and --triggers to keep the routines and triggers of original database. They are not copied by default.

\n

Note 3 You may have to create the (empty) database from MySQL if it doesn't exist already and the exported SQL don't contain CREATE DATABASE (exported with --no-create-db or -n option), before you can import it.

\n", + "upvotes": 7168, + "upvoterUsernames": [], + "downvotes": 2521, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e90081", + "creator": "jgillich", + "createdAt": 1394612887000, + "text": "

See the NPM docs and semver docs:

\n\n

See Comments below for exceptions, in particular for pre-one versions, such as ^0.2.3

\n", + "upvotes": 9947, + "upvoterUsernames": [], + "downvotes": 4621, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e911e8", + "creator": "jgillich", + "createdAt": 1394612887000, + "text": "

See the NPM docs and semver docs:

\n\n

See Comments below for exceptions, in particular for pre-one versions, such as ^0.2.3

\n", + "upvotes": 10649, + "upvoterUsernames": [], + "downvotes": 5323, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91206", + "creator": "Brandon Clapp", + "createdAt": 1430412215000, + "text": "

The code formatting is available in Visual Studio Code through the following shortcuts:

\n\n

Alternatively, you can find the shortcut, as well as other shortcuts, through the 'Command Palette' provided in the editor with Ctrl +Shift+ P (or Command + Shift + P on Mac), and then searching for format document.

\n

For unsaved snippets

\n
    \n
  1. Open command palette (Win: F1 or Ctrl+Shift+P)

    \n
  2. \n
  3. Find "Change Language Mode"

    \n
  4. \n
  5. Select language e.g. json. By now syntax should be highlighted.

    \n
  6. \n
  7. Format document (e.g. Open Command Palette -> "Format Document")

    \n
  8. \n
\n

Unformat

\n
    \n
  1. Select text
  2. \n
  3. Command Palette -> Join Lines
  4. \n
\n

'Show the pics'

\n

\"enter\n\"enter

\n", + "upvotes": 6998, + "upvoterUsernames": [], + "downvotes": 1952, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f321bc082fcc3049e9005f", + "creator": "Adam Zerner", + "createdAt": 1440034372000, + "text": "

ECMA 5+:

\n
// because Object.keys(new Date()).length === 0;\n// we have to do some additional check\nobj // 👈 null and undefined check\n&& Object.keys(obj).length === 0\n&& Object.getPrototypeOf(obj) === Object.prototype\n
\n

Note, though, that this creates an unnecessary array (the return value of keys).

\n

Pre-ECMA 5:

\n
function isEmpty(obj) {\n  for(var prop in obj) {\n    if(Object.prototype.hasOwnProperty.call(obj, prop)) {\n      return false;\n    }\n  }\n\n  return JSON.stringify(obj) === JSON.stringify({});\n}\n
\n

jQuery:

\n
jQuery.isEmptyObject({}); // true\n
\n

lodash:

\n
_.isEmpty({}); // true\n
\n

Underscore:

\n
_.isEmpty({}); // true\n
\n

Hoek

\n
Hoek.deepEqual({}, {}); // true\n
\n

ExtJS

\n
Ext.Object.isEmpty({}); // true\n
\n

AngularJS (version 1)

\n
angular.equals({}, {}); // true\n
\n

Ramda

\n
R.isEmpty({}); // true\n
\n", + "upvotes": 7799, + "upvoterUsernames": [], + "downvotes": 656, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f329bb082fcc3049e92dd9", + "creator": "Adam Zerner", + "createdAt": 1440034372000, + "text": "

ECMA 5+:

\n
// because Object.keys(new Date()).length === 0;\n// we have to do some additional check\nobj // 👈 null and undefined check\n&& Object.keys(obj).length === 0\n&& Object.getPrototypeOf(obj) === Object.prototype\n
\n

Note, though, that this creates an unnecessary array (the return value of keys).

\n

Pre-ECMA 5:

\n
function isEmpty(obj) {\n  for(var prop in obj) {\n    if(Object.prototype.hasOwnProperty.call(obj, prop)) {\n      return false;\n    }\n  }\n\n  return JSON.stringify(obj) === JSON.stringify({});\n}\n
\n

jQuery:

\n
jQuery.isEmptyObject({}); // true\n
\n

lodash:

\n
_.isEmpty({}); // true\n
\n

Underscore:

\n
_.isEmpty({}); // true\n
\n

Hoek

\n
Hoek.deepEqual({}, {}); // true\n
\n

ExtJS

\n
Ext.Object.isEmpty({}); // true\n
\n

AngularJS (version 1)

\n
angular.equals({}, {}); // true\n
\n

Ramda

\n
R.isEmpty({}); // true\n
\n", + "upvotes": 8953, + "upvoterUsernames": [], + "downvotes": 1810, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e9122d", + "creator": "Bergi", + "createdAt": 1464807729000, + "text": "

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

\n\n

Reading in sequence

\n\n

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

\n\n
async function printFiles () {\n  const files = await getFilePaths();\n\n  for (const file of files) {\n    const contents = await fs.readFile(file, 'utf8');\n    console.log(contents);\n  }\n}\n
\n\n

Reading in parallel

\n\n

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

\n\n
async function printFiles () {\n  const files = await getFilePaths();\n\n  await Promise.all(files.map(async (file) => {\n    const contents = await fs.readFile(file, 'utf8')\n    console.log(contents)\n  }));\n}\n
\n", + "upvotes": 5614, + "upvoterUsernames": [], + "downvotes": 978, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + }, + { + "_id": "62f322a3082fcc3049e91230", + "creator": "dustbuster", + "createdAt": 1537980198000, + "text": "

The problem is that Xcode Command-line Tools needs to be updated.

\n

Solution #1

\n

Go back to your terminal and enter:

\n
xcode-select --install\n
\n

You'll then receive the following output:

\n
xcode-select: note: install requested for command line developer tools\n
\n

You will then be prompted in a window to update Xcode Command Line tools. (which may take a while)

\n

Open a new terminal window and your development tools should be returned.

\n

Addition: With any major or semi-major update you'll need to update the command line tools in order to get them functioning properly again. Check Xcode with any update. This goes beyond Mojave...

\n

After that restart your terminal

\n

Alternatively, IF that fails, and it very well might.... you'll get a pop-up box saying "Software not found on server", see below!

\n

Solution #2

\n

and you hit xcode-select --install and it doesn't find the software, log into Apple Developer, and install it via webpage.

\n

Log in or sign up here:

\n

https://developer.apple.com/download/more/

\n

Look for: "Command Line Tools for Xcode 12.x" in the list of downloads\nThen click the dmg and download.

\n

\"image

\n", + "upvotes": 7261, + "upvoterUsernames": [], + "downvotes": 2659, + "downvoterUsernames": [], + "commentItems": [], + "accepted": false + } + ], + "comments": 250, + "commentItems": [ + { + "_id": "62f32a68082fcc3049e930f1", + "creator": "Sec", + "createdAt": 1222793866000, + "text": "Please do not store zipcodes as numbers. Some countries have letters in their zipcode.", + "upvotes": 423, + "upvoterUsernames": [], + "downvotes": 151, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918bc", + "creator": "Calum", + "createdAt": 1222872107000, + "text": "Yep. And in the (most common) case where you just want a list, the new ArrayList call is unecessary as well.", + "upvotes": 554, + "upvoterUsernames": [], + "downvotes": 158, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e94", + "creator": "Calum", + "createdAt": 1222872107000, + "text": "Yep. And in the (most common) case where you just want a list, the new ArrayList call is unecessary as well.", + "upvotes": 630, + "upvoterUsernames": [], + "downvotes": 234, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9311d", + "creator": "John Gardner", + "createdAt": 1223418375000, + "text": "actually, its "new-fashioned" because you used nanoTime, which wasn't added until java5", + "upvotes": 343, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93192", + "creator": "John Gardner", + "createdAt": 1223418375000, + "text": "actually, its "new-fashioned" because you used nanoTime, which wasn't added until java5", + "upvotes": 420, + "upvoterUsernames": [], + "downvotes": 158, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931a9", + "creator": "ICR", + "createdAt": 1223814246000, + "text": "In case people didn't know, you can do: Math.Round(x, MidpointRounding.AwayFromZero); To change the rounding scheme.", + "upvotes": 343, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ffa", + "creator": "MSalters", + "createdAt": 1224496629000, + "text": "That problem doesn't stop it from generating a copy ctor, where it's quite harmful.", + "upvotes": 537, + "upvoterUsernames": [], + "downvotes": 208, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e4", + "creator": "MSalters", + "createdAt": 1224496629000, + "text": "That problem doesn't stop it from generating a copy ctor, where it's quite harmful.", + "upvotes": 545, + "upvoterUsernames": [], + "downvotes": 216, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9187d", + "creator": "Claudiu", + "createdAt": 1224778928000, + "text": "I'd recommend putting the dict outside of the function if performance is an issue, so it doesn't re-build the dict on every function call", + "upvotes": 889, + "upvoterUsernames": [], + "downvotes": 442, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931f9", + "creator": "Claudiu", + "createdAt": 1224778928000, + "text": "I'd recommend putting the dict outside of the function if performance is an issue, so it doesn't re-build the dict on every function call", + "upvotes": 501, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff8", + "creator": "John Kraft", + "createdAt": 1225143856000, + "text": "Has anyone ever noticed that the statement, "Only a Sith deals in absolutes," is an absolute? Sorry. Couldn't help it.", + "upvotes": 625, + "upvoterUsernames": [], + "downvotes": 296, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e3", + "creator": "John Kraft", + "createdAt": 1225143856000, + "text": "Has anyone ever noticed that the statement, "Only a Sith deals in absolutes," is an absolute? Sorry. Couldn't help it.", + "upvotes": 635, + "upvoterUsernames": [], + "downvotes": 306, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930dd", + "creator": "lurks", + "createdAt": 1225329522000, + "text": "truly amazing . congratulations !", + "upvotes": 284, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93033", + "creator": "TheSoftwareJedi", + "createdAt": 1226541825000, + "text": "or, if you want for each table: SELECT table_name, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{your_db}';", + "upvotes": 347, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e99", + "creator": "Kip", + "createdAt": 1227039358000, + "text": "Well all primes are odd, except 2. Just sayin.", + "upvotes": 735, + "upvoterUsernames": [], + "downvotes": 345, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed9", + "creator": "John MacIntyre", + "createdAt": 1229706402000, + "text": "I wonder if it isn't more like *((5 * sizeof(a)) + a). Great explaination though.", + "upvotes": 492, + "upvoterUsernames": [], + "downvotes": 138, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93088", + "creator": "Daniel Earwicker", + "createdAt": 1230031817000, + "text": "Another cool thing specific to out is that the function has to assign to the out parameter. It's not allowed to leave it unassigned.", + "upvotes": 339, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930af", + "creator": "Jordan Parmer", + "createdAt": 1230128058000, + "text": "An abstract function has to be overridden while a virtual function may be overridden.", + "upvotes": 290, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d7", + "creator": "theman_on_vista", + "createdAt": 1230312720000, + "text": "who does this guy think he is ? the creator of mono???!! ... o wait..", + "upvotes": 284, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e9c", + "creator": "rampion", + "createdAt": 1231472825000, + "text": "or you can just use :retab", + "upvotes": 405, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9188f", + "creator": "Chris S", + "createdAt": 1232973940000, + "text": "And also generic collections are a lot faster as there's no boxing/unboxing", + "upvotes": 564, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930b8", + "creator": "ChrisW", + "createdAt": 1233942759000, + "text": "System.Drawing.Rectangle violates all three of these rules.", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 266, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93035", + "creator": "rfunduk", + "createdAt": 1235153451000, + "text": "Wow even comics about .NET suck.", + "upvotes": 421, + "upvoterUsernames": [], + "downvotes": 117, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eef", + "creator": "unbeknown", + "createdAt": 1236626073000, + "text": "pop() returns the element you want to remove. del just deletes is.", + "upvotes": 474, + "upvoterUsernames": [], + "downvotes": 131, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930bc", + "creator": "Frank Schwieterman", + "createdAt": 1236708630000, + "text": "I am going to get myself into so much trouble with this.", + "upvotes": 442, + "upvoterUsernames": [], + "downvotes": 159, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9312b", + "creator": "Jiminy", + "createdAt": 1237163002000, + "text": "That is ridiculous, who writes comments when they're drunk.", + "upvotes": 500, + "upvoterUsernames": [], + "downvotes": 242, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93199", + "creator": "Jiminy", + "createdAt": 1237163002000, + "text": "That is ridiculous, who writes comments when they're drunk.", + "upvotes": 351, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92fee", + "creator": "Bill K", + "createdAt": 1241040388000, + "text": "I think that we may have a syntactical mistake here, shouldn't that be "from __past__ import braces"?", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 320, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931de", + "creator": "Bill K", + "createdAt": 1241040388000, + "text": "I think that we may have a syntactical mistake here, shouldn't that be "from __past__ import braces"?", + "upvotes": 620, + "upvoterUsernames": [], + "downvotes": 285, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930f5", + "creator": "staticsan", + "createdAt": 1241058178000, + "text": "Be warned: some browsers will insert newlines at the continuance, some will not.", + "upvotes": 515, + "upvoterUsernames": [], + "downvotes": 244, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92edf", + "creator": "TheTXI", + "createdAt": 1242218475000, + "text": "Congratulations on being pretty much the only person here who actually shows that it is possible (although not recommended).", + "upvotes": 556, + "upvoterUsernames": [], + "downvotes": 206, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91890", + "creator": "Christoffer", + "createdAt": 1243597700000, + "text": "Find has a builtin "-delete" action, so you could do just find . -name \\*.pyc -delete", + "upvotes": 826, + "upvoterUsernames": [], + "downvotes": 395, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9303c", + "creator": "Joel Mueller", + "createdAt": 1244057974000, + "text": "...and also definitely not the same functionality.", + "upvotes": 569, + "upvoterUsernames": [], + "downvotes": 268, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e9e", + "creator": "Nosredna", + "createdAt": 1244746417000, + "text": "Why will the dog be released to the public?", + "upvotes": 701, + "upvoterUsernames": [], + "downvotes": 311, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ea1", + "creator": "GalacticCowboy", + "createdAt": 1245440290000, + "text": "Use a picture of Jesus. Because everybody knows that Jesus saves...", + "upvotes": 577, + "upvoterUsernames": [], + "downvotes": 189, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ea8", + "creator": "Iker Jimenez", + "createdAt": 1246468442000, + "text": "This will not only delete the contents but the folder itself as well. I don't think it is what the question asks.", + "upvotes": 434, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9301f", + "creator": "Ates Goral", + "createdAt": 1247069542000, + "text": "I'm convinced that there are use cases for having properties intentionally set to undefined.", + "upvotes": 538, + "upvoterUsernames": [], + "downvotes": 222, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046f", + "creator": "Stefano Borini", + "createdAt": 1247687931000, + "text": "if this is true, you just won a prize for "best debug of the year" :)", + "upvotes": 1015, + "upvoterUsernames": [], + "downvotes": 468, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb0", + "creator": "nickf", + "createdAt": 1249149567000, + "text": "too global? how many different "please wait" messages do you want?", + "upvotes": 619, + "upvoterUsernames": [], + "downvotes": 243, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb7", + "creator": "jkeys", + "createdAt": 1249948212000, + "text": "Don't be afraid to use a goto if that is the best option.", + "upvotes": 577, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b7", + "creator": "decasteljau", + "createdAt": 1250253427000, + "text": "Be careful with ToList(), because ToList() creates a copy of the sequence, which could cause performance and memory issues.", + "upvotes": 356, + "upvoterUsernames": [], + "downvotes": 102, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91892", + "creator": "nilamo", + "createdAt": 1251096674000, + "text": "Am I the only one that thinks 'RecursiveIteratorIterator' is a silly name?", + "upvotes": 473, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918b0", + "creator": "David Andres", + "createdAt": 1252730040000, + "text": "Should be noted that this more easily done with $("#target")[0].selectedIndex = 0;", + "upvotes": 552, + "upvoterUsernames": [], + "downvotes": 151, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90456", + "creator": "Khaled Alshaya", + "createdAt": 1253503177000, + "text": "@Pax namespace io = boost::filesystem;", + "upvotes": 1671, + "upvoterUsernames": [], + "downvotes": 781, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930e2", + "creator": "Jon Kruger", + "createdAt": 1253559634000, + "text": ""use case which doesn't add much value"? That's a subjective opinion, it could provide me a lot of value!", + "upvotes": 444, + "upvoterUsernames": [], + "downvotes": 169, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff2", + "creator": "erikkallen", + "createdAt": 1253560446000, + "text": "Would any sensible person create a library with types whose unqualified name collide with the std types?", + "upvotes": 409, + "upvoterUsernames": [], + "downvotes": 75, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931df", + "creator": "erikkallen", + "createdAt": 1253560446000, + "text": "Would any sensible person create a library with types whose unqualified name collide with the std types?", + "upvotes": 525, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93001", + "creator": "Gumbo", + "createdAt": 1253771452000, + "text": "Greetings from Bobby Tables.", + "upvotes": 350, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e8", + "creator": "Gumbo", + "createdAt": 1253771452000, + "text": "Greetings from Bobby Tables.", + "upvotes": 349, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9187c", + "creator": "Hendrik", + "createdAt": 1254915580000, + "text": "var currentTime = new Date();", + "upvotes": 659, + "upvoterUsernames": [], + "downvotes": 212, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931f8", + "creator": "Hendrik", + "createdAt": 1254915580000, + "text": "var currentTime = new Date();", + "upvotes": 822, + "upvoterUsernames": [], + "downvotes": 375, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c5", + "creator": "Cascabel", + "createdAt": 1255014449000, + "text": "The *alloc variants are pretty mnemonic - clear-alloc, memory-alloc, re-alloc.", + "upvotes": 485, + "upvoterUsernames": [], + "downvotes": 234, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93000", + "creator": "Peter", + "createdAt": 1255072166000, + "text": "jorney's array.inject(:+) is more efficient.", + "upvotes": 547, + "upvoterUsernames": [], + "downvotes": 221, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e7", + "creator": "Peter", + "createdAt": 1255072166000, + "text": "jorney's array.inject(:+) is more efficient.", + "upvotes": 357, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ecf", + "creator": "Gumbo", + "createdAt": 1255856099000, + "text": "Note that this algorithm is O(n^2).", + "upvotes": 633, + "upvoterUsernames": [], + "downvotes": 276, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930a2", + "creator": "MAK", + "createdAt": 1255976941000, + "text": "I would advise caution in using queue.shift. IIRC it is not O(1), but O(n) and might be too slow if the queue gets large.", + "upvotes": 528, + "upvoterUsernames": [], + "downvotes": 241, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91885", + "creator": "bdonlan", + "createdAt": 1256268337000, + "text": "Or git reset --hard origin/master, to reset it to whatever the origin was at.", + "upvotes": 874, + "upvoterUsernames": [], + "downvotes": 433, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931cf", + "creator": "intrepion", + "createdAt": 1256281514000, + "text": "That's a negative on negatives as well", + "upvotes": 417, + "upvoterUsernames": [], + "downvotes": 168, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930ed", + "creator": "Roman Nikitchenko", + "createdAt": 1257839257000, + "text": "... but this is regarding mutex vs counting semaphore. The question was asked about binary.", + "upvotes": 463, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930a6", + "creator": "Andy Ross", + "createdAt": 1257961328000, + "text": "Or, to disable buffering entirely: setbuf(stdout, NULL);", + "upvotes": 355, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9301d", + "creator": "JohannesH", + "createdAt": 1258540667000, + "text": "No, not 100%... Just 99,999999999999999999999999999999999999999999999999999999999‌999999999999999999% ;)", + "upvotes": 397, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93124", + "creator": "vsync", + "createdAt": 1259054579000, + "text": "I just write: if( $(selector).length ){ ... } without the '> 0'", + "upvotes": 430, + "upvoterUsernames": [], + "downvotes": 170, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93197", + "creator": "vsync", + "createdAt": 1259054579000, + "text": "I just write: if( $(selector).length ){ ... } without the '> 0'", + "upvotes": 408, + "upvoterUsernames": [], + "downvotes": 148, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91886", + "creator": "EightyOne Unite", + "createdAt": 1259336175000, + "text": "Holy code monkeys,..that's one in-depth answer. What's great is that you probably wrote in in vim in about 10 keystrokes.", + "upvotes": 494, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d0", + "creator": "Mongus Pong", + "createdAt": 1260365433000, + "text": "I would say the first one. Easier to see whats going on!", + "upvotes": 293, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930ba", + "creator": "volley", + "createdAt": 1260391117000, + "text": "See also LinkedHashSet, if you wish to retain the order.", + "upvotes": 460, + "upvoterUsernames": [], + "downvotes": 177, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931d1", + "creator": "Joel", + "createdAt": 1261558341000, + "text": "and StringBuilder is intended as a drop in replacement for StringBuffer where synchronisation is not required", + "upvotes": 377, + "upvoterUsernames": [], + "downvotes": 128, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045c", + "creator": "Michel Gokan Khan", + "createdAt": 1262530231000, + "text": "that's because a[10] means *(a+10) ... and 10[a] means *(10+a) :)", + "upvotes": 1417, + "upvoterUsernames": [], + "downvotes": 659, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a2", + "creator": "FogleBird", + "createdAt": 1262553536000, + "text": "+ for concat isn't the problem. Weak typing is.", + "upvotes": 493, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931ee", + "creator": "FogleBird", + "createdAt": 1262553536000, + "text": "+ for concat isn't the problem. Weak typing is.", + "upvotes": 770, + "upvoterUsernames": [], + "downvotes": 354, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930f6", + "creator": "TM.", + "createdAt": 1262554156000, + "text": "@FogleBird Neither one is really the problem. It's just the combination of the two with inconsistent coercion rules.", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 42, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec9", + "creator": "user168715", + "createdAt": 1262641148000, + "text": "These let you use the "WTF operator": (foo() != ERROR)??!??! cerr << "Error occurred" << endl;", + "upvotes": 500, + "upvoterUsernames": [], + "downvotes": 140, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930be", + "creator": "Hoang Pham", + "createdAt": 1263394099000, + "text": "be aware to commit all your changes before, otherwise you will loose control on all the changed files", + "upvotes": 555, + "upvoterUsernames": [], + "downvotes": 273, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931cd", + "creator": "Jon Skeet", + "createdAt": 1263764751000, + "text": "@unknown: In that case it would be Action instead of Func<string, int>.", + "upvotes": 366, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93078", + "creator": "Dapeng", + "createdAt": 1263915418000, + "text": "that prefix does nothing but to mess up the readability ...", + "upvotes": 530, + "upvoterUsernames": [], + "downvotes": 235, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930f3", + "creator": "wisnij", + "createdAt": 1264178027000, + "text": "Header files define INET6_ADDRSTRLEN to be 46, which fits with 45 chars plus a trailing null.", + "upvotes": 429, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930db", + "creator": "JuanZe", + "createdAt": 1265306082000, + "text": "Be ready to receive a lot of "Use JodaTime" answers...", + "upvotes": 307, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93122", + "creator": "Simon Nickerson", + "createdAt": 1266490661000, + "text": "The file might be truncated and you would never even kn", + "upvotes": 373, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93196", + "creator": "Simon Nickerson", + "createdAt": 1266490661000, + "text": "The file might be truncated and you would never even kn", + "upvotes": 450, + "upvoterUsernames": [], + "downvotes": 189, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90472", + "creator": "Marco Demaio", + "createdAt": 1267036150000, + "text": "Or inline: objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} );", + "upvotes": 964, + "upvoterUsernames": [], + "downvotes": 422, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab9082fcc3049e93225", + "creator": "Marco Demaio", + "createdAt": 1267036150000, + "text": "Or inline: objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} );", + "upvotes": 720, + "upvoterUsernames": [], + "downvotes": 178, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931bf", + "creator": "vy32", + "createdAt": 1267385507000, + "text": "Alas, this doesn't work if you can't fit the whole file in memory.", + "upvotes": 325, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9189a", + "creator": "Will", + "createdAt": 1268896119000, + "text": "Also note that the dict.copy() is shallow, if there is a nested list/etc in there changes will be applied to both. IIRC. Deepcopy will avoid that.", + "upvotes": 762, + "upvoterUsernames": [], + "downvotes": 342, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93129", + "creator": "Steve McLeod", + "createdAt": 1269863637000, + "text": "I had some colleagues who created some heavily obfuscated code. But that was not their intention.", + "upvotes": 300, + "upvoterUsernames": [], + "downvotes": 42, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319b", + "creator": "Steve McLeod", + "createdAt": 1269863637000, + "text": "I had some colleagues who created some heavily obfuscated code. But that was not their intention.", + "upvotes": 268, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931d3", + "creator": "Michael Burr", + "createdAt": 1270686854000, + "text": ""How to get git to go to vim for commit comments from the git-go?"", + "upvotes": 313, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9309e", + "creator": "jjujuma", + "createdAt": 1272623659000, + "text": "you can write the import as "import java.util.Map.Entry;" and it will work.", + "upvotes": 314, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90465", + "creator": "Noon Silk", + "createdAt": 1273731485000, + "text": "If it didn't, the keyword should be named probably instead.", + "upvotes": 947, + "upvoterUsernames": [], + "downvotes": 275, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90458", + "creator": "Art", + "createdAt": 1276329610000, + "text": "This breaks standard behaviour of many things, including buttons and links, contained within #menucontainer. I am surprised this answer is so popular.", + "upvotes": 1568, + "upvoterUsernames": [], + "downvotes": 773, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ee1", + "creator": "Shawn Mclean", + "createdAt": 1276623356000, + "text": "Hey, dont bash him, I ended up here from a google search lol.", + "upvotes": 658, + "upvoterUsernames": [], + "downvotes": 308, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91867", + "creator": "rmh", + "createdAt": 1278298782000, + "text": "saidimu: Since we're already using sorted(), it's much more efficient to pass in the reverse=True argument.", + "upvotes": 902, + "upvoterUsernames": [], + "downvotes": 413, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930f8", + "creator": "Jeffrey Jose", + "createdAt": 1278319235000, + "text": "However git commit --amend isnt as powerful as git rebase -i.", + "upvotes": 287, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93016", + "creator": "Joachim Sauer", + "createdAt": 1282226525000, + "text": "Strictly speaking the Tree class is not necessary, because every Node can in itself be seen as a tree.", + "upvotes": 368, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930c6", + "creator": "Tim Sullivan", + "createdAt": 1282343137000, + "text": "Why is this a better solution than simply calling [self.view endEditing:YES]?", + "upvotes": 467, + "upvoterUsernames": [], + "downvotes": 186, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ea5", + "creator": "DevinB", + "createdAt": 1282588419000, + "text": "If it's not cracked, it means nobody wants your application.", + "upvotes": 535, + "upvoterUsernames": [], + "downvotes": 151, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c7", + "creator": "Michael Myers", + "createdAt": 1282589710000, + "text": "Remember that there are more honest people in the world than dishonest -- [citation needed]", + "upvotes": 254, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9302f", + "creator": "Surya", + "createdAt": 1283201036000, + "text": "Best way to test a private method is not testing it directly", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 245, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90463", + "creator": "Randal Schwartz", + "createdAt": 1283913263000, + "text": "This regex eliminates valid, in-use emails. Do not use. Google for "RFC822" or "RFC2822" to get a proper regex.", + "upvotes": 1061, + "upvoterUsernames": [], + "downvotes": 372, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ee7", + "creator": "Georg Schölly", + "createdAt": 1285240427000, + "text": "Would this work as well? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);", + "upvotes": 454, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b0", + "creator": "DavidWinterbottom", + "createdAt": 1285762181000, + "text": "More succinctly: [ ! -f /tmp/foo.txt ] && echo "File not found!"", + "upvotes": 458, + "upvoterUsernames": [], + "downvotes": 204, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046b", + "creator": "Frank Shearar", + "createdAt": 1286293460000, + "text": "And if the commit was to the wrong branch, you may git checkout theRightBranch with all the changes stages. As I just had to do.", + "upvotes": 781, + "upvoterUsernames": [], + "downvotes": 216, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93116", + "creator": "gen_Eric", + "createdAt": 1286309803000, + "text": "arguments.callee.caller.name will get the function's name.", + "upvotes": 519, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9318d", + "creator": "gen_Eric", + "createdAt": 1286309803000, + "text": "arguments.callee.caller.name will get the function's name.", + "upvotes": 355, + "upvoterUsernames": [], + "downvotes": 92, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93024", + "creator": "SLaks", + "createdAt": 1287617356000, + "text": "The lesson is, always indent your code (and indent it correctly).", + "upvotes": 358, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90470", + "creator": "givanse", + "createdAt": 1287871968000, + "text": "All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance, an enum cannot extend anything else.", + "upvotes": 688, + "upvoterUsernames": [], + "downvotes": 143, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9189e", + "creator": "Chris Morgan", + "createdAt": 1290651842000, + "text": "Remember to use (var x in a) rather than (x in a) - don't want to create a global.", + "upvotes": 775, + "upvoterUsernames": [], + "downvotes": 356, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9188d", + "creator": "jondavidjohn", + "createdAt": 1293779223000, + "text": "printf("numbers from 1 to 1000");", + "upvotes": 657, + "upvoterUsernames": [], + "downvotes": 224, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff7", + "creator": "Konrad Rudolph", + "createdAt": 1294396009000, + "text": "Nominated for the stupidest interview question ever. And there’s a stiff competition.", + "upvotes": 565, + "upvoterUsernames": [], + "downvotes": 234, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e2", + "creator": "Konrad Rudolph", + "createdAt": 1294396009000, + "text": "Nominated for the stupidest interview question ever. And there’s a stiff competition.", + "upvotes": 479, + "upvoterUsernames": [], + "downvotes": 148, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90457", + "creator": "SmallChess", + "createdAt": 1294631839000, + "text": "This is a poor solution as it doesn't take any other delimiter, therefore not scalable and not maintable.", + "upvotes": 1074, + "upvoterUsernames": [], + "downvotes": 278, + "downvoterUsernames": [] + }, + { + "_id": "62f32b12082fcc3049e9322c", + "creator": "SmallChess", + "createdAt": 1294631839000, + "text": "This is a poor solution as it doesn't take any other delimiter, therefore not scalable and not maintable.", + "upvotes": 1005, + "upvoterUsernames": [], + "downvotes": 209, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9303d", + "creator": "damian", + "createdAt": 1294762576000, + "text": "In the "cherry-pick A..B" form, A should be older than B. If they're the wrong order the command will silently fail.", + "upvotes": 559, + "upvoterUsernames": [], + "downvotes": 258, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ebe", + "creator": "Martin Beckett", + "createdAt": 1294936876000, + "text": "Stackoverflow is the only usable MS documentation!", + "upvotes": 427, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff6", + "creator": "Quentin", + "createdAt": 1295043955000, + "text": "You can parse it as ASCII until you reach it. The HTML5 parsing algorithm takes this into account.", + "upvotes": 593, + "upvoterUsernames": [], + "downvotes": 261, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e1", + "creator": "Quentin", + "createdAt": 1295043955000, + "text": "You can parse it as ASCII until you reach it. The HTML5 parsing algorithm takes this into account.", + "upvotes": 590, + "upvoterUsernames": [], + "downvotes": 258, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91869", + "creator": "abernier", + "createdAt": 1295550243000, + "text": ""And by the way, is there a reason I can't simply git submodule rm whatever?" ?", + "upvotes": 938, + "upvoterUsernames": [], + "downvotes": 458, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90462", + "creator": "SilverbackNet", + "createdAt": 1296786324000, + "text": "Note that this one always evaluates everything, whereas the if/else construct only evaluates the winning expression.", + "upvotes": 819, + "upvoterUsernames": [], + "downvotes": 126, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931ac", + "creator": "Elazar Leibovich", + "createdAt": 1301176008000, + "text": "But why is it a keyword, and not an annotation @DoNotSerialize?", + "upvotes": 395, + "upvoterUsernames": [], + "downvotes": 141, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb3", + "creator": "Peter Wippermann", + "createdAt": 1301924895000, + "text": "I guess, this is owned to a time when there were no annotations in Java.", + "upvotes": 463, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930ac", + "creator": "nickf", + "createdAt": 1302019830000, + "text": "@Kevin, in that case, you might want to use the answer below this one. $('#list option:selected').text()", + "upvotes": 381, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93004", + "creator": "hallski", + "createdAt": 1302855158000, + "text": "In Xcode 4 you can simply double click the target name and change it directly in the target list.", + "upvotes": 336, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931eb", + "creator": "hallski", + "createdAt": 1302855158000, + "text": "In Xcode 4 you can simply double click the target name and change it directly in the target list.", + "upvotes": 458, + "upvoterUsernames": [], + "downvotes": 135, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045a", + "creator": "viam0Zah", + "createdAt": 1304242702000, + "text": "The opposite of this is String.fromCharCode(10).", + "upvotes": 776, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918a9", + "creator": "Alnitak", + "createdAt": 1304704304000, + "text": "the answer will not involve jQuery", + "upvotes": 630, + "upvoterUsernames": [], + "downvotes": 222, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931f2", + "creator": "Alnitak", + "createdAt": 1304704304000, + "text": "the answer will not involve jQuery", + "upvotes": 664, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9188a", + "creator": "Matthew Crumley", + "createdAt": 1305387701000, + "text": "@Dave, I'm not sure what the problem is since those are functions.", + "upvotes": 713, + "upvoterUsernames": [], + "downvotes": 276, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d2", + "creator": "GAgnew", + "createdAt": 1307039316000, + "text": "Wow I can't believe this was an accepted answer. There are many valid uses to remove a right-click in a webapp under specific conditions.", + "upvotes": 438, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eb1", + "creator": "Ben Clayton", + "createdAt": 1307278146000, + "text": "For more readable output try JSON.stringify(obj, null, 4). This'll write it out as neatly indented text", + "upvotes": 658, + "upvoterUsernames": [], + "downvotes": 282, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d4", + "creator": "fforw", + "createdAt": 1308325749000, + "text": "What!? creating and decompiling a Function to hack a multiline comment into being a multiline string? Now that's ugly.", + "upvotes": 468, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec0", + "creator": "bevacqua", + "createdAt": 1309737750000, + "text": "Who validates the validating regex?", + "upvotes": 416, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91896", + "creator": "Brann", + "createdAt": 1309826751000, + "text": "And of course, if you override a virtual method, you can always refer to the parent method by calling base.Foo(...)", + "upvotes": 734, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93022", + "creator": "jimt", + "createdAt": 1311713461000, + "text": "Then the bug should be fixed.", + "upvotes": 456, + "upvoterUsernames": [], + "downvotes": 141, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90475", + "creator": "deceze", + "createdAt": 1314348714000, + "text": "Start by understanding that +[] casts an empty array to 0... then waste an afternoon... ;)", + "upvotes": 546, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab9082fcc3049e93228", + "creator": "deceze", + "createdAt": 1314348714000, + "text": "Start by understanding that +[] casts an empty array to 0... then waste an afternoon... ;)", + "upvotes": 870, + "upvoterUsernames": [], + "downvotes": 349, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90478", + "creator": "pseudosudo", + "createdAt": 1314913713000, + "text": "sys.path.append('/path/to/application/app/folder') is cleaner imo", + "upvotes": 894, + "upvoterUsernames": [], + "downvotes": 380, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930d5", + "creator": "Run", + "createdAt": 1315411516000, + "text": "I found this - filter_var($string, FILTER_VALIDATE_BOOLEAN); is it a good thing?", + "upvotes": 510, + "upvoterUsernames": [], + "downvotes": 233, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93026", + "creator": "Jeremy Holovacs", + "createdAt": 1315596608000, + "text": "In MVC3, this is [AllowHtml] on the model property.", + "upvotes": 596, + "upvoterUsernames": [], + "downvotes": 282, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed3", + "creator": "biphobe", + "createdAt": 1316527008000, + "text": "Note that "disabled" checkbox doesn't send value via POST data.", + "upvotes": 501, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92ff4", + "creator": "Jens Kohl", + "createdAt": 1317391933000, + "text": "Probably you should use git submodule update --recursive nowadays.", + "upvotes": 510, + "upvoterUsernames": [], + "downvotes": 177, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931e0", + "creator": "Jens Kohl", + "createdAt": 1317391933000, + "text": "Probably you should use git submodule update --recursive nowadays.", + "upvotes": 419, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931ce", + "creator": "Adam Tuttle", + "createdAt": 1319515178000, + "text": "Also note: Don't do this with uncommitted changes in your working copy! This just bit me! :(", + "upvotes": 381, + "upvoterUsernames": [], + "downvotes": 132, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931cb", + "creator": "ObscureRobot", + "createdAt": 1319815538000, + "text": "w͢͢͝h͡o͢͡ ̸͢k̵͟n̴͘ǫw̸̛s͘ ̀́w͘͢ḩ̵a҉̡͢t ̧̕h́o̵r͏̵rors̡ ̶͡͠lį̶e͟͟ ̶͝in͢ ͏t̕h̷̡͟e ͟͟d̛a͜r̕͡k̢̨ ͡h̴e͏a̷̢̡rt́͏ ̴̷͠&òf̸ u̧͘níc͢͏o̷͏d̸͢e̡͝?͞", + "upvotes": 289, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918ac", + "creator": "WEFX", + "createdAt": 1320173511000, + "text": "I believe that's only valid if you have a single line of text in the div.", + "upvotes": 670, + "upvoterUsernames": [], + "downvotes": 265, + "downvoterUsernames": [] + }, + { + "_id": "62f32a9a082fcc3049e931f3", + "creator": "WEFX", + "createdAt": 1320173511000, + "text": "I believe that's only valid if you have a single line of text in the div.", + "upvotes": 618, + "upvoterUsernames": [], + "downvotes": 213, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918b6", + "creator": "Mathias Bynens", + "createdAt": 1321202364000, + "text": "git submodule foreach git pull", + "upvotes": 424, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eeb", + "creator": "Guillermo Gutiérrez", + "createdAt": 1321388783000, + "text": "Inefficient...but very creative :)", + "upvotes": 374, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93120", + "creator": "jaygooby", + "createdAt": 1321963756000, + "text": "And use the -n switch if you want the code back, but not automatically committed in again", + "upvotes": 338, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93190", + "creator": "jaygooby", + "createdAt": 1321963756000, + "text": "And use the -n switch if you want the code back, but not automatically committed in again", + "upvotes": 399, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93018", + "creator": "Rok Strniša", + "createdAt": 1321988312000, + "text": "You can get Eclipse to generate the two methods for you: Source > Generate hashCode() and equals().", + "upvotes": 397, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e93098", + "creator": "Dean", + "createdAt": 1323382022000, + "text": "Also, in edit mode gt goes to the next tab, and gT goes to the previous tab.", + "upvotes": 291, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93113", + "creator": "Chase", + "createdAt": 1323736789000, + "text": "In the spirit of Android, it's all yours! And hopefully when you build something that may be useful, you are able to share that :)", + "upvotes": 265, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9318f", + "creator": "Chase", + "createdAt": 1323736789000, + "text": "In the spirit of Android, it's all yours! And hopefully when you build something that may be useful, you are able to share that :)", + "upvotes": 397, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046a", + "creator": "Igor Popov", + "createdAt": 1325141984000, + "text": "Wow, so now I can just do :later 8h and I'm done for today? :P", + "upvotes": 1023, + "upvoterUsernames": [], + "downvotes": 443, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930ad", + "creator": "Vishnu Haridas", + "createdAt": 1325236977000, + "text": "Easy trick to remember: Take "layout-gravity" as "Lay-outside-gravity"", + "upvotes": 394, + "upvoterUsernames": [], + "downvotes": 108, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9185f", + "creator": "tungd", + "createdAt": 1326170413000, + "text": "@SaifBechan Don't worry about IE 6/7, we are talking about localStorage", + "upvotes": 860, + "upvoterUsernames": [], + "downvotes": 355, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e92fec", + "creator": "Dmitry Zaytsev", + "createdAt": 1326571129000, + "text": "Note: it's works only after onCreateView(). So, you can't use this in onCreate()", + "upvotes": 547, + "upvoterUsernames": [], + "downvotes": 210, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931dc", + "creator": "Dmitry Zaytsev", + "createdAt": 1326571129000, + "text": "Note: it's works only after onCreateView(). So, you can't use this in onCreate()", + "upvotes": 513, + "upvoterUsernames": [], + "downvotes": 176, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91878", + "creator": "Tom W", + "createdAt": 1326639265000, + "text": "Great answer, StackOverflow needs more of this sort of thing, instead of 'Why would you want to know that?' that happens all too often.", + "upvotes": 473, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931f6", + "creator": "Tom W", + "createdAt": 1326639265000, + "text": "Great answer, StackOverflow needs more of this sort of thing, instead of 'Why would you want to know that?' that happens all too often.", + "upvotes": 497, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ecb", + "creator": "Cameron MacFarland", + "createdAt": 1326792026000, + "text": "If the bad guys have access to the RAM of your machine, you have bigger issues than them stealing your password.", + "upvotes": 386, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed5", + "creator": "Richard Collette", + "createdAt": 1328024608000, + "text": "Keep in mind that if the column is nullable, then null will be the value used for existing rows.", + "upvotes": 488, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930c4", + "creator": "Milovan Zogovic", + "createdAt": 1328102973000, + "text": "for well structured css files, order of inclusion should not matter", + "upvotes": 283, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045e", + "creator": "Chris Ward", + "createdAt": 1328515332000, + "text": "I'll bet if you put a label on a sock drawer you'd call it "Socks".", + "upvotes": 1076, + "upvoterUsernames": [], + "downvotes": 326, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93135", + "creator": "Mark Ransom", + "createdAt": 1328896357000, + "text": "The Linux kernel does not use C++, at least not while Linus is still alive.", + "upvotes": 287, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319e", + "creator": "Mark Ransom", + "createdAt": 1328896357000, + "text": "The Linux kernel does not use C++, at least not while Linus is still alive.", + "upvotes": 326, + "upvoterUsernames": [], + "downvotes": 69, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930b1", + "creator": "lensovet", + "createdAt": 1329199023000, + "text": "clearly "a few cascading deletes"≠dropping all data from the table…", + "upvotes": 542, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90454", + "creator": "Gus", + "createdAt": 1329330902000, + "text": "Remember it by "bang, bang you're boolean"", + "upvotes": 1648, + "upvoterUsernames": [], + "downvotes": 348, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9309c", + "creator": "Lloyd Moore", + "createdAt": 1329836214000, + "text": "instead of merging using 'git pull', try git fetch --all followed by 'git reset --hard origin/master'", + "upvotes": 531, + "upvoterUsernames": [], + "downvotes": 243, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931bd", + "creator": "Viktor Apoyan", + "createdAt": 1330085910000, + "text": "Check your WiFi connection : )", + "upvotes": 260, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91871", + "creator": "Simon East", + "createdAt": 1330296557000, + "text": "Except this doesn't solve the problem at all when your rows have a background colour and you actually want WHITESPACE between your rows.", + "upvotes": 766, + "upvoterUsernames": [], + "downvotes": 294, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9310a", + "creator": "Ignacio Vazquez-Abrams", + "createdAt": 1330978311000, + "text": "Be careful, since lots of other things are false as well.", + "upvotes": 452, + "upvoterUsernames": [], + "downvotes": 187, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92e97", + "creator": "Eurig Jones", + "createdAt": 1331656457000, + "text": "Note: The above is converting DIPs to Pixels. The original question asked how to convert pixels to Dips!", + "upvotes": 412, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93031", + "creator": "dragonroot", + "createdAt": 1331777111000, + "text": "So your answer is basically 'contact the manufacturer'. Not useful.", + "upvotes": 461, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930b3", + "creator": "rubdottocom", + "createdAt": 1331970909000, + "text": "context.getSharedPreferences("YOUR_PREFS", 0).edit().clear().commit(); //remove all your prefs :)", + "upvotes": 507, + "upvoterUsernames": [], + "downvotes": 222, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9309a", + "creator": "Vanja", + "createdAt": 1332919714000, + "text": "To make a case insensitive search use "if ([string rangeOfString:@"bla" options:NSCaseInsensitiveSearch].location != NSNotFound)"", + "upvotes": 311, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90466", + "creator": "ala", + "createdAt": 1333418207000, + "text": "if you cant find XML Tools under Plugins, intall it from Plugins > Plugins Manager > Show Plugins Manager", + "upvotes": 1313, + "upvoterUsernames": [], + "downvotes": 647, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec3", + "creator": "drogon", + "createdAt": 1333480807000, + "text": "$('[selector]')[0].outerHTML", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 293, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90469", + "creator": "ThiefMaster", + "createdAt": 1334054324000, + "text": "If that ever becomes a real issue for you, you are doing something very wrong.", + "upvotes": 870, + "upvoterUsernames": [], + "downvotes": 261, + "downvoterUsernames": [] + }, + { + "_id": "62f32acb082fcc3049e9322a", + "creator": "ThiefMaster", + "createdAt": 1334054324000, + "text": "If that ever becomes a real issue for you, you are doing something very wrong.", + "upvotes": 918, + "upvoterUsernames": [], + "downvotes": 309, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9307f", + "creator": "JeremyP", + "createdAt": 1334055171000, + "text": "You can keep adding levels of pointers until your brain explodes or the compiler melts - whichever happens soonest.", + "upvotes": 458, + "upvoterUsernames": [], + "downvotes": 163, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918ba", + "creator": "muscardinus", + "createdAt": 1336215564000, + "text": "You can use Locale.getDefault().getLanguage(); to get the usual language code (e.g. "de", "en").", + "upvotes": 686, + "upvoterUsernames": [], + "downvotes": 290, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9301c", + "creator": "dragon", + "createdAt": 1336378422000, + "text": "Math.random().toString(36).substr(2, 5), because .substring(7) causes it to be longer than 5 characters. Full points, still!", + "upvotes": 440, + "upvoterUsernames": [], + "downvotes": 124, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93005", + "creator": "ceklock", + "createdAt": 1338793035000, + "text": "Why can't just pass the object without all this? We want to pass an object that is already in memory.", + "upvotes": 501, + "upvoterUsernames": [], + "downvotes": 178, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931ec", + "creator": "ceklock", + "createdAt": 1338793035000, + "text": "Why can't just pass the object without all this? We want to pass an object that is already in memory.", + "upvotes": 473, + "upvoterUsernames": [], + "downvotes": 150, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9045b", + "creator": "Larry Battle", + "createdAt": 1339481866000, + "text": "Think of a in apply for array of args and c in call for columns of args.", + "upvotes": 1280, + "upvoterUsernames": [], + "downvotes": 519, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9308a", + "creator": "tasmaniski", + "createdAt": 1340289999000, + "text": "My one line solution for generate short string is substr(md5(rand()), 0, 7); good luck ...", + "upvotes": 483, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90461", + "creator": "doub1ejack", + "createdAt": 1341343812000, + "text": "echo date("Y");", + "upvotes": 709, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9310c", + "creator": "Jack M", + "createdAt": 1341494381000, + "text": "What the hell were you even doing that required you to work with either base-19 numbers OR division by zero!?", + "upvotes": 528, + "upvoterUsernames": [], + "downvotes": 263, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930e1", + "creator": "Wes McKinney", + "createdAt": 1341770057000, + "text": "Note: df[['a','b']] produces a copy", + "upvotes": 507, + "upvoterUsernames": [], + "downvotes": 232, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91876", + "creator": "Grant Birchmeier", + "createdAt": 1342194439000, + "text": "Gotta love those useful Git error messages. Can't find the repository that I just cloned from, huh? Liar.", + "upvotes": 554, + "upvoterUsernames": [], + "downvotes": 96, + "downvoterUsernames": [] + }, + { + "_id": "62f32aaa082fcc3049e931f5", + "creator": "Grant Birchmeier", + "createdAt": 1342194439000, + "text": "Gotta love those useful Git error messages. Can't find the repository that I just cloned from, huh? Liar.", + "upvotes": 661, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91861", + "creator": "Salman von Abbas", + "createdAt": 1342894672000, + "text": "WTF why haven't I seen this being used somewhere in my 3 years with JavaScript :/..", + "upvotes": 895, + "upvoterUsernames": [], + "downvotes": 397, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93133", + "creator": "neworld", + "createdAt": 1343027471000, + "text": "Or if you opened activity with startActivity(), you can close with finish() (don't need pass any parameter)", + "upvotes": 389, + "upvoterUsernames": [], + "downvotes": 132, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e9319d", + "creator": "neworld", + "createdAt": 1343027471000, + "text": "Or if you opened activity with startActivity(), you can close with finish() (don't need pass any parameter)", + "upvotes": 431, + "upvoterUsernames": [], + "downvotes": 174, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9046e", + "creator": "Kyle Clegg", + "createdAt": 1343237373000, + "text": "The bigger question Chris, is why would you follow database naming conventions when naming a sock drawer?", + "upvotes": 809, + "upvoterUsernames": [], + "downvotes": 259, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b5", + "creator": "Alan Curry", + "createdAt": 1343434495000, + "text": "Improved version: log(pow(exp(number),sin(atan2(1,sqrt(8)))))", + "upvotes": 442, + "upvoterUsernames": [], + "downvotes": 188, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93108", + "creator": "Vili", + "createdAt": 1346661571000, + "text": "I don't (really) understand, but didn't you miss an S in ...auschreibung...?", + "upvotes": 287, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90468", + "creator": "NimChimpsky", + "createdAt": 1347266610000, + "text": "why don't all IDE's have the default to "yes show line numbers", how do you debug anythign without using line numbers ?", + "upvotes": 870, + "upvoterUsernames": [], + "downvotes": 239, + "downvoterUsernames": [] + }, + { + "_id": "62f32acb082fcc3049e93229", + "creator": "NimChimpsky", + "createdAt": 1347266610000, + "text": "why don't all IDE's have the default to "yes show line numbers", how do you debug anythign without using line numbers ?", + "upvotes": 897, + "upvoterUsernames": [], + "downvotes": 266, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930b6", + "creator": "AndaP", + "createdAt": 1348144957000, + "text": "Right on, in my opinion almost all documentation revolving around beans can't describe the term as concisely as you have. +1", + "upvotes": 529, + "upvoterUsernames": [], + "downvotes": 244, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918b8", + "creator": "meh", + "createdAt": 1348825487000, + "text": "You should use commitAllowingStateLoss() instead of commit()", + "upvotes": 443, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93040", + "creator": "UpTheCreek", + "createdAt": 1349275367000, + "text": "=== 'function' would be better than != 'undefined'", + "upvotes": 402, + "upvoterUsernames": [], + "downvotes": 102, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93107", + "creator": "Jon Skeet", + "createdAt": 1349714548000, + "text": "... and if you own the event, you can just write FindClicked = null; which is rather simpler.", + "upvotes": 465, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930aa", + "creator": "intrepidis", + "createdAt": 1350219636000, + "text": "I thought JSON was to supposed to be more human readable than, say, XML? Comments are for readability.", + "upvotes": 405, + "upvoterUsernames": [], + "downvotes": 119, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930e4", + "creator": "Kim", + "createdAt": 1351070412000, + "text": "git diff --shortstat <commit1> <commit2> was the one I wanted.", + "upvotes": 406, + "upvoterUsernames": [], + "downvotes": 132, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90459", + "creator": "usr", + "createdAt": 1351096118000, + "text": "Not sure why people think this code is beautiful. It is indecipherable. It seems to work nicely, but it is not beautiful.", + "upvotes": 1300, + "upvoterUsernames": [], + "downvotes": 508, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91865", + "creator": "Jez", + "createdAt": 1351177077000, + "text": "But what if the script is very specific to the partial? Doesn't it make logical sense for it to be defined in the partial, and not the view?", + "upvotes": 663, + "upvoterUsernames": [], + "downvotes": 168, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90464", + "creator": "Thane Brimhall", + "createdAt": 1351200526000, + "text": "git rev-parse --short HEAD returns the short version of the hash, just in case anyone was wondering.", + "upvotes": 1105, + "upvoterUsernames": [], + "downvotes": 419, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b9", + "creator": "joshcomley", + "createdAt": 1351591855000, + "text": "This is (in my opinion) one of the most fundamentally anti-useful features I've ever seen", + "upvotes": 351, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e918ae", + "creator": "kolypto", + "createdAt": 1352124018000, + "text": "Just in case, make a backup first: git tag BACKUP. You can return to it if something goes wrong: git reset --hard BACKUP", + "upvotes": 614, + "upvoterUsernames": [], + "downvotes": 211, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930ce", + "creator": "karmakaze", + "createdAt": 1353047327000, + "text": "@Justin To only remove from git and leave filesystem as-is, use git rm -r --cached myFolder", + "upvotes": 528, + "upvoterUsernames": [], + "downvotes": 250, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91863", + "creator": "Wes McKinney", + "createdAt": 1353467328000, + "text": "I'd recommend using pandas.notnull instead of np.isfinite", + "upvotes": 792, + "upvoterUsernames": [], + "downvotes": 296, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93013", + "creator": "keks", + "createdAt": 1354618744000, + "text": "The line deleted by Ctrl-U is recallable with Ctrl-Y, too.", + "upvotes": 387, + "upvoterUsernames": [], + "downvotes": 67, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90479", + "creator": "mr.freeze", + "createdAt": 1355255749000, + "text": "I trust the stackoverflow community more. I want to hear from people who use them in real-world scenarios instead of their sales department.", + "upvotes": 798, + "upvoterUsernames": [], + "downvotes": 286, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930a0", + "creator": "Lucretiel", + "createdAt": 1355642572000, + "text": "Even cleaner, I think= max(stats.iterkeys(), key=(lambda key: stats[key]))", + "upvotes": 461, + "upvoterUsernames": [], + "downvotes": 174, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e9307c", + "creator": "weddingcakes", + "createdAt": 1356704299000, + "text": "I actually use it because of the free plan ;).", + "upvotes": 330, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ee5", + "creator": "Berislav Lopac", + "createdAt": 1356708187000, + "text": "Why is this not built in by default? :-(", + "upvotes": 391, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93102", + "creator": "keithjgrant", + "createdAt": 1356876421000, + "text": ""poetry"? More like obscurity. This isn't code golf; use a little white space. Proper var names wouldn't hurt, either.", + "upvotes": 523, + "upvoterUsernames": [], + "downvotes": 255, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c2", + "creator": "Warpzit", + "createdAt": 1357554928000, + "text": "To go to top simply use: fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);", + "upvotes": 482, + "upvoterUsernames": [], + "downvotes": 231, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930e9", + "creator": "Mechanical snail", + "createdAt": 1357602159000, + "text": "MD5 is a digest algorithm. Think of it as converting a cow into a steak. Now try to reverse that.", + "upvotes": 481, + "upvoterUsernames": [], + "downvotes": 208, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91894", + "creator": "Luke", + "createdAt": 1357672091000, + "text": "This answer is more correct than try it because current behavior is not the same as guaranteed behavior.", + "upvotes": 794, + "upvoterUsernames": [], + "downvotes": 367, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ebb", + "creator": "Martijn", + "createdAt": 1357831199000, + "text": "@KevinSchroeder: In javascript parlance, [] is called an array, {} is called an object.", + "upvotes": 720, + "upvoterUsernames": [], + "downvotes": 350, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931b2", + "creator": "Fábio Santos", + "createdAt": 1358869265000, + "text": "I think they are fairly dangerous. However in python "global" variables are actually module-level, which solves a lot of issues.", + "upvotes": 332, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9189c", + "creator": "JBentley", + "createdAt": 1359490797000, + "text": "@tiwo I for one disagree that it's not useful. Your directory hierarchy is part of your project, so it should be version controlled.", + "upvotes": 446, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930b5", + "creator": "Lord Loh.", + "createdAt": 1359799755000, + "text": "For JSON, I use mObj=JSON.parse(JSON.stringify(jsonObject));", + "upvotes": 408, + "upvoterUsernames": [], + "downvotes": 123, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e931c1", + "creator": "Jakub P.", + "createdAt": 1359855827000, + "text": "Why doesn't the request to obtain this data require a CSRF-token instead?", + "upvotes": 288, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930f7", + "creator": "Cerbrus", + "createdAt": 1360838240000, + "text": "return a.last_nom.localeCompare(b.last_nom) will work, too.", + "upvotes": 469, + "upvoterUsernames": [], + "downvotes": 199, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930a8", + "creator": "Ziggy", + "createdAt": 1361328482000, + "text": "That's no one-liner... it's a space station.", + "upvotes": 424, + "upvoterUsernames": [], + "downvotes": 138, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93110", + "creator": "javajavajavajavajava", + "createdAt": 1361369938000, + "text": "@BonusKun List<Order> SortedList = objListOrder. OrderByDescending (o=>o.OrderDate).ToList();", + "upvotes": 516, + "upvoterUsernames": [], + "downvotes": 251, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93100", + "creator": "Anorov", + "createdAt": 1361490201000, + "text": "range in Python 3.x is xrange from Python 2.x. It was in fact Python 2.x's range that was removed.", + "upvotes": 428, + "upvoterUsernames": [], + "downvotes": 160, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e930fc", + "creator": "Nilzor", + "createdAt": 1363342470000, + "text": "Anyone care to explain what the {} \\; on the end the line means?", + "upvotes": 381, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93039", + "creator": "Aaron", + "createdAt": 1363380662000, + "text": "another option: $('#xxx').prop('outerHTML')", + "upvotes": 328, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91898", + "creator": "cryss", + "createdAt": 1363873169000, + "text": "And please note that the maximum number of rows in one insert statement is 1000.", + "upvotes": 649, + "upvoterUsernames": [], + "downvotes": 228, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec5", + "creator": "Kurt Zhong", + "createdAt": 1365652718000, + "text": "Replacing the --name-only option with --name-status will give more clear summary.", + "upvotes": 560, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ec7", + "creator": "jlembke", + "createdAt": 1365716584000, + "text": "I have a "Sock" drawer at home, not a "Socks" drawer. If it were a database, I'd call it the "Sock" table.", + "upvotes": 620, + "upvoterUsernames": [], + "downvotes": 259, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930c8", + "creator": "Tim MB", + "createdAt": 1366365822000, + "text": ""All you need to do..."", + "upvotes": 401, + "upvoterUsernames": [], + "downvotes": 121, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e93007", + "creator": "dfsq", + "createdAt": 1366836156000, + "text": "+1. But the shortest version will be in ECMAScript 6: [a, b] = [b, a];.", + "upvotes": 565, + "upvoterUsernames": [], + "downvotes": 242, + "downvoterUsernames": [] + }, + { + "_id": "62f32a8a082fcc3049e931ed", + "creator": "dfsq", + "createdAt": 1366836156000, + "text": "+1. But the shortest version will be in ECMAScript 6: [a, b] = [b, a];.", + "upvotes": 515, + "upvoterUsernames": [], + "downvotes": 192, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930ca", + "creator": "batman", + "createdAt": 1370617349000, + "text": "@NoBugs, Yes, and it is encouraged that variables are existent only where they are needed.", + "upvotes": 446, + "upvoterUsernames": [], + "downvotes": 166, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e93118", + "creator": "Oliver", + "createdAt": 1370794403000, + "text": "A software which costs 70$ should support printing.", + "upvotes": 423, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [] + }, + { + "_id": "62f32a7a082fcc3049e93195", + "creator": "Oliver", + "createdAt": 1370794403000, + "text": "A software which costs 70$ should support printing.", + "upvotes": 443, + "upvoterUsernames": [], + "downvotes": 181, + "downvoterUsernames": [] + }, + { + "_id": "62f32a56082fcc3049e930c0", + "creator": "WaelJ", + "createdAt": 1370878303000, + "text": "This is the most ingenious abuse of substitution that I have seen!", + "upvotes": 443, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90473", + "creator": "abbood", + "createdAt": 1371468441000, + "text": "a download progress bar wouldn't hurt anyone", + "upvotes": 869, + "upvoterUsernames": [], + "downvotes": 333, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab9082fcc3049e93226", + "creator": "abbood", + "createdAt": 1371468441000, + "text": "a download progress bar wouldn't hurt anyone", + "upvotes": 854, + "upvoterUsernames": [], + "downvotes": 318, + "downvoterUsernames": [] + }, + { + "_id": "62f32a12082fcc3049e9302c", + "creator": "Benjamin Oakes", + "createdAt": 1371740278000, + "text": "Short and sweet: File.write('filename', 'content')", + "upvotes": 420, + "upvoterUsernames": [], + "downvotes": 115, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ea3", + "creator": "ericmjl", + "createdAt": 1372269307000, + "text": "Is it possible to change a single column header name?", + "upvotes": 570, + "upvoterUsernames": [], + "downvotes": 182, + "downvoterUsernames": [] + }, + { + "_id": "62f32a68082fcc3049e9310e", + "creator": "Johan", + "createdAt": 1372851032000, + "text": "This does not work in the Nintendo 3DS browser. The change is not detected even though the input field contains the new string.", + "upvotes": 418, + "upvoterUsernames": [], + "downvotes": 153, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92ed7", + "creator": "jfreak53", + "createdAt": 1373414136000, + "text": "400 is too low as that makes it non-writable by your own user. 600 is actually recommended as it allows owner read-write not just read.", + "upvotes": 410, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [] + }, + { + "_id": "62f329c5082fcc3049e92eed", + "creator": "hughes", + "createdAt": 1374697274000, + "text": "So if you want to print "{42}", you'd use "{{{0}}}".format(42) !", + "upvotes": 438, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91872", + "creator": "tandy", + "createdAt": 1375908762000, + "text": "-e flag did it for me, which "enables interpretation of backslash escapes"", + "upvotes": 540, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1289, + "uvac": 1439 + } + }, + { + "_id": "62f321bb082fcc3049e8fef3", + "title": "Validate decimal numbers in JavaScript - IsNumeric()", + "title-lowercase": "validate decimal numbers in javascript - isnumeric()", + "creator": "Michael Haren", + "createdAt": 1219242073000, + "status": "open", + "text": "

What's the cleanest, most effective way to validate decimal numbers in JavaScript?

\n\n

Bonus points for:

\n\n
    \n
  1. Clarity. Solution should be clean and simple.
  2. \n
  3. Cross-platform.
  4. \n
\n\n

Test cases:

\n\n
01. IsNumeric('-1')      => true\n02. IsNumeric('-1.5')    => true\n03. IsNumeric('0')       => true\n04. IsNumeric('0.42')    => true\n05. IsNumeric('.42')     => true\n06. IsNumeric('99,999')  => false\n07. IsNumeric('0x89f')   => false\n08. IsNumeric('#abcdef') => false\n09. IsNumeric('1.2.3')   => false\n10. IsNumeric('')        => false\n11. IsNumeric('blah')    => false\n
\n", + "upvotes": 3074, + "upvoterUsernames": [], + "downvotes": 554, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1548592, + "answers": 50, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90cbc", + "creator": "Michael Haren", + "createdAt": 1219242176000, + "text": "

This way seems to work well:

\n\n
function IsNumeric(input){\n    var RE = /^-{0,1}\\d*\\.{0,1}\\d+$/;\n    return (RE.test(input));\n}\n
\n\n

In one line:

\n\n
const IsNumeric = (num) => /^-{0,1}\\d*\\.{0,1}\\d+$/.test(num);\n
\n\n

And to test it:

\n\n

\r\n
\r\n
const IsNumeric = (num) => /^-{0,1}\\d*\\.{0,1}\\d+$/.test(num);\r\n    \r\n    function TestIsNumeric(){\r\n        var results = ''\r\n        results += (IsNumeric('-1')?\"Pass\":\"Fail\") + \": IsNumeric('-1') => true\\n\";\r\n        results += (IsNumeric('-1.5')?\"Pass\":\"Fail\") + \": IsNumeric('-1.5') => true\\n\";\r\n        results += (IsNumeric('0')?\"Pass\":\"Fail\") + \": IsNumeric('0') => true\\n\";\r\n        results += (IsNumeric('0.42')?\"Pass\":\"Fail\") + \": IsNumeric('0.42') => true\\n\";\r\n        results += (IsNumeric('.42')?\"Pass\":\"Fail\") + \": IsNumeric('.42') => true\\n\";\r\n        results += (!IsNumeric('99,999')?\"Pass\":\"Fail\") + \": IsNumeric('99,999') => false\\n\";\r\n        results += (!IsNumeric('0x89f')?\"Pass\":\"Fail\") + \": IsNumeric('0x89f') => false\\n\";\r\n        results += (!IsNumeric('#abcdef')?\"Pass\":\"Fail\") + \": IsNumeric('#abcdef') => false\\n\";\r\n        results += (!IsNumeric('1.2.3')?\"Pass\":\"Fail\") + \": IsNumeric('1.2.3') => false\\n\";\r\n        results += (!IsNumeric('')?\"Pass\":\"Fail\") + \": IsNumeric('') => false\\n\";\r\n        results += (!IsNumeric('blah')?\"Pass\":\"Fail\") + \": IsNumeric('blah') => false\\n\";\r\n        \r\n        return results;\r\n    }\r\n\r\nconsole.log(TestIsNumeric());
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
\r\n
\r\n

\n\n

I borrowed that regex from http://www.codetoad.com/javascript/isnumeric.asp. Explanation:

\n\n
/^ match beginning of string\n-{0,1} optional negative sign\n\\d* optional digits\n\\.{0,1} optional decimal point\n\\d+ at least one digit\n$/ match end of string\n
\n", + "upvotes": 164, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cbd", + "creator": "pottedmeat", + "createdAt": 1219244006000, + "text": "

A couple of tests to add:

\n\n
IsNumeric('01.05') => false\nIsNumeric('1.') => false\nIsNumeric('.') => false\n
\n\n

I came up with this:

\n\n
function IsNumeric(input) {\n    return /^-?(0|[1-9]\\d*|(?=\\.))(\\.\\d+)?$/.test(input);\n}\n
\n\n

The solution covers:

\n\n\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cbe", + "creator": "Marius", + "createdAt": 1219246828000, + "text": "

I'd like to add the following:

\n\n
\n1. IsNumeric('0x89f') => true\n2. IsNumeric('075') => true\n
\n\n

Positive hex numbers start with 0x and negative hex numbers start with -0x.\nPositive oct numbers start with 0 and negative oct numbers start with -0.\nThis one takes most of what has already been mentioned into consideration, but includes hex and octal numbers, negative scientific, Infinity and has removed decimal scientific (4e3.2 is not valid).

\n\n
function IsNumeric(input){\n  var RE = /^-?(0|INF|(0[1-7][0-7]*)|(0x[0-9a-fA-F]+)|((0|[1-9][0-9]*|(?=[\\.,]))([\\.,][0-9]+)?([eE]-?\\d+)?))$/;\n  return (RE.test(input));\n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cbf", + "creator": "bubbassauro", + "createdAt": 1219280535000, + "text": "

Use the function isNaN. I believe if you test for !isNaN(yourstringhere) it works fine for any of these situations.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc1", + "creator": "Aquatic", + "createdAt": 1219417685000, + "text": "

It can be done without RegExp as

\n\n
function IsNumeric(data){\n    return parseFloat(data)==data;\n}\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc0", + "creator": "travis", + "createdAt": 1219291511000, + "text": "

Yeah, the built-in isNaN(object) will be much faster than any regex parsing, because it's built-in and compiled, instead of interpreted on the fly.

\n\n

Although the results are somewhat different to what you're looking for (try it):

\n\n
                                              // IS NUMERIC\ndocument.write(!isNaN('-1') + \"<br />\");      // true\ndocument.write(!isNaN('-1.5') + \"<br />\");    // true\ndocument.write(!isNaN('0') + \"<br />\");       // true\ndocument.write(!isNaN('0.42') + \"<br />\");    // true\ndocument.write(!isNaN('.42') + \"<br />\");     // true\ndocument.write(!isNaN('99,999') + \"<br />\");  // false\ndocument.write(!isNaN('0x89f') + \"<br />\");   // true\ndocument.write(!isNaN('#abcdef') + \"<br />\"); // false\ndocument.write(!isNaN('1.2.3') + \"<br />\");   // false\ndocument.write(!isNaN('') + \"<br />\");        // true\ndocument.write(!isNaN('blah') + \"<br />\");    // false\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc2", + "creator": "Joel Coehoorn", + "createdAt": 1223309085000, + "text": "

Arrrgh! Don't listen to the regular expression answers. RegEx is icky for this, and I'm not talking just performance. It's so easy to make subtle, impossible to spot mistakes with your regular expression.

\n\n

If you can't use isNaN(), this should work much better:

\n\n
function IsNumeric(input)\n{\n    return (input - 0) == input && (''+input).trim().length > 0;\n}\n
\n\n

Here's how it works:

\n\n

The (input - 0) expression forces JavaScript to do type coercion on your input value; it must first be interpreted as a number for the subtraction operation. If that conversion to a number fails, the expression will result in NaN. This numeric result is then compared to the original value you passed in. Since the left hand side is now numeric, type coercion is again used. Now that the input from both sides was coerced to the same type from the same original value, you would think they should always be the same (always true). However, there's a special rule that says NaN is never equal to NaN, and so a value that can't be converted to a number (and only values that cannot be converted to numbers) will result in false.

\n\n

The check on the length is for a special case involving empty strings. Also note that it falls down on your 0x89f test, but that's because in many environments that's an okay way to define a number literal. If you want to catch that specific scenario you could add an additional check. Even better, if that's your reason for not using isNaN() then just wrap your own function around isNaN() that can also do the additional check.

\n\n

In summary, if you want to know if a value can be converted to a number, actually try to convert it to a number.

\n\n
\n\n

I went back and did some research for why a whitespace string did not have the expected output, and I think I get it now: an empty string is coerced to 0 rather than NaN. Simply trimming the string before the length check will handle this case.

\n\n

Running the unit tests against the new code and it only fails on the infinity and boolean literals, and the only time that should be a problem is if you're generating code (really, who would type in a literal and check if it's numeric? You should know), and that would be some strange code to generate.

\n\n

But, again, the only reason ever to use this is if for some reason you have to avoid isNaN().

\n", + "upvotes": 552, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc3", + "creator": "camomileCase", + "createdAt": 1250284047000, + "text": "

Yahoo! UI uses this:

\n\n
isNumber: function(o) {\n    return typeof o === 'number' && isFinite(o);\n}\n
\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc4", + "creator": "user189277", + "createdAt": 1255453516000, + "text": "
function IsNumeric(num) {\n     return (num >=0 || num < 0);\n}\n
\n\n

This works for 0x23 type numbers as well.

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc5", + "creator": "Christian C. Salvadó", + "createdAt": 1259732182000, + "text": "

@Joel's answer is pretty close, but it will fail in the following cases:

\n\n
// Whitespace strings:\nIsNumeric(' ')    == true;\nIsNumeric('\\t\\t') == true;\nIsNumeric('\\n\\r') == true;\n\n// Number literals:\nIsNumeric(-1)  == false;\nIsNumeric(0)   == false;\nIsNumeric(1.1) == false;\nIsNumeric(8e5) == false;\n
\n\n

Some time ago I had to implement an IsNumeric function, to find out if a variable contained a numeric value, regardless of its type, it could be a String containing a numeric value (I had to consider also exponential notation, etc.), a Number object, virtually anything could be passed to that function, I couldn't make any type assumptions, taking care of type coercion (eg. +true == 1; but true shouldn't be considered as \"numeric\").

\n\n

I think is worth sharing this set of +30 unit tests made to numerous function implementations, and also share the one that passes all my tests:

\n\n
function isNumeric(n) {\n    return !isNaN(parseFloat(n)) && isFinite(n);\n}\n
\n\n

P.S. isNaN & isFinite have a confusing behavior due to forced conversion to number. In ES6, Number.isNaN & Number.isFinite would fix these issues. Keep that in mind when using them.

\n\n
\n\n

Update : \nHere's how jQuery does it now (2.2-stable):

\n\n
isNumeric: function(obj) {\n    var realStringObj = obj && obj.toString();\n    return !jQuery.isArray(obj) && (realStringObj - parseFloat(realStringObj) + 1) >= 0;\n}\n
\n\n

Update :\nAngular 4.3:

\n\n
export function isNumeric(value: any): boolean {\n    return !isNaN(value - parseFloat(value));\n}\n
\n", + "upvotes": 4784, + "upvoterUsernames": [], + "downvotes": 1814, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc6", + "creator": "InsertNameHere", + "createdAt": 1275046226000, + "text": "

To me, this is the best way:

\n\n
isNumber : function(v){\n   return typeof v === 'number' && isFinite(v);\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283d082fcc3049e92731", + "creator": "Arman", + "createdAt": 1369067803000, + "text": "Unfortunately, this is kinda STRICT numeric checker that will fail for any string containing numeric letters only, like "0" etc...", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cc7", + "creator": "user532188", + "createdAt": 1291635006000, + "text": "

This should work. Some of the functions provided here are flawed, also should be faster than any other function here.

\n\n
        function isNumeric(n)\n        {\n            var n2 = n;\n            n = parseFloat(n);\n            return (n!='NaN' && n2==n);\n        }\n
\n\n

Explained:

\n\n

Create a copy of itself, then converts the number into float, then compares itself with the original number, if it is still a number, (whether integer or float) , and matches the original number, that means, it is indeed a number.

\n\n

It works with numeric strings as well as plain numbers. Does not work with hexadecimal numbers.

\n\n

Warning: use at your own risk, no guarantees.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc8", + "creator": "jberenguer", + "createdAt": 1294868483000, + "text": "

The following may work as well.

\n\n
function isNumeric(v) {\n         return v.length > 0 && !isNaN(v) && v.search(/[A-Z]|[#]/ig) == -1;\n   };\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cca", + "creator": "Manusoftar", + "createdAt": 1297468114000, + "text": "

My solution,

\n\n
function isNumeric(input) {\n    var number = /^\\-{0,1}(?:[0-9]+){0,1}(?:\\.[0-9]+){0,1}$/i;\n    var regex = RegExp(number);\n    return regex.test(input) && input.length>0;\n}\n
\n\n

It appears to work in every situation, but I might be wrong.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cc9", + "creator": "jayakumar", + "createdAt": 1296213779000, + "text": "
return (input - 0) == input && input.length > 0;\n
\n\n

didn't work for me. When I put in an alert and tested, input.length was undefined. I think there is no property to check integer length. So what I did was

\n\n
var temp = '' + input;\nreturn (input - 0) == input && temp.length > 0;\n
\n\n

It worked fine.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ccb", + "creator": "solidarius", + "createdAt": 1307708843000, + "text": "

An integer value can be verified by:

\n\n
function isNumeric(value) {\n    var bool = isNaN(+value));\n    bool = bool || (value.indexOf('.') != -1);\n    bool = bool || (value.indexOf(\",\") != -1);\n    return !bool;\n};\n
\n\n

This way is easier and faster! All tests are checked!

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ccc", + "creator": "Doctor Rudolf", + "createdAt": 1315492473000, + "text": "

@Zoltan Lengyel 'other locales' comment (Apr 26 at 2:14) in @CMS Dec answer (2 '09 at 5:36):

\n\n

I would recommend testing for typeof (n) === 'string':

\n\n
    function isNumber(n) {\n        if (typeof (n) === 'string') {\n            n = n.replace(/,/, \".\");\n        }\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n
\n\n

This extends Zoltans recommendation to not only be able to test \"localized numbers\" like isNumber('12,50') but also \"pure\" numbers like isNumber(2011).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ccd", + "creator": "Rafael", + "createdAt": 1332183618000, + "text": "

Well, I'm using this one I made...

\n\n

It's been working so far:

\n\n
function checkNumber(value) {\n    if ( value % 1 == 0 )\n        return true;\n    else\n        return false;\n}\n
\n\n

If you spot any problem with it, tell me, please.

\n\n

Like any numbers should be divisible by one with nothing left, I figured I could just use the module, and if you try dividing a string into a number the result wouldn't be that. So.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cce", + "creator": "Hans Schmucker", + "createdAt": 1339488565000, + "text": "

If I'm not mistaken, this should match any valid JavaScript number value, excluding constants (Infinity, NaN) and the sign operators +/- (because they are not actually part of the number as far as I concerned, they are separate operators):

\n\n

I needed this for a tokenizer, where sending the number to JavaScript for evaluation wasn't an option... It's definitely not the shortest possible regular expression, but I believe it catches all the finer subtleties of JavaScript's number syntax.

\n\n
/^(?:(?:(?:[1-9]\\d*|\\d)\\.\\d*|(?:[1-9]\\d*|\\d)?\\.\\d+|(?:[1-9]\\d*|\\d)) \n(?:[e]\\d+)?|0[0-7]+|0x[0-9a-f]+)$/i\n
\n\n

Valid numbers would include:

\n\n
 - 0\n - 00\n - 01\n - 10\n - 0e1\n - 0e01\n - .0\n - 0.\n - .0e1\n - 0.e1\n - 0.e00\n - 0xf\n - 0Xf\n
\n\n

Invalid numbers would be

\n\n
 - 00e1\n - 01e1\n - 00.0\n - 00x0\n - .\n - .e0\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ccf", + "creator": "Ali Gonabadi", + "createdAt": 1339849525000, + "text": "

I'm using simpler solution:

\n\n
function isNumber(num) {\n    return parseFloat(num).toString() == num\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283e082fcc3049e9273b", + "creator": "Janus Troelsen", + "createdAt": 1348870413000, + "text": "this will fail on anything with superfluous 0's in the end. example: "10.0"", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cd1", + "creator": "Kuf", + "createdAt": 1361178103000, + "text": "

Since jQuery 1.7, you can use jQuery.isNumeric():

\n\n
$.isNumeric('-1');      // true\n$.isNumeric('-1.5');    // true\n$.isNumeric('0');       // true\n$.isNumeric('0.42');    // true\n$.isNumeric('.42');     // true\n$.isNumeric('0x89f');   // true (valid hexa number)\n$.isNumeric('99,999');  // false\n$.isNumeric('#abcdef'); // false\n$.isNumeric('1.2.3');   // false\n$.isNumeric('');        // false\n$.isNumeric('blah');    // false\n
\n\n

Just note that unlike what you said, 0x89f is a valid number (hexa)

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd0", + "creator": "bob", + "createdAt": 1354162633000, + "text": "

Here I've collected the \"good ones\" from this page and put them into a simple test pattern for you to evaluate on your own.

\n\n

For newbies, the console.log is a built in function (available in all modern browsers) that lets you output results to the JavaScript console (dig around, you'll find it) rather than having to output to your HTML page.

\n\n
var isNumeric = function(val){\n    // --------------------------\n    // Recommended\n    // --------------------------\n\n    // jQuery - works rather well\n    // See CMS's unit test also: http://dl.getdropbox.com/u/35146/js/tests/isNumber.html\n    return !isNaN(parseFloat(val)) && isFinite(val);\n\n    // Aquatic - good and fast, fails the \"0x89f\" test, but that test is questionable.\n    //return parseFloat(val)==val;\n\n    // --------------------------\n    // Other quirky options\n    // --------------------------\n    // Fails on \"\", null, newline, tab negative.\n    //return !isNaN(val);\n\n    // user532188 - fails on \"0x89f\"\n    //var n2 = val;\n    //val = parseFloat(val);\n    //return (val!='NaN' && n2==val);\n\n    // Rafael - fails on negative + decimal numbers, may be good for isInt()?\n    // return ( val % 1 == 0 ) ? true : false;\n\n    // pottedmeat - good, but fails on stringy numbers, which may be a good thing for some folks?\n    //return /^-?(0|[1-9]\\d*|(?=\\.))(\\.\\d+)?$/.test(val);\n\n    // Haren - passes all\n    // borrowed from http://www.codetoad.com/javascript/isnumeric.asp\n    //var RE = /^-{0,1}\\d*\\.{0,1}\\d+$/;\n    //return RE.test(val);\n\n    // YUI - good for strict adherance to number type. Doesn't let stringy numbers through.\n    //return typeof val === 'number' && isFinite(val);\n\n    // user189277 - fails on \"\" and \"\\n\"\n    //return ( val >=0 || val < 0);\n}\n\nvar tests = [0, 1, \"0\", 0x0, 0x000, \"0000\", \"0x89f\", 8e5, 0x23, -0, 0.0, \"1.0\", 1.0, -1.5, 0.42, '075', \"01\", '-01', \"0.\", \".0\", \"a\", \"a2\", true, false, \"#000\", '1.2.3', '#abcdef', '', \"\", \"\\n\", \"\\t\", '-', null, undefined];\n\nfor (var i=0; i<tests.length; i++){\n    console.log( \"test \" + i + \":    \" + tests[i] + \"    \\t   \" + isNumeric(tests[i]) );\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd2", + "creator": "NaveenKumar1410", + "createdAt": 1361936327000, + "text": "

knockoutJs Inbuild library validation functions

\n\n

By extending it the field get validated

\n\n

1) number

\n\n

self.number = ko.observable(numberValue).extend({ number: true});

\n\n

TestCase

\n\n
numberValue = '0.0'    --> true\nnumberValue = '0'      --> true\nnumberValue = '25'     --> true\nnumberValue = '-1'     --> true\nnumberValue = '-3.5'   --> true\nnumberValue = '11.112' --> true\nnumberValue = '0x89f'  --> false\nnumberValue = ''       --> false\nnumberValue = 'sfsd'   --> false\nnumberValue = 'dg##$'  --> false\n
\n\n

2) digit

\n\n

self.number = ko.observable(numberValue).extend({ digit: true});

\n\n

TestCase

\n\n
numberValue = '0'      --> true\nnumberValue = '25'     --> true\nnumberValue = '0.0'    --> false\nnumberValue = '-1'     --> false\nnumberValue = '-3.5'   --> false\nnumberValue = '11.112' --> false\nnumberValue = '0x89f'  --> false\nnumberValue = ''       --> false\nnumberValue = 'sfsd'   --> false\nnumberValue = 'dg##$'  --> false\n
\n\n

3) min and max

\n\n

self.number = ko.observable(numberValue).extend({ min: 5}).extend({ max: 10});

\n\n

This field accept value between 5 and 10 only

\n\n

TestCase

\n\n
numberValue = '5'    --> true\nnumberValue = '6'    --> true\nnumberValue = '6.5'  --> true\nnumberValue = '9'    --> true\nnumberValue = '11'   --> false\nnumberValue = '0'    --> false\nnumberValue = ''    --> false\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd3", + "creator": "Phil", + "createdAt": 1365934458000, + "text": "

@CMS' answer: Your snippet failed on whitespace cases on my machine using nodejs. So I combined it with \n@joel's answer to the following:

\n\n
is_float = function(v) {\n    return !isNaN(v) && isFinite(v) &&\n        (typeof(v) == 'number' || v.replace(/^\\s+|\\s+$/g, '').length > 0);\n}\n
\n\n

I unittested it with those cases that are floats:

\n\n
var t = [\n        0,\n        1.2123,\n        '0',\n        '2123.4',\n        -1,\n        '-1',\n        -123.423,\n        '-123.432',\n        07,\n        0xad,\n        '07',\n        '0xad'\n    ];\n
\n\n

and those cases that are no floats (including empty whitespaces and objects / arrays):

\n\n
    var t = [\n        'hallo',\n        [],\n        {},\n        'jklsd0',\n        '',\n        \"\\t\",\n        \"\\n\",\n        ' '\n    ];\n
\n\n

Everything works as expected here. Maybe this helps.

\n\n

Full source code for this can be found here.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd5", + "creator": "hobs", + "createdAt": 1370561945000, + "text": "

Only problem I had with @CMS's answer is the exclusion of NaN and Infinity, which are useful numbers for many situations. One way to check for NaN's is to check for numeric values that don't equal themselves, NaN != NaN! So there are really 3 tests you'd like to deal with ...

\n\n
function isNumber(n) {\n  n = parseFloat(n);\n  return !isNaN(n) || n != n;\n}\nfunction isFiniteNumber(n) {\n  n = parseFloat(n);\n  return !isNaN(n) && isFinite(n);\n}    \nfunction isComparableNumber(n) {\n  n = parseFloat(n);\n  return (n >=0 || n < 0);\n}\n\nisFiniteNumber('NaN')\nfalse\nisFiniteNumber('OxFF')\ntrue\nisNumber('NaN')\ntrue\nisNumber(1/0-1/0)\ntrue\nisComparableNumber('NaN')\nfalse\nisComparableNumber('Infinity')\ntrue\n
\n\n

My isComparableNumber is pretty close to another elegant answer, but handles hex and other string representations of numbers.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd6", + "creator": "Mr Br", + "createdAt": 1373411444000, + "text": "

I found simple solution, probably not best but it's working fine :)

\n\n

So, what I do is next, I parse string to Int and check if length size of new variable which is now int type is same as length of original string variable. Logically if size is the same it means string is fully parsed to int and that is only possible if string is \"made\" only of numbers.

\n\n
var val=1+$(e).val()+'';\nvar n=parseInt(val)+'';\nif(val.length == n.length )alert('Is int');\n
\n\n

You can easily put that code in function and instead of alert use return true if int.\nRemember, if you use dot or comma in string you are checking it's still false cos you are parsing to int.

\n\n

Note: Adding 1+ on e.val so starting zero wouldn't be removed.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd7", + "creator": "Aaron Gong", + "createdAt": 1380303140000, + "text": "

I have run the following below and it passes all the test cases...

\n\n

It makes use of the different way in which parseFloat and Number handle their inputs...

\n\n
function IsNumeric(_in) {\n    return (parseFloat(_in) === Number(_in) && Number(_in) !== NaN);\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283e082fcc3049e92743", + "creator": "Michael Haren", + "createdAt": 1380303235000, + "text": "I haven't tried this, but just a tip: you can reduce that to just return the if expression, e.g. return parseFloat...", + "upvotes": 1183, + "upvoterUsernames": [], + "downvotes": 1183, + "downvoterUsernames": [] + }, + { + "_id": "62f3283e082fcc3049e92744", + "creator": "Alexis Wilke", + "createdAt": 1418686707000, + "text": "This is wrong, you cannot compare against NaN with ==, ===, !=, or !==, it always returns false.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cd4", + "creator": "Arman", + "createdAt": 1369068498000, + "text": "

Here's a lil bit improved version (probably the fastest way out there) that I use instead of exact jQuery's variant, I really don't know why don't they use this one:

\n\n
function isNumeric(val) {\n    return !isNaN(+val) && isFinite(val);\n}\n
\n\n

The downside of jQuery's version is that if you pass a string with leading numerics and trailing letters like \"123abc\" the parseFloat | parseInt will extract the numeric fraction out and return 123, BUT, the second guard isFinite will fail it anyway.\nWith the unary + operator it will die on the very first guard since + throws NaN for such hybrids :)\nA little performance yet I think a solid semantic gain.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd8", + "creator": "daniel1426", + "createdAt": 1387576915000, + "text": "

The following seems to works fine for many cases:

\n\n
function isNumeric(num) {\n    return (num > 0 || num === 0 || num === '0' || num < 0) && num !== true && isFinite(num);\n}\n
\n\n

This is built on top of this answer (which is for this answer too):\nhttps://stackoverflow.com/a/1561597/1985601

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cd9", + "creator": "Sean the Bean", + "createdAt": 1389631370000, + "text": "

I realize the original question did not mention jQuery, but if you do use jQuery, you can do:

\n\n
$.isNumeric(val)\n
\n\n

Simple.

\n\n

https://api.jquery.com/jQuery.isNumeric/ (as of jQuery 1.7)

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cda", + "creator": "donquixote", + "createdAt": 1397431729000, + "text": "

I realize this has been answered many times, but the following is a decent candidate which can be useful in some scenarios.

\n\n

it should be noted that it assumes that '.42' is NOT a number, and '4.' is NOT a number, so this should be taken into account.

\n\n
function isDecimal(x) {\n  return '' + x === '' + +x;\n}\n\nfunction isInteger(x) {\n  return '' + x === '' + parseInt(x);\n}\n
\n\n

The isDecimal passes the following test:

\n\n
function testIsNumber(f) {\n  return f('-1') && f('-1.5') && f('0') && f('0.42')\n    && !f('.42') && !f('99,999') && !f('0x89f')\n    && !f('#abcdef') && !f('1.2.3') && !f('') && !f('blah');\n}\n
\n\n

The idea here is that every number or integer has one \"canonical\" string representation, and every non-canonical representation should be rejected. So we cast to a number and back, and see if the result is the original string.

\n\n

Whether these functions are useful for you depends on the use case. One feature is that distinct strings represent distinct numbers (if both pass the isNumber() test).

\n\n

This is relevant e.g. for numbers as object property names.

\n\n
var obj = {};\nobj['4'] = 'canonical 4';\nobj['04'] = 'alias of 4';\nobj[4];  // prints 'canonical 4' to the console.\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cdb", + "creator": "Nik", + "createdAt": 1410845527000, + "text": "

I use this way to chack that varible is numeric:

\n\n
v * 1 == v\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283f082fcc3049e92749", + "creator": "jkdev", + "createdAt": 1436917518000, + "text": "Problem: false * 1 == false evaluates to true.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cdc", + "creator": "Simon Hi", + "createdAt": 1418577193000, + "text": "
function isNumber(n) {\n    return (n===n+''||n===n-0) && n*0==0 && /\\S/.test(n);\n}\n
\n\n

Explanations:

\n\n

(n===n-0||n===n+'') verifies if n is a number or a string (discards arrays, boolean, date, null, ...). You can replace (n===n-0||n===n+'') by n!==undefined && n!==null && (n.constructor===Number||n.constructor===String): significantly faster but less concise.

\n\n

n*0==0 verifies if n is a finite number as isFinite(n) does. If you need to check strings that represent negative hexadecimal, just replace n*0==0 by something like n.toString().replace(/^\\s*-/,'')*0==0.
\nIt costs a little of course, so if you don't need it, don't use it.

\n\n

/\\S/.test(n) discards empty strings or strings, that contain only white-spaces (necessary since isFinite(n) or n*0==0 return a false positive in this case). You can reduce the number of call to .test(n) by using (n!=0||/0/.test(n)) instead of /\\S/.test(n), or you can use a slightly faster but less concise test such as (n!=0||(n+'').indexOf('0')>=0): tiny improvement.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cdd", + "creator": "John", + "createdAt": 1419345492000, + "text": "

None of the answers return false for empty strings, a fix for that...

\n\n
function is_numeric(n)\n{\n return (n != '' && !isNaN(parseFloat(n)) && isFinite(n));\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cde", + "creator": "Dmitry Sheiko", + "createdAt": 1452781515000, + "text": "

One can use a type-check library like https://github.com/arasatasaygin/is.js or just extract a check snippet from there (https://github.com/arasatasaygin/is.js/blob/master/is.js#L131):

\n\n
is.nan = function(value) {    // NaN is number :) \n  return value !== value;\n};\n // is a given value number?\nis.number = function(value) {\n    return !is.nan(value) && Object.prototype.toString.call(value) === '[object Number]';\n};\n
\n\n

In general if you need it to validate parameter types (on entry point of function call), you can go with JSDOC-compliant contracts (https://www.npmjs.com/package/bycontract):

\n\n
/**\n * This is JSDOC syntax\n * @param {number|string} sum\n * @param {Object.<string, string>} payload\n * @param {function} cb\n */\nfunction foo( sum, payload, cb ) {\n  // Test if the contract is respected at entry point\n  byContract( arguments, [ \"number|string\", \"Object.<string, string>\", \"function\" ] );\n}\n// Test it\nfoo( 100, { foo: \"foo\" }, function(){}); // ok\nfoo( 100, { foo: 100 }, function(){}); // exception\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce0", + "creator": "Shishir Arora", + "createdAt": 1459368607000, + "text": "

isNumeric=(el)=>{return Boolean(parseFloat(el)) && isFinite(el)}

\n\n

Nothing very different but we can use Boolean constructor

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cdf", + "creator": "studio-klik", + "createdAt": 1455133259000, + "text": "

If you need to validate a special set of decimals y\nyou can use this simple javascript:

\n\n

http://codesheet.org/codesheet/x1kI7hAD

\n\n
<input type=\"text\" name=\"date\" value=\"\" pattern=\"[0-9]){1,2}(\\.){1}([0-9]){2}\" maxlength=\"6\" placeholder=\"od npr.: 16.06\" onchange=\"date(this);\" />\n
\n\n

The Javascript:

\n\n
function date(inputField) {        \n  var isValid = /^([0-9]){1,2}(\\.){1}([0-9]){2}$/.test(inputField.value);   \n  if (isValid) {\n    inputField.style.backgroundColor = '#bfa';\n  } else {\n    inputField.style.backgroundColor = '#fba';\n  }\n  return isValid;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce1", + "creator": "adius", + "createdAt": 1460310838000, + "text": "

To check if a variable contains a valid number and not\njust a String which looks like a number,\nNumber.isFinite(value) can be used.

\n\n

This is part of the language since\nES2015

\n\n

Examples:

\n\n
Number.isFinite(Infinity)   // false\nNumber.isFinite(NaN)        // false\nNumber.isFinite(-Infinity)  // false\n\nNumber.isFinite(0)          // true\nNumber.isFinite(2e64)       // true\n\nNumber.isFinite('0')        // false\nNumber.isFinite(null)       // false\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce2", + "creator": "Syed Nasir Abbas", + "createdAt": 1463683887000, + "text": "
function isNumeric(n) {\n    var isNumber = true;\n\n    $.each(n.replace(/ /g,'').toString(), function(i, v){\n        if(v!=',' && v!='.' && v!='-'){\n            if(isNaN(v)){\n               isNumber = false;\n               return false;\n            }\n         }\n     });\n\n    return isNumber;\n}\n\nisNumeric(-3,4567.89);   // true <br>\n\nisNumeric(3,4567.89);   // true <br>\n\nisNumeric(\"-3,4567.89\");   // true <br>\n\nisNumeric(3d,4567.89);   // false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce3", + "creator": "paulalexandru", + "createdAt": 1463986220000, + "text": "

Best way to do this is like this:

\n\n
function isThisActuallyANumber(data){\n    return ( typeof data === \"number\" && !isNaN(data) );\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce5", + "creator": "chrmcpn", + "createdAt": 1470860235000, + "text": "
function inNumeric(n){\n   return Number(n).toString() === n;\n}\n
\n\n

If n is numeric Number(n) will return the numeric value and toString() will turn it back to a string. But if n isn't numeric Number(n) will return NaN so it won't match the original n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32840082fcc3049e9274f", + "creator": "vinoth", + "createdAt": 1653483283000, + "text": ""1.20" is converted as number to 1.2. So in this case this doesn't work", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90ce4", + "creator": "John Mikic", + "createdAt": 1466618246000, + "text": "

I think parseFloat function can do all the work here. The function below passes all the tests on this page including isNumeric(Infinity) == true:

\n\n
function isNumeric(n) {\n\n    return parseFloat(n) == n;\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce7", + "creator": "Saurabh Chandra Patel", + "createdAt": 1486105881000, + "text": "
$('.rsval').bind('keypress', function(e){  \n        var asciiCodeOfNumbers = [48,46, 49, 50, 51, 52, 53, 54, 54, 55, 56, 57];\n        var keynum = (!window.event) ? e.which : e.keyCode; \n        var splitn = this.value.split(\".\"); \n        var decimal = splitn.length;\n        var precision = splitn[1]; \n        if(decimal == 2 && precision.length >= 2  ) { console.log(precision , 'e');   e.preventDefault(); } \n        if( keynum == 46 ){  \n            if(decimal > 2) { e.preventDefault(); }  \n        } \n        if ($.inArray(keynum, asciiCodeOfNumbers) == -1)\n            e.preventDefault();    \n  });\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce6", + "creator": "smsmware", + "createdAt": 1483824243000, + "text": "

I think my code is perfect ...

\n\n

\r\n
\r\n
/**\r\n * @param {string} s\r\n * @return {boolean}\r\n */\r\nvar isNumber = function(s) {\r\n    return s.trim()!==\"\" && !isNaN(Number(s));\r\n};
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce8", + "creator": "Vixed", + "createdAt": 1487861779000, + "text": "

You can minimize this function in a lot of way, and you can also implement it with a custom regex for negative values or custom charts:

\n\n
$('.number').on('input',function(){\n    var n=$(this).val().replace(/ /g,'').replace(/\\D/g,'');\n    if (!$.isNumeric(n))\n        $(this).val(n.slice(0, -1))\n    else\n        $(this).val(n)\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cea", + "creator": "Mhmdrz_A", + "createdAt": 1548535672000, + "text": "

A simple and clean solution by leveraging language's dynamic type checking:

\n\n
function IsNumeric (string) {\n   if(string === ' '.repeat(string.length)){\n     return false\n   }\n   return string - 0 === string * 1\n}\n\n
\n\n

if you don't care about white-spaces you can remove that \" if \"

\n\n

see test cases below

\n\n

\r\n
\r\n
function IsNumeric (string) {\r\n   if(string === ' '.repeat(string.length)){\r\n      return false\r\n   }\r\n   return string - 0 === string * 1\r\n}\r\n\r\n\r\nconsole.log('-1' + ' → ' + IsNumeric('-1'))    \r\nconsole.log('-1.5' + ' → ' + IsNumeric('-1.5')) \r\nconsole.log('0' + ' → ' + IsNumeric('0'))     \r\nconsole.log('0.42' + ' → ' + IsNumeric('0.42'))   \r\nconsole.log('.42' + ' → ' + IsNumeric('.42'))    \r\nconsole.log('99,999' + ' → ' + IsNumeric('99,999'))\r\nconsole.log('0x89f' + ' → ' + IsNumeric('0x89f'))  \r\nconsole.log('#abcdef' + ' → ' + IsNumeric('#abcdef'))\r\nconsole.log('1.2.3' + ' → ' + IsNumeric('1.2.3')) \r\nconsole.log('' + ' → ' + IsNumeric(''))    \r\nconsole.log('33 ' + ' → ' + IsNumeric('33 '))
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ce9", + "creator": "Alston", + "createdAt": 1509344757000, + "text": "

No need to use extra lib.

\n\n
const IsNumeric = (...numbers) => {\n  return numbers.reduce((pre, cur) => pre && !!(cur === 0 || +cur), true);\n};\n
\n\n

Test

\n\n
> IsNumeric(1)\ntrue\n> IsNumeric(1,2,3)\ntrue\n> IsNumeric(1,2,3,0)\ntrue\n> IsNumeric(1,2,3,0,'')\nfalse\n> IsNumeric(1,2,3,0,'2')\ntrue\n> IsNumeric(1,2,3,0,'200')\ntrue\n> IsNumeric(1,2,3,0,'-200')\ntrue\n> IsNumeric(1,2,3,0,'-200','.32')\ntrue\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ceb", + "creator": "MarredCheese", + "createdAt": 1569384784000, + "text": "

Here's a dead-simple one (tested in Chrome, Firefox, and IE):

\n\n
function isNumeric(x) {\n  return parseFloat(x) == x;\n}\n
\n\n

Test cases from question:

\n\n
console.log('trues');\nconsole.log(isNumeric('-1'));\nconsole.log(isNumeric('-1.5'));\nconsole.log(isNumeric('0'));\nconsole.log(isNumeric('0.42'));\nconsole.log(isNumeric('.42'));\n\nconsole.log('falses');\nconsole.log(isNumeric('99,999'));\nconsole.log(isNumeric('0x89f'));\nconsole.log(isNumeric('#abcdef'));\nconsole.log(isNumeric('1.2.3'));\nconsole.log(isNumeric(''));\nconsole.log(isNumeric('blah'));\n
\n\n

Some more test cases:

\n\n
console.log('trues');\nconsole.log(isNumeric(0));\nconsole.log(isNumeric(-1));\nconsole.log(isNumeric(-500));\nconsole.log(isNumeric(15000));\nconsole.log(isNumeric(0.35));\nconsole.log(isNumeric(-10.35));\nconsole.log(isNumeric(2.534e25));\nconsole.log(isNumeric('2.534e25'));\nconsole.log(isNumeric('52334'));\nconsole.log(isNumeric('-234'));\nconsole.log(isNumeric(Infinity));\nconsole.log(isNumeric(-Infinity));\nconsole.log(isNumeric('Infinity'));\nconsole.log(isNumeric('-Infinity'));\n\nconsole.log('falses');\nconsole.log(isNumeric(NaN));\nconsole.log(isNumeric({}));\nconsole.log(isNumeric([]));\nconsole.log(isNumeric(''));\nconsole.log(isNumeric('one'));\nconsole.log(isNumeric(true));\nconsole.log(isNumeric(false));\nconsole.log(isNumeric());\nconsole.log(isNumeric(undefined));\nconsole.log(isNumeric(null));\nconsole.log(isNumeric('-234aa'));\n
\n\n

Note that it considers infinity a number.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ced", + "creator": "Mykola Uspalenko", + "createdAt": 1656686794000, + "text": "

Need to check for the null/undefined condition and remove commas (for the US number format) if typeof n === 'string'.

\n
function isNumeric(n)\n{\n    if(n === null || typeof n === 'undefined')\n         return false;\n\n    if(typeof n === 'string')\n        n = n.split(',').join('');\n\n    return !isNaN(parseFloat(n)) && isFinite(n);\n}\n
\n

https://jsfiddle.net/NickU/nyzeot03/3/

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cec", + "creator": "chickens", + "createdAt": 1644716290000, + "text": "

With regex we can cover all the cases ask in the question. Here it is:

\n

isNumeric for all integers and decimals:

\n
const isNumeric = num => /^-?[0-9]+(?:\\.[0-9]+)?$/.test(num+'');\n
\n

isInteger for just integers:

\n
const isInteger = num => /^-?[0-9]+$/.test(num+'');\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321c9082fcc3049e90c5c", + "creator": "Calmarius", + "createdAt": 1297866565000, + "text": "Decimal comma is the standard in entire Europe and Russia (except UK)", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f321c9082fcc3049e90c5d", + "creator": "user2950593", + "createdAt": 1387971097000, + "text": "function isnum( num) { return res = ( num / num) ? true : false; }", + "upvotes": 766, + "upvoterUsernames": [], + "downvotes": 766, + "downvoterUsernames": [] + }, + { + "_id": "62f321c9082fcc3049e90c5e", + "creator": "Eric F.", + "createdAt": 1417146499000, + "text": "Becareful 99,999.9999 is valid numeric in Thailand.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c9082fcc3049e90c5f", + "creator": "Henry Heleine", + "createdAt": 1418162947000, + "text": "Same in the UK @EricF.", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1551670, + "uvac": 1551720 + } + }, + { + "_id": "62f321bb082fcc3049e8fed9", + "title": "Detecting an undefined object property", + "title-lowercase": "detecting an undefined object property", + "creator": "Matt Sheppard", + "createdAt": 1219735508000, + "status": "open", + "text": "

What's the best way of checking if an object property in JavaScript is undefined?

\n", + "upvotes": 5670, + "upvoterUsernames": [], + "downvotes": 2539, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1410943, + "answers": 46, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e9088a", + "creator": "tslocum", + "createdAt": 1219735677000, + "text": "
if (somevariable == undefined) {\n  alert('the variable is not defined!');\n}\n
\n\n

You can also make it into a function, as shown here:

\n\n
function isset(varname){\n  return(typeof(window[varname]) != 'undefined');\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e9200e", + "creator": "Denis", + "createdAt": 1377608972000, + "text": "what about local variables? strange trick with window[variable]", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9088b", + "creator": "Pandincus", + "createdAt": 1219736173000, + "text": "

In JavaScript there is null and there is undefined. They have different meanings.

\n\n

Marijn Haverbeke states, in his free, online book "Eloquent JavaScript" (emphasis mine):

\n
\n

There is also a similar value, null, whose meaning is 'this value is defined, but it does not have a value'. The difference in meaning between undefined and null is mostly academic, and usually not very interesting. In practical programs, it is often necessary to check whether something 'has a value'. In these cases, the expression something == undefined may be used, because, even though they are not exactly the same value, null == undefined will produce true.

\n
\n

So, I guess the best way to check if something was undefined would be:

\n
if (something == undefined)\n
\n

Object properties should work the same way.

\n
var person = {\n    name: "John",\n    age: 28,\n    sex: "male"\n};\n\nalert(person.name); // "John"\nalert(person.fakeVariable); // undefined\n
\n", + "upvotes": 192, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e92013", + "creator": "Sebastian Rittau", + "createdAt": 1259574434000, + "text": "if (something == undefined) is better written as if (something === undefined)", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92015", + "creator": "Morgan Cheng", + "createdAt": 1271819071000, + "text": "if something is an undefined global variable, (something == undefined) brings up javascript error.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92017", + "creator": "Andrew", + "createdAt": 1305831041000, + "text": "The problem with this is that if var a = null then a == undefined evaluates to true, even though a is most certainly defined.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92018", + "creator": "juhan_h", + "createdAt": 1308036086000, + "text": "if (something === undefined) is not working for me with ie7 or ie8 if (typeof something == "undefined") is.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9088c", + "creator": "Ricky", + "createdAt": 1219754302000, + "text": "

The solution is incorrect. In JavaScript,

\n\n
null == undefined\n
\n\n

will return true, because they both are \"casted\" to a boolean and are false. The correct way would be to check

\n\n
if (something === undefined)\n
\n\n

which is the identity operator...

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e9201b", + "creator": "Simon East", + "createdAt": 1308097355000, + "text": "Wrong. This throws an error if the variable has not been created. Should not be voted up. Use typeof instead.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e9201c", + "creator": "Peter Mortensen", + "createdAt": 1595633663000, + "text": "What solution? Can you link directly to it?", + "upvotes": 345, + "upvoterUsernames": [], + "downvotes": 345, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9088e", + "creator": "Rixius", + "createdAt": 1278968063000, + "text": "
function isUnset(inp) {\n  return (typeof inp === 'undefined')\n}\n
\n\n

Returns false if variable is set, and true if is undefined.

\n\n

Then use:

\n\n
if (isUnset(var)) {\n  // initialize variable here\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9088d", + "creator": "Erwin", + "createdAt": 1231244861000, + "text": "

The usual way to check if the value of a property is the special value undefined, is:

\n
if(o.myProperty === undefined) {\n  alert("myProperty value is the special value `undefined`");\n}\n
\n

To check if an object does not actually have such a property, and will therefore return undefined by default when you try to access it:

\n
if(!o.hasOwnProperty('myProperty')) {\n  alert("myProperty does not exist");\n}\n
\n

To check if the value associated with an identifier is the special value undefined, or if that identifier has not been declared:

\n
if(typeof myVariable === 'undefined') {\n  alert('myVariable is either the special value `undefined`, or it has not been declared');\n}\n
\n

Note: this last method is the only way to refer to an undeclared identifier without an early error, which is different from having a value of undefined.

\n

In versions of JavaScript prior to ECMAScript 5, the property named "undefined" on the global object was writeable, and therefore a simple check foo === undefined might behave unexpectedly if it had accidentally been redefined. In modern JavaScript, the property is read-only.

\n

However, in modern JavaScript, "undefined" is not a keyword, and so variables inside functions can be named "undefined" and shadow the global property.

\n

If you are worried about this (unlikely) edge case, you can use the void operator to get at the special undefined value itself:

\n
if(myVariable === void 0) {\n  alert("myVariable is the special value `undefined`");\n}\n
\n", + "upvotes": 3354, + "upvoterUsernames": [], + "downvotes": 469, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32633082fcc3049e9201f", + "creator": "user1032531", + "createdAt": 1513255300000, + "text": "@Ryan what "other answer"?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e92021", + "creator": "Ry-", + "createdAt": 1513281740000, + "text": "@user1032531: I’d recommend mine", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e92022", + "creator": "Anjana Silva", + "createdAt": 1599754127000, + "text": "One thumbs-up is not certainly enough. Thank you.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e92024", + "creator": "Ray Foss", + "createdAt": 1612475588000, + "text": "grrr im just gonna typeof window !== typeof undefined... no chance of misspelling undefined and works on the global scope", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e92025", + "creator": "AlejandroVD", + "createdAt": 1629735766000, + "text": "eslint advices against using hasOwnProperty. Use the in operator, see recent answers as examples.", + "upvotes": 5283, + "upvoterUsernames": [], + "downvotes": 5283, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9088f", + "creator": "Kevin", + "createdAt": 1280246632000, + "text": "
if ( typeof( something ) == \"undefined\") \n
\n\n

This worked for me while the others didn't.

\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32633082fcc3049e92028", + "creator": "aehlke", + "createdAt": 1281439329000, + "text": "parens are unnecessary since typeof is an operator", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e9202a", + "creator": "Abhi Beckert", + "createdAt": 1346891296000, + "text": "But they make it clearer what is being checked. Otherwise it might be read as typeof (something == "undefined").", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90891", + "creator": "Eric", + "createdAt": 1285165218000, + "text": "

I'm not sure where the origin of using === with typeof came from, and as a convention I see it used in many libraries, but the typeof operator returns a string literal, and we know that up front, so why would you also want to type check it too?

\n\n
typeof x;                      // some string literal \"string\", \"object\", \"undefined\"\nif (typeof x === \"string\") {   // === is redundant because we already know typeof returns a string literal\nif (typeof x == \"string\") {    // sufficient\n
\n", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32633082fcc3049e9202c", + "creator": "Simon East", + "createdAt": 1309331780000, + "text": "Great point Eric. Is there a performance hit from checking type also?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e9202d", + "creator": "svidgen", + "createdAt": 1372431275000, + "text": "== is one less character than === :)", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90890", + "creator": "MarkPflug", + "createdAt": 1282586598000, + "text": "

I believe there are a number of incorrect answers to this topic. Contrary to common belief, \"undefined\" is not a keyword in JavaScript and can in fact have a value assigned to it.

\n\n

Correct Code

\n\n

The most robust way to perform this test is:

\n\n
if (typeof myVar === \"undefined\")\n
\n\n

This will always return the correct result, and even handles the situation where myVar is not declared.

\n\n

Degenerate code. DO NOT USE.

\n\n
var undefined = false;  // Shockingly, this is completely legal!\nif (myVar === undefined) {\n    alert(\"You have been misled. Run away!\");\n}\n
\n\n

Additionally, myVar === undefined will raise an error in the situation where myVar is undeclared.

\n", + "upvotes": 1063, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32633082fcc3049e92030", + "creator": "Kevin Meredith", + "createdAt": 1373041072000, + "text": "What about if(typeof myVar != "undefined"). Is the "!=" acceptable?", + "upvotes": 639, + "upvoterUsernames": [], + "downvotes": 639, + "downvoterUsernames": [] + }, + { + "_id": "62f32633082fcc3049e92032", + "creator": "rojobuffalo", + "createdAt": 1421435885000, + "text": "fiddle for demonstration of this point", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90892", + "creator": "Michael Anderson", + "createdAt": 1307505889000, + "text": "

The issue boils down to three cases:

\n\n
    \n
  1. The object has the property and its value is not undefined.
  2. \n
  3. The object has the property and its value is undefined.
  4. \n
  5. The object does not have the property.
  6. \n
\n\n

This tells us something I consider important:

\n\n

There is a difference between an undefined member and a defined member with an undefined value.

\n\n

But unhappily typeof obj.foo does not tell us which of the three cases we have. However we can combine this with \"foo\" in obj to distinguish the cases.

\n\n
                               |  typeof obj.x === 'undefined' | !(\"x\" in obj)\n1.                     { x:1 } |  false                        | false\n2.    { x : (function(){})() } |  true                         | false\n3.                          {} |  true                         | true\n
\n\n

Its worth noting that these tests are the same for null entries too

\n\n
                               |  typeof obj.x === 'undefined' | !(\"x\" in obj)\n                    { x:null } |  false                        | false\n
\n\n

I'd argue that in some cases it makes more sense (and is clearer) to check whether the property is there, than checking whether it is undefined, and the only case where this check will be different is case 2, the rare case of an actual entry in the object with an undefined value.

\n\n

For example: I've just been refactoring a bunch of code that had a bunch of checks whether an object had a given property.

\n\n
if( typeof blob.x != 'undefined' ) {  fn(blob.x); }\n
\n\n

Which was clearer when written without a check for undefined.

\n\n
if( \"x\" in blob ) { fn(blob.x); }\n
\n\n

But as has been mentioned these are not exactly the same (but are more than good enough for my needs).

\n", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90893", + "creator": "Codebeat", + "createdAt": 1313160024000, + "text": "

If you do

\n\n
if (myvar == undefined )\n{ \n    alert('var does not exists or is not initialized');\n}\n
\n\n

it will fail when the variable myvar does not exists, because myvar is not defined, so the script is broken and the test has no effect.

\n\n

Because the window object has a global scope (default object) outside a function, a declaration will be 'attached' to the window object.

\n\n

For example:

\n\n
var myvar = 'test';\n
\n\n

The global variable myvar is the same as window.myvar or window['myvar']

\n\n

To avoid errors to test when a global variable exists, you better use:

\n\n
if(window.myvar == undefined )\n{ \n    alert('var does not exists or is not initialized');\n}\n
\n\n

The question if a variable really exists doesn't matter, its value is incorrect. Otherwise, it is silly to initialize variables with undefined, and it is better use the value false to initialize. When you know that all variables that you declare are initialized with false, you can simply check its type or rely on !window.myvar to check if it has a proper/valid value. So even when the variable is not defined then !window.myvar is the same for myvar = undefined or myvar = false or myvar = 0.

\n\n

When you expect a specific type, test the type of the variable. To speed up testing a condition you better do:

\n\n
if( !window.myvar || typeof window.myvar != 'string' )\n{\n    alert('var does not exists or is not type of string');\n}\n
\n\n

When the first and simple condition is true, the interpreter skips the next tests.

\n\n

It is always better to use the instance/object of the variable to check if it got a valid value. It is more stable and is a better way of programming.

\n\n

(y)

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90894", + "creator": "Anoop", + "createdAt": 1318850572000, + "text": "

You can get an array all undefined with path using the following code.

\n\n
 function getAllUndefined(object) {\n\n        function convertPath(arr, key) {\n            var path = \"\";\n            for (var i = 1; i < arr.length; i++) {\n\n                path += arr[i] + \"->\";\n            }\n            path += key;\n            return path;\n        }\n\n\n        var stack = [];\n        var saveUndefined= [];\n        function getUndefiend(obj, key) {\n\n            var t = typeof obj;\n            switch (t) {\n                case \"object\":\n                    if (t === null) {\n                        return false;\n                    }\n                    break;\n                case \"string\":\n                case \"number\":\n                case \"boolean\":\n                case \"null\":\n                    return false;\n                default:\n                    return true;\n            }\n            stack.push(key);\n            for (k in obj) {\n                if (obj.hasOwnProperty(k)) {\n                    v = getUndefiend(obj[k], k);\n                    if (v) {\n                        saveUndefined.push(convertPath(stack, k));\n                    }\n                }\n            }\n            stack.pop();\n\n        }\n\n        getUndefiend({\n            \"\": object\n        }, \"\");\n        return saveUndefined;\n    }\n
\n\n

jsFiddle link

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32633082fcc3049e92036", + "creator": "icktoofay", + "createdAt": 1368500541000, + "text": "While it won't affect the validity of your code, you've got a typo: getUndefiend should be getUndefined.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90895", + "creator": "Corey Richardson", + "createdAt": 1329582404000, + "text": "

Object.hasOwnProperty(o, 'propertyname');

\n\n

This doesn't look up through the prototype chain, however.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90896", + "creator": "Joe Johnson", + "createdAt": 1348598494000, + "text": "

I didn't see (hope I didn't miss it) anyone checking the object before the property. So, this is the shortest and most effective (though not necessarily the most clear):

\n
if (obj && obj.prop) {\n  // Do something;\n}\n
\n

If the obj or obj.prop is undefined, null, or "falsy", the if statement will not execute the code block. This is usually the desired behavior in most code block statements (in JavaScript).

\n

UPDATE: (7/2/2021)

\n

The latest version of JavaScript introduces a new operator for\noptional chaining: ?.

\n

This is probably going to be the most explicit and efficient method of checking for the existence of object properties, moving forward.

\n

Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32633082fcc3049e92039", + "creator": "NikoKyriakid", + "createdAt": 1532080168000, + "text": "I believe the question is for checking against undefined explicitly. Your condition check against all false values of JS.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90897", + "creator": "wayneseymour", + "createdAt": 1376574996000, + "text": "

Here is my situation:

\n

I am using the result of a REST call. The result should be parsed from JSON to a JavaScript object.

\n

There is one error I need to defend. If the arguments to the REST call were incorrect as far as the user specifying the arguments wrong, the REST call comes back basically empty.

\n

While using this post to help me defend against this, I tried this:

\n
if( typeof restResult.data[0] === "undefined" ) { throw  "Some error"; }\n
\n

For my situation, if restResult.data[0] === "object", then I can safely start inspecting the rest of the members. If undefined then throw the error as above.

\n

What I am saying is that for my situation, all the previous suggestions in this post did not work. I'm not saying I'm right and everyone is wrong. I am not a JavaScript master at all, but hopefully this will help someone.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e9203c", + "creator": "Headbank", + "createdAt": 1551368189000, + "text": "In your case you could more simply check if the array is empty: if(!restResult.data.length) { throw "Some error"; }", + "upvotes": 850, + "upvoterUsernames": [], + "downvotes": 850, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90898", + "creator": "Marthijn", + "createdAt": 1387449887000, + "text": "

In the article Exploring the Abyss of Null and Undefined in JavaScript I read that frameworks like Underscore.js use this function:

\n\n
function isUndefined(obj){\n    return obj === void 0;\n}\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e9203f", + "creator": "Stijn de Witt", + "createdAt": 1446345705000, + "text": "isUndefined(obj): 16 chars. obj === void 0: 14 chars. 'nough said.", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9089a", + "creator": "DenisS", + "createdAt": 1392049565000, + "text": "

'if (window.x) { }' is error safe

\n\n

Most likely you want if (window.x). This check is safe even if x hasn't been declared (var x;) - browser doesn't throw an error.

\n\n

Example: I want to know if my browser supports History API

\n\n
if (window.history) {\n    history.call_some_function();\n}\n
\n\n

How this works:

\n\n

window is an object which holds all global variables as its members, and it is legal to try to access a non-existing member. If x hasn't been declared or hasn't been set then window.x returns undefined. undefined leads to false when if() evaluates it.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e92041", + "creator": "Stijn de Witt", + "createdAt": 1446344903000, + "text": "But what if you run in Node? typeof history != 'undefined' actually works in both systems.", + "upvotes": 560, + "upvoterUsernames": [], + "downvotes": 560, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90899", + "creator": "bevacqua", + "createdAt": 1388667597000, + "text": "

Compare with void 0, for terseness.

\n\n
if (foo !== void 0)\n
\n\n

It's not as verbose as if (typeof foo !== 'undefined')

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e92043", + "creator": "daniel1426", + "createdAt": 1394232374000, + "text": "But it will throw a ReferenceError if foo is undeclared.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32634082fcc3049e92044", + "creator": "user8897421", + "createdAt": 1513604879000, + "text": "@daniel1426: So if there's an error in your code, you want to hide it instead of fixing it? Not a great approach, IMO.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9089b", + "creator": "sam", + "createdAt": 1399248789000, + "text": "
\"propertyName\" in obj //-> true | false\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9089c", + "creator": "raskalbass", + "createdAt": 1402920957000, + "text": "

Also, the same things can be written shorter:

\n
if (!variable){\n    // Do it if the variable is undefined\n}\n
\n

or

\n
if (variable){\n    // Do it if the variable is defined\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e92048", + "creator": "Stranded Kid", + "createdAt": 1436521467000, + "text": "Your first case can be triggered by defined variable. For example if variable = 0, !variable will trigger. So your answer is quite wrong.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9089d", + "creator": "Juan Garcia", + "createdAt": 1403068883000, + "text": "

All the answers are incomplete. This is the right way of knowing that there is a property 'defined as undefined':

\n
var hasUndefinedProperty = function hasUndefinedProperty(obj, prop){\n  return ((prop in obj) && (typeof obj[prop] == 'undefined'));\n};\n
\n

Example:

\n
var a = { b : 1, e : null };\na.c = a.d;\n\nhasUndefinedProperty(a, 'b'); // false: b is defined as 1\nhasUndefinedProperty(a, 'c'); // true: c is defined as undefined\nhasUndefinedProperty(a, 'd'); // false: d is undefined\nhasUndefinedProperty(a, 'e'); // false: e is defined as null\n\n// And now...\ndelete a.c ;\nhasUndefinedProperty(a, 'c'); // false: c is undefined\n
\n

Too bad that this been the right answer and is buried in wrong answers >_<

\n

So, for anyone who pass by, I will give you undefined's for free!!

\n
var undefined ; undefined ; // undefined\n({}).a ;                    // undefined\n[].a ;                      // undefined\n''.a ;                      // undefined\n(function(){}()) ;          // undefined\nvoid(0) ;                   // undefined\neval() ;                    // undefined\n1..a ;                      // undefined\n/a/.a ;                     // undefined\n(true).a ;                  // undefined\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9089e", + "creator": "Angelin Nadar", + "createdAt": 1404807547000, + "text": "

Going through the comments, for those who want to check both is it undefined or its value is null:

\n\n
//Just in JavaScript\nvar s; // Undefined\nif (typeof s == \"undefined\" || s === null){\n    alert('either it is undefined or value is null')\n}\n
\n\n

If you are using jQuery Library then jQuery.isEmptyObject() will suffice for both cases,

\n\n
var s; // Undefined\njQuery.isEmptyObject(s); // Will return true;\n\ns = null; // Defined as null\njQuery.isEmptyObject(s); // Will return true;\n\n//Usage\nif (jQuery.isEmptyObject(s)) {\n    alert('Either variable:s is undefined or its value is null');\n} else {\n     alert('variable:s has value ' + s);\n}\n\ns = 'something'; // Defined with some value\njQuery.isEmptyObject(s); // Will return false;\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32634082fcc3049e9204b", + "creator": "Henry Heleine", + "createdAt": 1418163138000, + "text": "jQuery will also take care of any cross-browser compatibility issues with the different JavaScript APIs.", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9089f", + "creator": "Seti", + "createdAt": 1412842148000, + "text": "

I would like to show you something I'm using in order to protect the undefined variable:

\n\n
Object.defineProperty(window, 'undefined', {});\n
\n\n

This forbids anyone to change the window.undefined value therefore destroying the code based on that variable. If using \"use strict\", anything trying to change its value will end in error, otherwise it would be silently ignored.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a2", + "creator": "Val", + "createdAt": 1425657480000, + "text": "

I use if (this.variable) to test if it is defined. A simple if (variable), recommended in a previous answer, fails for me.

\n

It turns out that it works only when a variable is a field of some object, obj.someField to check if it is defined in the dictionary. But we can use this or window as the dictionary object since any variable is a field in the current window, as I understand it. Therefore here is a test:

\n

\r\n
\r\n
if (this.abc) \n    alert(\"defined\"); \nelse \n    alert(\"undefined\");\n\nabc = \"abc\";\nif (this.abc) \n    alert(\"defined\"); \nelse \n    alert(\"undefined\");
\r\n
\r\n
\r\n

\n

It first detects that variable abc is undefined and it is defined after initialization.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a0", + "creator": "Vitalii Fedorenko", + "createdAt": 1418596524000, + "text": "

If you are using Angular:

\n\n
angular.isUndefined(obj)\nangular.isUndefined(obj.prop)\n
\n\n

Underscore.js:

\n\n
_.isUndefined(obj) \n_.isUndefined(obj.prop) \n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a1", + "creator": "Travis", + "createdAt": 1423969811000, + "text": "

Reading through this, I'm amazed I didn't see this. I have found multiple algorithms that would work for this.

\n\n

Never Defined

\n\n

If the value of an object was never defined, this will prevent from returning true if it is defined as null or undefined. This is helpful if you want true to be returned for values set as undefined

\n\n
if(obj.prop === void 0) console.log(\"The value has never been defined\");\n
\n\n

Defined as undefined Or never Defined

\n\n

If you want it to result as true for values defined with the value of undefined, or never defined, you can simply use === undefined

\n\n
if(obj.prop === undefined) console.log(\"The value is defined as undefined, or never defined\");\n
\n\n

Defined as a falsy value, undefined,null, or never defined.

\n\n

Commonly, people have asked me for an algorithm to figure out if a value is either falsy, undefined, or null. The following works.

\n\n
if(obj.prop == false || obj.prop === null || obj.prop === undefined) {\n    console.log(\"The value is falsy, null, or undefined\");\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32635082fcc3049e9204f", + "creator": "Stijn de Witt", + "createdAt": 1446344680000, + "text": "I think you can replace the last example with if (!obj.prop)", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908a4", + "creator": "lzl124631x", + "createdAt": 1452750233000, + "text": "

From lodash.js.

\n
var undefined;\nfunction isUndefined(value) {\n  return value === undefined;\n}\n
\n

It creates a local variable named undefined which is initialized with the default value -- the real undefined, then compares value with the variable undefined.

\n
\n

Update 9/9/2019

\n

I found Lodash updated its implementation. See my issue and the code.

\n

To be bullet-proof, simply use:

\n
function isUndefined(value) {\n  return value === void 0;\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a3", + "creator": "Mike Clark", + "createdAt": 1439552042000, + "text": "

Use:

\n\n

To check if property is undefined:

\n\n
if (typeof something === \"undefined\") {\n    alert(\"undefined\");\n}\n
\n\n

To check if property is not undefined:

\n\n
if (typeof something !== \"undefined\") {\n    alert(\"not undefined\");\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a5", + "creator": "Marian Klühspies", + "createdAt": 1456999536000, + "text": "

There is a nice and elegant way to assign a defined property to a new variable if it is defined or assign a default value to it as a fallback if it’s undefined.

\n
var a = obj.prop || defaultValue;\n
\n

It’s suitable if you have a function, which receives an additional configuration property:

\n
var yourFunction = function(config){\n\n   this.config = config || {};\n   this.yourConfigValue = config.yourConfigValue || 1;\n   console.log(this.yourConfigValue);\n}\n
\n

Now executing

\n
yourFunction({yourConfigValue:2});\n//=> 2\n\nyourFunction();\n//=> 1\n\nyourFunction({otherProperty:5});\n//=> 1\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a6", + "creator": "Patrick Roberts", + "createdAt": 1490659413000, + "text": "

I'm surprised I haven't seen this suggestion yet, but it gets even more specificity than testing with typeof. Use Object.getOwnPropertyDescriptor() if you need to know whether an object property was initialized with undefined or if it was never initialized:

\n
// to test someObject.someProperty\nvar descriptor = Object.getOwnPropertyDescriptor(someObject, 'someProperty');\n\nif (typeof descriptor === 'undefined') {\n  // was never initialized\n} else if (typeof descriptor.value === 'undefined') {\n  if (descriptor.get || descriptor.set) {\n    // is an accessor property, defined via getter and setter\n  } else {\n    // is initialized with `undefined`\n  }\n} else {\n  // is initialized with some other value\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a8", + "creator": "IliasT", + "createdAt": 1497485790000, + "text": "

I'm assuming you're going to also want to check for it being either undefined or null. If so, I suggest:

\n

myVar == null

\n

This is one of the only times a double equals is very helpful as it will evaluate to true when myVar is undefined or null, but it will evaluate to false when it is other falsey values such as 0, false, '', and NaN.

\n

This the actual the source code for Lodash's isNil method.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32635082fcc3049e92054", + "creator": "IliasT", + "createdAt": 1518201221000, + "text": "Surprised this hasn't gotten more upvotes or comments. What do people think about this?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908a7", + "creator": "Bekim Bacaj", + "createdAt": 1492714888000, + "text": "

This is probably the only explicit form of determining if the existing property-name has an explicit and intended value of undefined; which is, nonetheless, a JavaScript type.

\n
"propertyName" in containerObject && ""+containerObject["propertyName"] == "undefined";\n>> true \\ false\n
\n

This expression will only return true if the property name of the given context exists (truly) and only if its intended value is explicitly undefined.

\n

There will be no false positives like with empty or blank strings zeros nulls or empty arrays and alike. This does exactly that. Checks i.e., makes sure the property name exists (otherwise it would be a false positive), than it explicitly checks if its value is undefined e.g. of an undefined JavaScript type in it's string representation form (literally "undefined") therefore == instead of === because no further conversion is possible. And this expression will only return true if both, that is all conditions are met. E.g. if the property-name doesn't exist, - it will return false. Which is the only correct return since nonexistent properties can't have values, not even an undefined one.

\n

Example:

\n
containerObject = { propertyName: void "anything" }\n>> Object { propertyName: undefined }\n\n// Now the testing\n\n"propertyName" in containerObject && ""+containerObject["propertyName"] == "undefined";\n>> true\n\n/* Which makes sure that nonexistent property will not return a false positive\n * unless it is previously defined  */\n\n"foo" in containerObject && ""+containerObject["foo"] == "undefined";\n>> false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32635082fcc3049e92056", + "creator": "Bekim Bacaj", + "createdAt": 1514481654000, + "text": "Don't ever use null for what it's not.", + "upvotes": 1088, + "upvoterUsernames": [], + "downvotes": 1088, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908aa", + "creator": "Aditya Vashishtha", + "createdAt": 1520446525000, + "text": "

You can use the JavaScript object function like this:

\n
var ojb ={\n    age: 12\n}\n\nif(ojb.hasOwnProperty('name')){\n    console.log('property exists and is not undefined');\n}\n
\n

The above method returns true if it got that property or the property is not undefined.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908a9", + "creator": "blackmiaool", + "createdAt": 1509182652000, + "text": "

I provide three ways here for those who expect weird answers:

\n

\r\n
\r\n
function isUndefined1(val) {\n    try {\n        val.a;\n    } catch (e) {\n        return /undefined/.test(e.message);\n    }\n    return false;\n}\n\nfunction isUndefined2(val) {\n    return !val && val+'' === 'undefined';\n}\n\nfunction isUndefined3(val) {\n    const defaultVal = {};\n    return ((input = defaultVal) => input === defaultVal)(val);\n}\n\nfunction test(func){\n    console.group(`test start :`+func.name);\n    console.log(func(undefined));\n    console.log(func(null));\n    console.log(func(1));\n    console.log(func(\"1\"));\n    console.log(func(0));\n    console.log(func({}));\n    console.log(func(function () { }));\n    console.groupEnd();\n}\ntest(isUndefined1);\ntest(isUndefined2);\ntest(isUndefined3);
\r\n
\r\n
\r\n

\n

isUndefined1:

\n

Try to get a property of the input value, and check the error message if it exists. If the input value is undefined, the error message would be Uncaught TypeError: Cannot read property 'b' of undefined.

\n

isUndefined2:

\n

Convert the input value to a string to compare with "undefined" and ensure it's a negative value.

\n

isUndefined3:

\n

In JavaScript, an optional parameter works when the input value is exactly undefined.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ab", + "creator": "Sarkis Arutiunian", + "createdAt": 1521651026000, + "text": "

You can also use a Proxy. It will work with nested calls, but it will require one extra check:

\n
function resolveUnknownProps(obj, resolveKey) {\n  const handler = {\n    get(target, key) {\n      if (\n        target[key] !== null &&\n        typeof target[key] === 'object'\n      ) {\n        return resolveUnknownProps(target[key], resolveKey);\n      } else if (!target[key]) {\n        return resolveUnknownProps({ [resolveKey]: true }, resolveKey);\n      }\n\n      return target[key];\n    },\n  };\n\n  return new Proxy(obj, handler);\n}\n\nconst user = {}\n\nconsole.log(resolveUnknownProps(user, 'isUndefined').personalInfo.name.something.else); // { isUndefined: true }\n
\n

So you will use it like:

\n
const { isUndefined } = resolveUnknownProps(user, 'isUndefined').personalInfo.name.something.else;\nif (!isUndefined) {\n  // Do something\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ac", + "creator": "Aliaksandr Sushkevich", + "createdAt": 1523099775000, + "text": "

There are a few little helpers in the Lodash library:

\n

isUndefined - to check if value is undefined.

\n
_.isUndefined(undefined) // => true\n_.isUndefined(null) // => false\n
\n

has - to check if object contains a property

\n
const object = { 'a': { 'b': 2 } }\n\n_.has(object, 'a.b') // => true\n_.has(object, 'a.c') // => false\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ad", + "creator": "Krishnadas PC", + "createdAt": 1532347848000, + "text": "

Introduced in ECMAScript 6, we can now deal with undefined in a new way using Proxies. It can be used to set a default value to any properties which doesn't exist so that we don't have to check each time whether it actually exists.

\n
var handler = {\n  get: function(target, name) {\n    return name in target ? target[name] : 'N/A';\n  }\n};\n\nvar p = new Proxy({}, handler);\np.name = 'Kevin';\nconsole.log('Name: ' +p.name, ', Age: '+p.age, ', Gender: '+p.gender)\n
\n

Will output the below text without getting any undefined.

\n
Name: Kevin , Age: N/A , Gender: N/A\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908af", + "creator": "Przemek Struciński", + "createdAt": 1569567369000, + "text": "

ECMAScript 10 introduced a new feature - optional chaining which you can use to use a property of an object only when an object is defined like this:

\n
const userPhone = user?.contactDetails?.phone;\n
\n

It will reference to the phone property only when user and contactDetails are defined.

\n

Ref. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32636082fcc3049e9205c", + "creator": "Ben Racicot", + "createdAt": 1648050150000, + "text": "This is the answer to the question.", + "upvotes": 504, + "upvoterUsernames": [], + "downvotes": 504, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908b0", + "creator": "Ravi Makwana", + "createdAt": 1580452925000, + "text": "

I found this article, 7 Tips to Handle undefined in JavaScript, which is showing really interesting things about undefined\nlike:

\n

The existence of undefined is a consequence of JavaScript’s permissive nature that allows the usage of:

\n\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32636082fcc3049e9205f", + "creator": "Peter Mortensen", + "createdAt": 1595634939000, + "text": "Yes, but how does it answer the question?", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908ae", + "creator": "CodeDraken", + "createdAt": 1536668636000, + "text": "

A simple way to check if a key exists is to use in:

\n
if (key in obj) {\n  // Do something\n} else {\n  // Create key\n}\n\nconst obj = {\n  0: 'abc',\n  1: 'def'\n}\n\nconst hasZero = 0 in obj\n\nconsole.log(hasZero) // true\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b2", + "creator": "Sajad Saderi", + "createdAt": 1588660869000, + "text": "

There is a very easy and simple way.

\n

You can use optional chaining:

\n
x = {prop:{name:"sajad"}}\n\nconsole.log(x.prop?.name) // Output is: "sajad"\nconsole.log(x.prop?.lastName) // Output is: undefined\n
\n

or

\n
if(x.prop?.lastName) // The result of this 'if' statement is false and is not throwing an error\n
\n

You can use optional chaining even for functions or arrays.

\n

As of mid-2020 this is not universally implemented. Check the documentation at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32636082fcc3049e92061", + "creator": "aecend", + "createdAt": 1602401329000, + "text": "This is exactly what I was looking for. Thank you!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908b1", + "creator": "Kiran Maniya", + "createdAt": 1588367513000, + "text": "

In JavaScript, there are truthy and falsy expressions. If you want to check if the property is undefined or not, there is a straight way of using an if condition as given,

\n
    \n
  1. Using truthy/falsy concept.
  2. \n
\n
if(!ob.someProp){\n    console.log('someProp is falsy')\n}\n
\n

However, there are several more approaches to check the object has property or not, but it seems long to me. Here are those.

\n
    \n
  1. Using === undefined check in if condition
  2. \n
\n
if(ob.someProp === undefined){\n    console.log('someProp is undefined')\n}\n
\n
    \n
  1. Using typeof
  2. \n
\n

typeof acts as a combined check for the value undefined and for whether a variable exists.

\n
if(typeof ob.someProp === 'undefined'){\n    console.log('someProp is undefined')\n}\n
\n
    \n
  1. Using hasOwnProperty method
  2. \n
\n

The JavaScript object has built in the hasOwnProperty function in the object prototype.

\n
if(!ob.hasOwnProperty('someProp')){\n    console.log('someProp is undefined')\n}\n
\n

Not going in deep, but the 1st way looks shortened and good to me. Here are the details on truthy/falsy values in JavaScript and undefined is the falsy value listed in there. So the if condition behaves normally without any glitch. Apart from the undefined, values NaN, false (Obviously), '' (empty string) and number 0 are also the falsy values.

\n
\n

Warning: Make sure the property value does not contain any falsy value, otherwise the if condition will return false. For such a case, you can use the hasOwnProperty method

\n
\n", + "upvotes": 821, + "upvoterUsernames": [], + "downvotes": 821, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32636082fcc3049e92064", + "creator": "Peter Seliger", + "createdAt": 1589293218000, + "text": "... do you also check the code examples you are posting ?... e.g ... if (typeof ob.someProp === undefined) { ...", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32636082fcc3049e92066", + "creator": "Kiran Maniya", + "createdAt": 1589294846000, + "text": "@PeterSeliger Thanks for noticing that.", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908b3", + "creator": "Kamil Kiełczewski", + "createdAt": 1595356957000, + "text": "

Review

\n

A lot of the given answers give a wrong result because they do not distinguish between the case when an object property does not exist and the case when a property has value undefined. Here is proof for most popular solutions:

\n

\r\n
\r\n
let obj = {\n  a: 666,\n  u: undefined // The 'u' property has value 'undefined'\n               // The 'x' property does not exist\n}\n\nconsole.log('>>> good results:');\nconsole.log('A', \"u\" in obj, \"x\" in obj);\nconsole.log('B', obj.hasOwnProperty(\"u\"),      obj.hasOwnProperty(\"x\"));\n\nconsole.log('\\n>>> bad results:');\nconsole.log('C', obj.u === undefined,          obj.x === undefined);\nconsole.log('D', obj.u == undefined,           obj.x == undefined);\nconsole.log('E', obj[\"u\"] === undefined,       obj[\"x\"] === undefined);\nconsole.log('F', obj[\"u\"] == undefined,        obj[\"x\"] == undefined);\nconsole.log('G', !obj.u,                      !obj.x);\nconsole.log('H', typeof obj.u === 'undefined', typeof obj.x === 'undefined');
\r\n
\r\n
\r\n

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b4", + "creator": "Hanzla Habib", + "createdAt": 1601402977000, + "text": "

In recent JavaScript release there is new chaining operator introduced, which is most probably best way to check if property exists else it will give you undefined

\n

see example below

\n
  const adventurer = {\n  name: 'Alice',\n  cat: {\n    name: 'Dinah'\n  }\n};\n\nconst dogName = adventurer.dog?.name;\nconsole.log(dogName);\n// expected output: undefined\n\nconsole.log(adventurer.someNonExistentMethod?.());\n// expected output: undefined\n
\n

We can replace this old syntax

\n
if (response && response.data && response.data.someData && response.data.someData.someMoreData) {}\n
\n

with this neater syntax

\n
if( response?.data?.someData?.someMoreData) {}\n
\n

This syntax is not supported in IE, Opera, safari & samsund android

\n

for more detail you can check this URL

\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b5", + "creator": "Adam111p", + "createdAt": 1608215607000, + "text": "

Version for the use of dynamic variables\nDid you know?

\n

\r\n
\r\n
var boo ='lala';\n\nfunction check(){\n  if(this['foo']){\n        console.log('foo is here');}\n  else{\n        console.log('have no foo');\n      }\n\n  if(this['boo']){\n        console.log('boo is here');}\n  else{\n        console.log('have no boo');\n      }\n}\n\ncheck();
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b6", + "creator": "ßãlãjî", + "createdAt": 1624765005000, + "text": "

handle undefined

\n
function isUndefined(variable,defaultvalue=''){\n\n    if (variable == undefined ) return defaultvalue;\n    \n    return variable;\n\n}\n
\n

\r\n
\r\n
var obj={\nund:undefined,\nnotundefined:'hi i am not undefined'\n}\n\n\nfunction isUndefined(variable,defaultvalue=''){\n\n    if (variable == undefined )\n    { \n        return defaultvalue;\n    }\n    return variable\n\n}\n\nconsole.log(isUndefined(obj.und,'i am print'))\nconsole.log(isUndefined(obj.notundefined,'i am print'))
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b7", + "creator": "Ali Yaghoubi", + "createdAt": 1634208288000, + "text": "

We at ES6 can with !! Convert all values to Boolean.\nUsing this, all falsy values become false.

\n

First solution

\n
if (!(!!variable)) {\n    // Code\n}\n
\n

Second solution

\n
if (!variable) {\n    // Code\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1416613, + "uvac": 1416659 + } + }, + { + "_id": "62f321bb082fcc3049e8fede", + "title": "Is there an "exists" function for jQuery?", + "title-lowercase": "is there an "exists" function for jquery?", + "creator": "Jake McGraw", + "createdAt": 1219866581000, + "status": "open", + "text": "

How can I check the existence of an element in jQuery?

\n\n

The current code that I have is this:

\n\n
if ($(selector).length > 0) {\n    // Do something\n}\n
\n\n

Is there a more elegant way to approach this? Perhaps a plugin or a function?

\n", + "upvotes": 5424, + "upvoterUsernames": [], + "downvotes": 2370, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 839501, + "answers": 45, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e9099e", + "creator": "Jon Erickson", + "createdAt": 1231962365000, + "text": "

If you used

\n\n
jQuery.fn.exists = function(){return ($(this).length > 0);}\nif ($(selector).exists()) { }\n
\n\n

you would imply that chaining was possible when it is not.

\n\n

This would be better:

\n\n
jQuery.exists = function(selector) {return ($(selector).length > 0);}\nif ($.exists(selector)) { }\n
\n\n

Alternatively, from the FAQ:

\n\n
if ( $('#myDiv').length ) { /* Do something */ }\n
\n\n

You could also use the following. If there are no values in the jQuery object array then getting the first item in the array would return undefined.

\n\n
if ( $('#myDiv')[0] ) { /* Do something */ }\n
\n", + "upvotes": 506, + "upvoterUsernames": [], + "downvotes": 117, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9099d", + "creator": "Devon", + "createdAt": 1221674014000, + "text": "

You can use:

\n\n
if ($(selector).is('*')) {\n  // Do something\n}\n
\n\n

A little more elegant, perhaps.

\n", + "upvotes": 132, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c5082fcc3049e92194", + "creator": "vsync", + "createdAt": 1259054923000, + "text": "This is too much for such a simple thing. see Tim Büthe answer", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9099c", + "creator": "Jake McGraw", + "createdAt": 1219866603000, + "text": "

Yes!

\n\n
jQuery.fn.exists = function(){ return this.length > 0; }\n\nif ($(selector).exists()) {\n    // Do something\n}\n
\n\n

This is in response to: Herding Code podcast with Jeff Atwood

\n", + "upvotes": 1495, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c5082fcc3049e92196", + "creator": "vsync", + "createdAt": 1259054579000, + "text": "I just write: if( $(selector).length ){ ... } without the '> 0'", + "upvotes": 264, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f326c5082fcc3049e92197", + "creator": "redsquare", + "createdAt": 1280794393000, + "text": "@quixoto, sorry but .length is a standard across many languages that does not need wrapping. How else do you interpret .length?", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9099f", + "creator": "Mad_Hat", + "createdAt": 1235529879000, + "text": "
if ( $('#myDiv').size() > 0 ) { //do something }\n
\n\n

size() counts the number of elements returned by the selector

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909a1", + "creator": "Yanni", + "createdAt": 1301833026000, + "text": "

You can use this:

\n\n
// if element exists\nif($('selector').length){ /* do something */ }\n
\n\n
\n\n
// if element does not exist\nif(!$('selector').length){ /* do something */ }\n
\n", + "upvotes": 230, + "upvoterUsernames": [], + "downvotes": 82, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e9219a", + "creator": "Th4t Guy", + "createdAt": 1406827698000, + "text": "Did you not see that Tim Büthe had already given this answer 2 years prior to you?", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e9219c", + "creator": "Jeremy W", + "createdAt": 1438786999000, + "text": "Pfft, Tim never showed how to test if the element does not exist.", + "upvotes": 193, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909a0", + "creator": "Tim Büthe", + "createdAt": 1235589381000, + "text": "

In JavaScript, everything is 'truthy' or 'falsy', and for numbers 0 means false, everything else true. So you could write:

\n
if ($(selector).length)\n
\n

You don't need that >0 part.

\n", + "upvotes": 3011, + "upvoterUsernames": [], + "downvotes": 302, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e9219e", + "creator": "Robert", + "createdAt": 1498768522000, + "text": "@abhirathore2006 If you use an id selector and the element doesn't exist then length is 0 and doesn't throw exceptions.", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921a0", + "creator": "Robert", + "createdAt": 1498769567000, + "text": "Interestingly enough NaN != false.", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921a1", + "creator": "Ismael Miguel", + "createdAt": 1499551778000, + "text": "@James That's because [].toString() === [].join(',') === "" and "" === "".", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921a3", + "creator": "user1280483", + "createdAt": 1638142146000, + "text": "@Robert, that's because NaN != a when a is literally any value.", + "upvotes": 3328, + "upvoterUsernames": [], + "downvotes": 3328, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921a5", + "creator": "Parzh from Ukraine", + "createdAt": 1652279775000, + "text": "NaN may be != false, but NaN is definitely falsy, which is a slightly different thing", + "upvotes": 1684, + "upvoterUsernames": [], + "downvotes": 1684, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909a2", + "creator": "amypellegrini", + "createdAt": 1321280452000, + "text": "

There's no need for jQuery really. With plain JavaScript it's easier and semantically correct to check for:

\n\n
if(document.getElementById(\"myElement\")) {\n    //Do something...\n}\n
\n\n

If for any reason you don't want to put an id to the element, you can still use any other JavaScript method designed to access the DOM.

\n\n

jQuery is really cool, but don't let pure JavaScript fall into oblivion...

\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909a3", + "creator": "Magne", + "createdAt": 1326284869000, + "text": "

The fastest and most semantically self explaining way to check for existence is actually by using plain JavaScript:

\n\n
if (document.getElementById('element_id')) {\n    // Do something\n}\n
\n\n

It is a bit longer to write than the jQuery length alternative, but executes faster since it is a native JS method.

\n\n

And it is better than the alternative of writing your own jQuery function. That alternative is slower, for the reasons @snover stated. But it would also give other programmers the impression that the exists() function is something inherent to jQuery. JavaScript would/should be understood by others editing your code, without increased knowledge debt.

\n\n

NB: Notice the lack of an '#' before the element_id (since this is plain JS, not jQuery).

\n", + "upvotes": 169, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e921a9", + "creator": "Blue Skies", + "createdAt": 1386463433000, + "text": "@Noz if(document.querySelector("#foo a.special")) would work. No jQuery needed.", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909a5", + "creator": "jcreamer898", + "createdAt": 1333659766000, + "text": "

I had a case where I wanted to see if an object exists inside of another so I added something to the first answer to check for a selector inside the selector..

\n\n
// Checks if an object exists.\n// Usage:\n//\n//     $(selector).exists()\n//\n// Or:\n// \n//     $(selector).exists(anotherSelector);\njQuery.fn.exists = function(selector) {\n    return selector ? this.find(selector).length : this.length;\n};\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909a4", + "creator": "Oleg", + "createdAt": 1328560675000, + "text": "

I have found if ($(selector).length) {} to be insufficient. It will silently break your app when selector is an empty object {}.

\n\n
var $target = $({});        \nconsole.log($target, $target.length);\n\n// Console output:\n// -------------------------------------\n// [▼ Object              ] 1\n//    ► __proto__: Object\n
\n\n

My only suggestion is to perform an additional check for {}.

\n\n
if ($.isEmptyObject(selector) || !$(selector).length) {\n    throw new Error('Unable to work with the given selector.');\n}\n
\n\n

I'm still looking for a better solution though as this one is a bit heavy.

\n\n

Edit: WARNING! This doesn't work in IE when selector is a string.

\n\n
$.isEmptyObject('hello') // FALSE in Chrome and TRUE in IE\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e921ac", + "creator": "nnnnnn", + "createdAt": 1419247496000, + "text": "How often do you find yourself calling $() with an empty object as an argument?", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921ae", + "creator": "Uyghur Lives Matter", + "createdAt": 1427384814000, + "text": "Why on earth would you pass an empty object {} to $()?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921b0", + "creator": "Oleg", + "createdAt": 1427384899000, + "text": "@cpburnz why do you ask me? I was just an API provider... People pass all kinds of stupid things to APIs.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909a7", + "creator": "andy_314", + "createdAt": 1343858165000, + "text": "

I'm using this:

\n\n
    $.fn.ifExists = function(fn) {\n      if (this.length) {\n        $(fn(this));\n      }\n    };\n    $(\"#element\").ifExists( \n      function($this){\n        $this.addClass('someClass').animate({marginTop:20},function(){alert('ok')});               \n      }\n    ); \n
\n\n

Execute the chain only if a jQuery element exist - http://jsfiddle.net/andres_314/vbNM3/2/

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909a9", + "creator": "hiway", + "createdAt": 1382507210000, + "text": "

Is $.contains() what you want?

\n
\n

jQuery.contains( container, contained )

\n

The $.contains() method returns true if the DOM element provided by the second argument is a descendant of the DOM element provided by the first argument, whether it is a direct child or nested more deeply. Otherwise, it returns false. Only element nodes are supported; if the second argument is a text or comment node, $.contains() will return false.

\n

Note: The first argument must be a DOM element, not a jQuery object or plain JavaScript object.

\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e921b3", + "creator": "user1106925", + "createdAt": 1465046897000, + "text": "This doesn't accept a selector, which means he would have to select it, which means he could just check the result of his selection.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909a8", + "creator": "王奕然", + "createdAt": 1370071202000, + "text": "

You could use this:

\n\n
jQuery.fn.extend({\n    exists: function() { return this.length }\n});\n\nif($(selector).exists()){/*do something*/}\n
\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909a6", + "creator": "SJG", + "createdAt": 1338209888000, + "text": "
$(selector).length && //Do something\n
\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c6082fcc3049e921b7", + "creator": "Emile Bergeron", + "createdAt": 1475886930000, + "text": "I hate these clever ways of avoiding to use an if where an if would improve readability at the cost of 2 bytes.", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921b9", + "creator": "user7892745", + "createdAt": 1495489613000, + "text": "Plus, minifiers will do all these && for you.", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [] + }, + { + "_id": "62f326c6082fcc3049e921bb", + "creator": "Tilak Madichetti", + "createdAt": 1594558390000, + "text": "Haha now 8 years later it is so common in React JS 😄", + "upvotes": 152, + "upvoterUsernames": [], + "downvotes": 152, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909aa", + "creator": "Salman A", + "createdAt": 1390035887000, + "text": "

You can save a few bytes by writing:

\n\n
if ($(selector)[0]) { ... }\n
\n\n

This works because each jQuery object also masquerades as an array, so we can use the array dereferencing operator to get the first item from the array. It returns undefined if there is no item at the specified index.

\n", + "upvotes": 141, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909ab", + "creator": "MAX POWER", + "createdAt": 1393967745000, + "text": "

How about:

\n\n
function exists(selector) {\n    return $(selector).length;\n}\n\nif (exists(selector)) {\n    // do something\n}\n
\n\n

It's very minimal and saves you having to enclose the selector with $() every time.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909ad", + "creator": "technosaurus", + "createdAt": 1407800539000, + "text": "

The reason all of the previous answers require the .length parameter is that they are mostly using jquery's $() selector which has querySelectorAll behind the curtains (or they are using it directly). This method is rather slow because it needs to parse the entire DOM tree looking for all matches to that selector and populating an array with them.

\n\n

The ['length'] parameter is not needed or useful and the code will be a lot faster if you directly use document.querySelector(selector) instead, because it returns the first element it matches or null if not found.

\n\n
function elementIfExists(selector){  //named this way on purpose, see below\n    return document.querySelector(selector);\n}\n/* usage: */\nvar myelement = elementIfExists(\"#myid\") || myfallbackelement;\n
\n\n

However this method leaves us with the actual object being returned; which is fine if it isn't going to be saved as variable and used repeatedly (thus keeping the reference around if we forget).

\n\n
var myel=elementIfExists(\"#myid\");\n// now we are using a reference to the element which will linger after removal\nmyel.getParentNode.removeChild(myel);\nconsole.log(elementIfExists(\"#myid\")); /* null */\nconsole.log(myel); /* giant table lingering around detached from document */\nmyel=null; /* now it can be garbage collected */\n
\n\n

In some cases this may be desired. It can be used in a for loop like this:

\n\n
/* locally scoped myel gets garbage collected even with the break; */\nfor (var myel; myel = elementIfExist(sel); myel.getParentNode.removeChild(myel))\n    if (myel == myblacklistedel) break;\n
\n\n

If you don't actually need the element and want to get/store just a true/false, just double not it !! It works for shoes that come untied, so why knot here?

\n\n
function elementExists(selector){\n    return !!document.querySelector(selector);\n}\n/* usage: */\nvar hastables = elementExists(\"table\");  /* will be true or false */\nif (hastables){\n    /* insert css style sheet for our pretty tables */\n}\nsetTimeOut(function (){if (hastables && !elementExists(\"#mytablecss\"))\n                           alert(\"bad table layouts\");},3000);\n
\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909ac", + "creator": "Eternal1", + "createdAt": 1406544646000, + "text": "

I stumbled upon this question and i'd like to share a snippet of code i currently use:

\n\n
$.fn.exists = function(callback) {\n    var self = this;\n    var wrapper = (function(){\n            function notExists () {}\n\n            notExists.prototype.otherwise = function(fallback){\n                if (!self.length) {                    \n                    fallback.call();\n                }\n            };\n\n            return new notExists;\n        })();\n\n    if(self.length) {\n        callback.call();    \n    }\n\n    return wrapper;\n}\n
\n\n

And now i can write code like this -

\n\n
$(\"#elem\").exists(function(){\n    alert (\"it exists\");\n}).otherwise(function(){\n    alert (\"it doesn't exist\");\n});\n
\n\n

It might seem a lot of code, but when written in CoffeeScript it is quite small:

\n\n
$.fn.exists = (callback) ->\n    exists = @length\n    callback.call() if exists        \n    new class\n       otherwise: (fallback) ->            \n            fallback.call() if not exists\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c7082fcc3049e921c0", + "creator": "Eternal1", + "createdAt": 1407224331000, + "text": "For simple cases - you're right. But for more complex situations involving a lot of code on both cases i think my approach is better.", + "upvotes": 539, + "upvoterUsernames": [], + "downvotes": 539, + "downvoterUsernames": [] + }, + { + "_id": "62f326c7082fcc3049e921c2", + "creator": "Jarvl", + "createdAt": 1466794505000, + "text": "In what complex situation would this approach be better than a simple if/else statement?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909ae", + "creator": "Santiago Hernández", + "createdAt": 1430710281000, + "text": "

this is very similar to all of the answers, but why not use the ! operator twice so you can get a boolean:

\n\n
jQuery.fn.exists = function(){return !!this.length};\n\nif ($(selector).exists()) {\n    // the element exists, now what?...\n}\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c7082fcc3049e921c4", + "creator": "user7892745", + "createdAt": 1495489649000, + "text": "Because Boolean(x) can sometimes be more efficient.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909b0", + "creator": "guest271314", + "createdAt": 1439088959000, + "text": "

Try testing for DOM element

\n\n
if (!!$(selector)[0]) // do stuff\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b1", + "creator": "Oliver", + "createdAt": 1444766498000, + "text": "

Inspired by hiway's answer I came up with the following:

\n\n
$.fn.exists = function() {\n    return $.contains( document.documentElement, this[0] );\n}\n
\n\n

jQuery.contains takes two DOM elements and checks whether the first one contains the second one.

\n\n

Using document.documentElement as the first argument fulfills the semantics of the exists method when we want to apply it solely to check the existence of an element in the current document.

\n\n

Below, I've put together a snippet that compares jQuery.exists() against the $(sel)[0] and $(sel).length approaches which both return truthy values for $(4) while $(4).exists() returns false. In the context of checking for existence of an element in the DOM this seems to be the desired result.

\n\n

\r\n
\r\n
$.fn.exists = function() {\r\n    return $.contains(document.documentElement, this[0]); \r\n  }\r\n  \r\n  var testFuncs = [\r\n    function(jq) { return !!jq[0]; },\r\n    function(jq) { return !!jq.length; },\r\n    function(jq) { return jq.exists(); },\r\n  ];\r\n    \r\n  var inputs = [\r\n    [\"$()\",$()],\r\n    [\"$(4)\",$(4)],\r\n    [\"$('#idoexist')\",$('#idoexist')],\r\n    [\"$('#idontexist')\",$('#idontexist')]\r\n  ];\r\n  \r\n  for( var i = 0, l = inputs.length, tr, input; i < l; i++ ) {\r\n    input = inputs[i][1];\r\n    tr = \"<tr><td>\" + inputs[i][0] + \"</td><td>\"\r\n          + testFuncs[0](input) + \"</td><td>\"\r\n          + testFuncs[1](input) + \"</td><td>\"\r\n          + testFuncs[2](input) + \"</td></tr>\";\r\n    $(\"table\").append(tr);\r\n  }
\r\n
td { border: 1px solid black }
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div id=\"idoexist\">#idoexist</div>\r\n<table style>\r\n<tr>\r\n  <td>Input</td><td>!!$(sel)[0]</td><td>!!$(sel).length</td><td>$(sel).exists()</td>\r\n</tr>\r\n</table>\r\n<script>\r\n  \r\n  $.fn.exists = function() {\r\n    return $.contains(document.documentElement, this[0]); \r\n  }\r\n  \r\n</script>
\r\n
\r\n
\r\n

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909af", + "creator": "Anurag Deokar", + "createdAt": 1436445541000, + "text": "

You can check element is present or not using length in java script.\n If length is greater than zero then element is present if length is zero then\n element is not present

\n\n
// These by Id\nif ($(\"#elementid\").length > 0) {\n  // Element is Present\n} else {\n  // Element is not Present\n}\n\n// These by Class\nif ($(\".elementClass\").length > 0) {\n  // Element is Present\n} else {\n  // Element is not Present\n}\n
\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c7082fcc3049e921c7", + "creator": "Pranav Labhe", + "createdAt": 1440242573000, + "text": "You need not to check weather length is greater than 0, if( $('#elementid').length ) { } will be sufficient.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c7082fcc3049e921c9", + "creator": "A1rPun", + "createdAt": 1458144089000, + "text": "Have you actually read the question? It's exactly the same method OP is using.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909b3", + "creator": "Kamuran Sönecek", + "createdAt": 1458903949000, + "text": "

$(\"selector\") returns an object which has the length property. If the selector finds any elements, they will be included in the object. So if you check its length you can see if any elements exist. In JavaScript 0 == false, so if you don't get 0 your code will run.

\n\n
if($(\"selector\").length){\n   //code in the case\n} \n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b2", + "creator": "ducdhm", + "createdAt": 1457456815000, + "text": "

Here is my favorite exist method in jQuery

\n\n
$.fn.exist = function(callback) {\n    return $(this).each(function () {\n        var target = $(this);\n\n        if (this.length > 0 && typeof callback === 'function') {\n            callback.call(target);\n        }\n    });\n};\n
\n\n

and other version which supports callback when selector does not exist

\n\n
$.fn.exist = function(onExist, onNotExist) {\n    return $(this).each(function() {\n        var target = $(this);\n\n        if (this.length > 0) {\n            if (typeof onExist === 'function') {\n                onExist.call(target);\n            }\n        } else {\n            if (typeof onNotExist === 'function') {\n                onNotExist.call(target);\n            }\n        }\n    });\n};\n
\n\n

Example:

\n\n
$('#foo .bar').exist(\n    function () {\n        // Stuff when '#foo .bar' exists\n    },\n    function () {\n        // Stuff when '#foo .bar' does not exist\n    }\n);\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b4", + "creator": "Sanu Uthaiah Bollera", + "createdAt": 1466289423000, + "text": "

I just like to use plain vanilla javascript to do this.

\n\n
function isExists(selector){\n  return document.querySelectorAll(selector).length>0;\n}\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b5", + "creator": "Sunil Kumar", + "createdAt": 1469511146000, + "text": "

Yes The best method of doing this :

\n\n

By JQuery :

\n\n
if($(\"selector\").length){\n   //code in the case\n}\n
\n\n

selector can be Element ID OR Element Class

\n\n

OR

\n\n

If you don't want to use jQuery Library then you can achieve this by using Core JavaScript :

\n\n

By JavaScript :

\n\n
if(document.getElementById(\"ElementID\")) {\n    //Do something...\n}\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c8082fcc3049e921cc", + "creator": "Abdur Rehman", + "createdAt": 1502371214000, + "text": "if "ElementID" doesn't exist then it will not throw error in if condition?", + "upvotes": 1382, + "upvoterUsernames": [], + "downvotes": 1382, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909b6", + "creator": "abhirathore2006", + "createdAt": 1469947336000, + "text": "

Here is the complete example of different situations and way to check if element exists using direct if on jQuery selector may or may not work because it returns array or elements.

\n
var a = null;\n\nvar b = []\n\nvar c = undefined ;\n\nif(a) { console.log(" a exist")} else { console.log("a doesn't exit")}\n// output: a doesn't exit\n\nif(b) { console.log(" b exist")} else { console.log("b doesn't exit")}\n// output: b exist\n\nif(c) { console.log(" c exist")} else { console.log("c doesn't exit")}\n// output: c doesn't exit\n
\n

FINAL SOLUTION

\n
if($("#xysyxxs").length){ console.log("xusyxxs exist")} else { console.log("xusyxxs doesnn't exist") }\n//output : xusyxxs doesnn't exist\n\nif($(".xysyxxs").length){ console.log("xusyxxs exist")} else { console.log("xusyxxs doesnn't exist") }\n    //output : xusyxxs doesnn't exist\n
\n

Demo

\n

\r\n
\r\n
console.log(\"existing id\", $('#id-1').length)\nconsole.log(\"non existing id\", $('#id-2').length)\n\nconsole.log(\"existing class single instance\", $('.cls-1').length)\nconsole.log(\"existing class multiple instance\", $('.cls-2').length)\nconsole.log(\"non existing class\", $('.cls-3').length)
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n<div id=\"id-1\">\n  <div class=\"cls-1 cls-2\"></div>\n  <div class=\"cls-2\"></div>\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b7", + "creator": "Jonathan Cardoz", + "createdAt": 1471959469000, + "text": "

Use the following syntax to check if the element actually exists using jQuery.

\n\n
let oElement = $(\".myElementClass\");\nif(oElement[0]) {\n    // Do some jQuery operation here using oElement\n}\nelse {\n    // Unable to fetch the object\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b8", + "creator": "Pawel", + "createdAt": 1480329814000, + "text": "

No need for jQuery (basic solution)

\n
if(document.querySelector('.a-class')) {\n  // do something\n}\n
\n

Much more performant option below (notice the lack of a dot before a-class).

\n
if(document.getElementsByClassName('a-class')[0]) {\n  // do something\n}\n
\n

querySelector uses a proper matching engine like $() (sizzle) in jQuery and uses more computing power but in 99% of cases will do just fine. The second option is more explicit and tells the code exactly what to do. It's much faster according to JSBench https://jsbench.me/65l2up3t8i

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909b9", + "creator": "Tilak Madichetti", + "createdAt": 1490193169000, + "text": "

Checking for existence of an element is documented neatly in the official jQuery website itself!

\n\n
\n

Use the .length property of the jQuery collection returned by your\n selector:

\n\n
if ($(\"#myDiv\").length) {\n    $(\"#myDiv\").show();\n}\n
\n \n

Note that it isn't always necessary to test whether an element exists.\n The following code will show the element if it exists, and do nothing\n (with no errors) if it does not:

\n\n
$(\"#myDiv\").show();\n
\n
\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909ba", + "creator": "Alireza", + "createdAt": 1495272072000, + "text": "

I see most of the answers here are not accurate as they should be, they check element length, it can be OK in many cases, but not 100%, imagine if number pass to the function instead, so I prototype a function which check all conditions and return the answer as it should be:

\n\n
$.fn.exists = $.fn.exists || function() { \n  return !!(this.length && (this[0] instanceof HTMLDocument || this[0] instanceof HTMLElement)); \n}\n
\n\n

This will check both length and type, Now you can check it this way:

\n\n
$(1980).exists(); //return false\n$([1,2,3]).exists(); //return false\n$({name: 'stackoverflow', url: 'http://www.stackoverflow.com'}).exists(); //return false\n$([{nodeName: 'foo'}]).exists() // returns false\n$('div').exists(); //return true\n$('.header').exists(); //return true\n$(document).exists(); //return true\n$('body').exists(); //return true\n
\n", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909bb", + "creator": "Andrei Todorut", + "createdAt": 1497951818000, + "text": "

You don't have to check if it's greater than 0 like $(selector).length > 0, $(selector).length it's enough and an elegant way to check the existence of elements. I don't think that it is worth to write a function only for this, if you want to do more extra things, then yes.

\n
if($(selector).length){\n  // true if length is not 0\n} else {\n  // false if length is 0\n}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909bc", + "creator": "sina_Islam", + "createdAt": 1508163829000, + "text": "

A simple utility function for both id and class selector.

\n\n
function exist(IdOrClassName, IsId) {\n  var elementExit = false;\n  if (IsId) {\n    elementExit = $(\"#\" + \"\" + IdOrClassName + \"\").length ? true : false;\n  } else {\n    elementExit = $(\".\" + \"\" + IdOrClassName + \"\").length ? true : false;\n  }\n  return elementExit;\n}\n
\n\n

calling this function like bellow

\n\n
$(document).ready(function() {\n  $(\"#btnCheck\").click(function() {\n    //address is the id so IsId is true. if address is class then need to set IsId false\n    if (exist(\"address\", true)) {\n      alert(\"exist\");\n    } else {\n      alert(\"not exist\");\n    }\n  });\n});\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c8082fcc3049e921d0", + "creator": "Roland", + "createdAt": 1510649166000, + "text": "This way you put it into global namespace. Which you may want to reconsider.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909bd", + "creator": "Jonas Lundman", + "createdAt": 1521255264000, + "text": "

All answers is not working bullet proof to check existence of an element in jQuery. After many years of coding, only this solution does not throw any warnings about existance or not:

\n\n
if($(selector).get(0)) { // Do stuff }\n
\n\n

Or to bail instead in the beginning of your function:

\n\n
if(!$(selector).get(0)) return;\n
\n\n

Explained

\n\n

In this case, you dont have to deal with zero | null lengths issues. This forces to fetch an element, not count them.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909bf", + "creator": "Hassan Sadeghi", + "createdAt": 1534573622000, + "text": "

Try this.

\n\n

simple and short and usable in the whole project:

\n\n
jQuery.fn.exists=function(){return !!this[0];}; //jQuery Plugin\n
\n\n

Usage:

\n\n
console.log($(\"element-selector\").exists());\n
\n\n

_________________________________

\n\n

OR EVEN SHORTER:\n(for when you don't want to define a jQuery plugin):

\n\n
if(!!$(\"elem-selector\")[0]) ...;\n
\n\n

or even

\n\n
if($(\"elem-selector\")[0]) ...;\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909be", + "creator": "Manish Vadher", + "createdAt": 1534422440000, + "text": "

The input won't have a value if it doesn't exist. Try this...

\n\n
if($(selector).val())\n
\n", + "upvotes": 2128, + "upvoterUsernames": [], + "downvotes": 2128, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c0", + "creator": "Rafal Enden", + "createdAt": 1535041344000, + "text": "

Use querySelectorAll with forEach, no need for if and extra assignment:

\n\n
document.querySelectorAll('.my-element').forEach((element) => {\n  element.classList.add('new-class');\n});\n
\n\n

as the opposite to:

\n\n
const myElement = document.querySelector('.my-element');\nif (myElement) {\n  element.classList.add('new-class');\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c1", + "creator": "Abdul Rahman", + "createdAt": 1545193854000, + "text": "

I am using this:

\n\n
 if($(\"#element\").length > 0){\n   //the element exists in the page, you can do the rest....\n }\n
\n\n

Its very simple and easy to find an element.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c2", + "creator": "Majedur", + "createdAt": 1545295126000, + "text": "

Just check the length of the selector, if it more than 0 then it's return true otherwise false.

\n\n

For ID:

\n\n
 if( $('#selector').length )         // use this if you are using id to check\n{\n     // it exists\n}\n
\n\n

For Class:

\n\n
 if( $('.selector').length )         // use this if you are using class to check\n{\n     // it exists\n}\n
\n\n

For Dropdown:

\n\n
if( $('#selector option').size() ) {   // use this if you are using dropdown size to check\n\n   // it exists\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c5", + "creator": "Greedo", + "createdAt": 1600852335000, + "text": "

I found that this is the most jQuery way, IMHO.\nExtending the default function is easy and can be done in a global extension file.

\n

\r\n
\r\n
$.fn.exist = function(){\n  return !!this.length;\n};\n\nconsole.log($(\"#yes\").exist())\n\nconsole.log($(\"#no\").exist())
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n\n<div id=\"yes\">id = yes</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c3", + "creator": "chickens", + "createdAt": 1571338400000, + "text": "

With jQuery you do not need >0, this is all you need:

\n\n
if ($(selector).length)\n
\n\n

With vanilla JS, you can do the same with:

\n\n
if(document.querySelector(selector))\n
\n\n

If you want to turn it into a function that returns bool:

\n\n
const exists = selector => !!document.querySelector(selector);\n\nif(exists(selector)){\n  // some code\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c4", + "creator": "Amit Sharma", + "createdAt": 1583902081000, + "text": "

By default - No.

\n

There's the length property that is commonly used for the same result in the following way:

\n
if ($(selector).length)\n
\n

Here, 'selector' is to be replaced by the actual selector you are interested to find if it exists or not. If it does exist, the length property will output an integer more than 0 and hence the if statement will become true and hence execute the if block. If it doesn't, it will output the integer '0' and hence the if block won't get executed.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c9082fcc3049e921d8", + "creator": "Amit Sharma", + "createdAt": 1583903381000, + "text": "of course there are other elegant ways to get the same result but this is one of the simplest and most effective", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e909c7", + "creator": "Haroon Fayyaz", + "createdAt": 1638712034000, + "text": "

Thanks for sharing this question. First of all, there are multiple ways to check it.\nIf you want to check whether an HTML element exists in DOM. To do so you can try the following ways.

\n
    \n
  1. Using Id selector: In DOM to select element by id, you should provide the name of the id starting with the prefix (#). You have to make sure that each Html element in the DOM must have unique id.
  2. \n
  3. Using class selector: You can select all elements with belonging to a specific class by using the prefix (.).
  4. \n
\n

Now if you want to check if the element exist or not in DOM you can check it using the following code.

\n

\r\n
\r\n
if($(\"#myId\").length){\n   //id selector\n} \n\nif($(\".myClass\").length){\n   //class selector\n} 
\r\n
\r\n
\r\n

\n

If you want to check any variable is undefined or not. You can check it using the following code.

\n

\r\n
\r\n
let x\nif(x)\n  console.log(\"X\");\nelse\n  console.log(\"X is not defined\");
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c6", + "creator": "Gareth Compton", + "createdAt": 1612377437000, + "text": "

There is an oddity known as short circuit conditioning. Not many are making this feature known so allow me to explain! <3

\n
//you can check if it isnt defined or if its falsy by using OR\nconsole.log( $(selector) || 'this value doesnt exist' )\n\n//or run the selector if its true, and ONLY true\nconsole.log( $(selector) && 'this selector is defined, now lemme do somethin!' )\n\n//sometimes I do the following, and see how similar it is to SWITCH\nconsole.log(\n({  //return something only if its in the method name\n    'string':'THIS is a string',\n    'function':'THIS is a function',\n    'number':'THIS is a number',\n    'boolean':'THIS is a boolean'\n})[typeof $(selector)]||\n//skips to this value if object above is undefined\n'typeof THIS is not defined in your search')\n
\n

The last bit allows me to see what kind of input my typeof has, and runs in that list. If there is a value outside of my list, I use the OR (||) operator to skip and nullify. This has the same performance as a Switch Case and is considered somewhat concise. Test Performance of the conditionals and uses of logical operators.

\n

Side note: The Object-Function kinda has to be rewritten >.<' But this test I built was made to look into concise and expressive conditioning.

\n

Resources:\nLogical AND (with short circuit evaluation)

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e909c8", + "creator": "Vishwesh Chotaliya", + "createdAt": 1638795439000, + "text": "

In Javascript

\n
if (typeof selector != "undefined") {\n   console.log("selector exists");\n} else {\n   console.log("selector does not exists");\n}\n
\n

In jQuery

\n
if($('selector').length){\n    alert("selector exists");\n} else{\n    alert("selector does not exists");\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 844925, + "uvac": 844970 + } + }, + { + "_id": "62f321bb082fcc3049e8feb9", + "title": "How can I validate an email address in JavaScript?", + "title-lowercase": "how can i validate an email address in javascript?", + "creator": "pix0r", + "createdAt": 1220631011000, + "status": "open", + "text": "

I'd like to check if the user input is an email address in JavaScript, before sending it to a server or attempting to send an email to it, to prevent the most basic mistyping. How could I achieve this?

\n", + "upvotes": 5760, + "upvoterUsernames": [], + "downvotes": 381, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 4181169, + "answers": 100, + "answerItems": [ + { + "_id": "62f321be082fcc3049e90177", + "creator": "Ben Scheirman", + "createdAt": 1220631195000, + "text": "

JavaScript can match a regular expression:

\n
emailAddress.match( / some_regex /);\n
\n

Here's an RFC22 regular expression for emails:

\n
^((?>[a-zA-Z\\d!#$%&'*+\\-/=?^_`{|}~]+\\x20*|"((?=[\\x01-\\x7f])[^"\\\\]|\\\\[\\x01-\\x7f])*\n"\\x20*)*(?<angle><))?((?!\\.)(?>\\.?[a-zA-Z\\d!#$%&'*+\\-/=?^_`{|}~]+)+|"((?=[\\x01-\\x\n7f])[^"\\\\]|\\\\[\\x01-\\x7f])*")@(((?!-)[a-zA-Z\\d\\-]+(?<!-)\\.)+[a-zA-Z]{2,}|\\[(((?(?<\n!\\[)\\.)(25[0-5]|2[0-4]\\d|[01]?\\d?\\d)){4}|[a-zA-Z\\d\\-]*[a-zA-Z\\d]:((?=[\\x01-\\x7f])\n[^\\\\\\[\\]]|\\\\[\\x01-\\x7f])+)\\])(?(angle)>)$\n
\n", + "upvotes": 161, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f5082fcc3049e91358", + "creator": "iPzard", + "createdAt": 1604781807000, + "text": "The match method returns an array, the test method, which returns a boolean, would be better for this situation.", + "upvotes": 310, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90178", + "creator": "John Rutherford", + "createdAt": 1220631334000, + "text": "

Using regular expressions is probably the best way. You can see a bunch of tests here (taken from chromium)

\n
const validateEmail = (email) => {\n  return String(email)\n    .toLowerCase()\n    .match(\n      /^(([^<>()[\\]\\\\.,;:\\s@"]+(\\.[^<>()[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n    );\n};\n
\n

Here's the example of a regular expression that accepts unicode:

\n
const re =\n  /^(([^<>()[\\]\\.,;:\\s@\\"]+(\\.[^<>()[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\"]{2,})$/i;\n
\n

But keep in mind that one should not rely only upon JavaScript validation. JavaScript can easily be disabled. This should be validated on the server side as well.

\n

Here's an example of the above in action:

\n

\r\n
\r\n
const validateEmail = (email) => {\n  return email.match(\n    /^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n  );\n};\n\nconst validate = () => {\n  const $result = $('#result');\n  const email = $('#email').val();\n  $result.text('');\n\n  if (validateEmail(email)) {\n    $result.text(email + ' is valid :)');\n    $result.css('color', 'green');\n  } else {\n    $result.text(email + ' is not valid :(');\n    $result.css('color', 'red');\n  }\n  return false;\n}\n\n$('#email').on('input', validate);
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n\n<label for=\"email\">Enter an email address: </label>\n<input id=\"email\" />\n<h2 id=\"result\"></h2>
\r\n
\r\n
\r\n

\n", + "upvotes": 6327, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f322f5082fcc3049e9135b", + "creator": "Randal Schwartz", + "createdAt": 1283913263000, + "text": "This regex eliminates valid, in-use emails. Do not use. Google for "RFC822" or "RFC2822" to get a proper regex.", + "upvotes": 1209, + "upvoterUsernames": [], + "downvotes": 520, + "downvoterUsernames": [] + }, + { + "_id": "62f322f5082fcc3049e9135c", + "creator": "Gautam Parmar", + "createdAt": 1628231585000, + "text": "gautam+@Gmail.com - showing is valid which should not", + "upvotes": 1011, + "upvoterUsernames": [], + "downvotes": 1011, + "downvoterUsernames": [] + }, + { + "_id": "62f322f5082fcc3049e9135e", + "creator": "Iasmini Gomes", + "createdAt": 1632486413000, + "text": "This kind of email foo@3.com is not validated by the regex from the answer. I've tested this one and it worked: ^[\\w.]+@[a-z]+.\\w{2,3}$", + "upvotes": 597, + "upvoterUsernames": [], + "downvotes": 597, + "downvoterUsernames": [] + }, + { + "_id": "62f322f5082fcc3049e91360", + "creator": "Code Cooker", + "createdAt": 1636102892000, + "text": "this is invalid sean.o'leary@cobbcounty.org and your code couldn't verify that, It says valid email.", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [] + }, + { + "_id": "62f322f5082fcc3049e91362", + "creator": "Gabriel Chaves Becchi", + "createdAt": 1651236464000, + "text": "Invalidates valid emails, such as xxxxxxx@hotmail.com", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9017a", + "creator": "Paolo Bergantino", + "createdAt": 1241284709000, + "text": "

There's something you have to understand the second you decide to use a regular expression to validate emails: It's probably not a good idea. Once you have come to terms with that, there are many implementations out there that can get you halfway there, this article sums them up nicely.

\n\n

In short, however, the only way to be absolutely, positively sure that what the user entered is in fact an email is to actually send an email and see what happens. Other than that it's all just guesses.

\n", + "upvotes": 713, + "upvoterUsernames": [], + "downvotes": 347, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9017b", + "creator": "Adam McKee", + "createdAt": 1241284786000, + "text": "

This was stolen from http://codesnippets.joyent.com/posts/show/1917

\n\n
email = $('email');\nfilter = /^([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$/;\nif (filter.test(email.value)) {\n  // Yay! valid\n  return true;\n}\nelse\n  {return false;}\n
\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9017c", + "creator": "Vikram", + "createdAt": 1241284809000, + "text": "

Following regular expression:

\n\n
/^([\\w]+)(.[\\w]+)*@([\\w]+)(.[\\w]{2,3}){1,2}$/;\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f6082fcc3049e91366", + "creator": "tchrist", + "createdAt": 1373826934000, + "text": "That does not match the RFC.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9017d", + "creator": "jacobangel", + "createdAt": 1241284983000, + "text": "

It's hard to get an email validator 100% correct. The only real way to get it correct would be to send a test email to the account. That said, there are a few basic checks that can help make sure that you're getting something reasonable.

\n\n

Some things to improve:

\n\n

Instead of new RegExp, just try writing the regexp out like this:

\n\n
if (reg.test(/@/))\n
\n\n

Second, check to make sure that a period comes after the @ sign, and make sure that there are characters between the @s and periods.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9017e", + "creator": "Esteban Küber", + "createdAt": 1251987969000, + "text": "

Just for completeness, here you have another RFC 2822 compliant regex

\n
\n

The official standard is known as RFC 2822. It describes the syntax that valid email addresses must adhere to. You can (but you shouldn'tread on) implement it with this regular expression:

\n

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])

\n

(...) We get a more practical implementation of RFC 2822 if we omit the syntax using double quotes and square brackets. It will still match 99.99% of all email addresses in actual use today.

\n

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

\n

A further change you could make is to allow any two-letter country code top level domain, and only specific generic top level domains. This regex filters dummy email addresses like asdf@adsf.adsf. You will need to update it as new top-level domains are added.

\n

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\\b

\n

So even when following official standards, there are still trade-offs to be made. Don't blindly copy regular expressions from online libraries or discussion forums. Always test them on your own data and with your own applications.

\n
\n

Emphasis mine

\n", + "upvotes": 1725, + "upvoterUsernames": [], + "downvotes": 845, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f6082fcc3049e9136a", + "creator": "Cristian Traìna", + "createdAt": 1654521898000, + "text": "@Toastrackenigma if someone is using an emoji email doesn't deserve to subscribe to my website. As easy as that.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90180", + "creator": "Félix Saparelli", + "createdAt": 1285925851000, + "text": "

Apparently, that's it:

\n\n
/^([\\w\\!\\#$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`{\\|\\}\\~]+\\.)*[\\w\\!\\#$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`{\\|\\}\\~]+@((((([a-z0-9]{1}[a-z0-9\\-]{0,62}[a-z0-9]{1})|[a-z])\\.)+[a-z]{2,6})|(\\d{1,3}\\.){3}\\d{1,3}(\\:\\d{1,5})?)$/i\n
\n\n

Taken from http://fightingforalostcause.net/misc/2006/compare-email-regex.php on Oct 1 '10.

\n\n

But, of course, that's ignoring internationalization.

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9017f", + "creator": "Eric Schoonover", + "createdAt": 1275084345000, + "text": "

Here is a very good discussion about using regular expressions to validate email addresses; \"Comparing E-mail Address Validating Regular Expressions\"

\n\n

Here is the current top expression, that is JavaScript compatible, for reference purposes:

\n\n
/^[-a-z0-9~!$%^&*_=+}{\\'?]+(\\.[-a-z0-9~!$%^&*_=+}{\\'?]+)*@([a-z0-9_][-a-z0-9_]*(\\.[-a-z0-9_]+)*\\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,5})?$/i\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f6082fcc3049e9136d", + "creator": "Slbox", + "createdAt": 1630695736000, + "text": "Extremely outdated now.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90181", + "creator": "Miloš Rašić", + "createdAt": 1299065162000, + "text": "

Correct validation of email address in compliance with the RFCs is not something that can be achieved with a one-liner regular expression. An article with the best solution I've found in PHP is What is a valid email address?. Obviously, it has been ported to Java. I think the function is too complex to be ported and used in JavaScript. JavaScript/node.js port: https://www.npmjs.com/package/email-addresses.

\n\n

A good practice is to validate your data on the client, but double-check the validation on the server. With this in mind, you can simply check whether a string looks like a valid email address on the client and perform the strict check on the server.

\n\n

Here's the JavaScript function I use to check if a string looks like a valid mail address:

\n\n
function looksLikeMail(str) {\n    var lastAtPos = str.lastIndexOf('@');\n    var lastDotPos = str.lastIndexOf('.');\n    return (lastAtPos < lastDotPos && lastAtPos > 0 && str.indexOf('@@') == -1 && lastDotPos > 2 && (str.length - lastDotPos) > 2);\n}\n
\n\n

Explanation:

\n\n\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90182", + "creator": "Anoop", + "createdAt": 1318839814000, + "text": "

HTML5 itself has email validation. If your browser supports HTML5 then you can use the following code.

\n
<form>\n  <label>Email Address\n    <input type="email" placeholder="me@example.com" required>\n  </label>\n  <input type="submit">\n</form>\n
\n

jsFiddle link

\n

From the HTML5 spec:

\n
\n

A valid e-mail address is a string that matches the email production of the following ABNF, the character set for which is Unicode.

\n
email   = 1*( atext / "." ) "@" label *( "." label )\nlabel   = let-dig [ [ ldh-str ] let-dig ]  ; limited to a length of 63 characters by RFC 1034 section 3.5\natext   = < as defined in RFC 5322 section 3.2.3 >\nlet-dig = < as defined in RFC 1034 section 3.5 >\nldh-str = < as defined in RFC 1034 section 3.5 >\n
\n

This requirement is a willful violation of RFC 5322, which defines a syntax for e-mail addresses that is simultaneously too strict (before the "@" character), too vague (after the "@" character), and too lax (allowing comments, whitespace characters, and quoted strings in manners unfamiliar to most users) to be of practical use here.

\n

The following JavaScript- and Perl-compatible regular expression is an implementation of the above definition.

\n
/^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/\n
\n
\n", + "upvotes": 535, + "upvoterUsernames": [], + "downvotes": 250, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f6082fcc3049e91371", + "creator": "KNP", + "createdAt": 1638815187000, + "text": "@ThomasChampion Multiple "@" IS valid. First point is valid though.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90179", + "creator": "Jaymon", + "createdAt": 1220822506000, + "text": "

Wow, there are lots of complexity here. If all you want to do is just catch the most obvious syntax errors, I would do something like this:

\n
^\\S+@\\S+$\n
\n

It usually catches the most obvious errors that the user makes and assures that the form is mostly right, which is what JavaScript validation is all about.

\n

EDIT:\nWe can also check for '.' in the email using

\n
/^\\S+@\\S+\\.\\S+$/\n
\n", + "upvotes": 805, + "upvoterUsernames": [], + "downvotes": 317, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f6082fcc3049e91374", + "creator": "Mohit Atray", + "createdAt": 1625774230000, + "text": "But it won't accept "Mohit Atray"@gmail.com because it contains space character. Maybe we should just use /^\\S.*@\\S+$/ regex.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f322f6082fcc3049e91376", + "creator": "Merc", + "createdAt": 1647162973000, + "text": "This is the best answer", + "upvotes": 1029, + "upvoterUsernames": [], + "downvotes": 1029, + "downvoterUsernames": [] + }, + { + "_id": "62f322f6082fcc3049e91378", + "creator": "Randal Schwartz", + "createdAt": 1655849428000, + "text": "That's still not valid. Spaces are permitted in the local-part as long as they are properly escaped (in double quotes).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90183", + "creator": "Zo72", + "createdAt": 1324319942000, + "text": "

You should not use regular expressions to validate an input string to check if it's an email. It's too complicated and would not cover all the cases.

\n\n

Now since you can only cover 90% of the cases, write something like:

\n\n
function isPossiblyValidEmail(txt) {\n   return txt.length > 5 && txt.indexOf('@')>0;\n}\n
\n\n

You can refine it. For instance, 'aaa@' is valid. But overall you get the gist. And don't get carried away... A simple 90% solution is better than 100% solution that does not work.

\n\n

The world needs simpler code...

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e9137a", + "creator": "cazlab", + "createdAt": 1325891264000, + "text": "This allows the entry of so many invalid email addresses it is useless advice.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90185", + "creator": "Steve C", + "createdAt": 1338939719000, + "text": "

In contrast to squirtle, here is a complex solution, but it does a mighty fine job of validating emails properly:

\n\n
function isEmail(email) { \n    return /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i.test(email);\n} \n
\n\n

Use like so:

\n\n
if (isEmail('youremail@yourdomain.com')){ console.log('This is email is valid'); }\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90184", + "creator": "C. Lee", + "createdAt": 1328754152000, + "text": "

I've slightly modified Jaymon's answer for people who want really simple validation in the form of:

\n
anystring@anystring.anystring\n
\n

The regular expression:

\n
/\\S+@\\S+\\.\\S+/\n
\n

To prevent matching multiple @ signs:

\n
/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n
\n

Example JavaScript function:

\n

\r\n
\r\n
function validateEmail(email) \n    {\n        var re = /\\S+@\\S+\\.\\S+/;\n        return re.test(email);\n    }\n    \nconsole.log(validateEmail('anystring@anystring.anystring'));
\r\n
\r\n
\r\n

\n", + "upvotes": 1308, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e9137e", + "creator": "ruohola", + "createdAt": 1633529324000, + "text": "Emails can contain multiple @ signs (as comments), also an email doesn't have to contain a period.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91380", + "creator": "Jose G.", + "createdAt": 1644484244000, + "text": "@ruohola is this also the reason why an email will be valid after you input an @ sign without having any periods after?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91382", + "creator": "ruohola", + "createdAt": 1644486179000, + "text": "@JoseG. Yes. E.g. http://ai is someone's valid domain, so they could use e.g. a@ai as their email.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90186", + "creator": "Darren Cato", + "createdAt": 1346338843000, + "text": "

Sectrean's solution works great, but it was failing my linter. So I added some escapes:

\n\n
function validateEmail(email){ \n     var re = /^(([^<>()[]\\\\.,;:\\s@\\\"]+(\\.[^<>()[]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@(([[0-9]{1,3}\\‌​.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/; \n     return re.test(email); \n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e91384", + "creator": "Ikrom", + "createdAt": 1382436253000, + "text": "validateEmail("e_mail@sth.com") and validateEmail("e.mail@sth.com") both return false value", + "upvotes": 897, + "upvoterUsernames": [], + "downvotes": 897, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90188", + "creator": "Ryan Taylor", + "createdAt": 1351778787000, + "text": "

Do this:

\n
^([a-zA-Z0-9!#$%&'*+\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)$\n
\n

It's based on RFC 2822

\n

Test it at https://regex101.com/r/857lzc/1

\n

Often when storing email addresses in the database I make them lowercase and, in practice, regexs can usually be marked case insensitive. In those cases this is slightly shorter:

\n
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\n
\n

Here's an example of it being used in JavaScript (with the case insensitive flag i at the end).

\n
var emailCheck=/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\nconsole.log( emailCheck.test('some.body@domain.co.uk') );\n
\n

Note:
\nTechnically some emails can include quotes in the section before the @ symbol with escape characters inside the quotes (so your email user can be obnoxious and contain stuff like @ and "..." as long as it's written in quotes). NOBODY DOES THIS EVER! It's obsolete. But, it IS included in the true RFC 2822 standard and omitted here.

\n

Note 2:\nThe beginning of an email (before the @ sign) can be case sensitive (via the spec). However, anyone with a case-sensitive email is probably used to having issues, and, in practice, case insensitive is a safe assumption. More info: Are email addresses case sensitive?

\n

More info: http://www.regular-expressions.info/email.html

\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e91387", + "creator": "Gautam Parmar", + "createdAt": 1628231580000, + "text": "gautam+@Gmail.com - showing is valid which should not", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91388", + "creator": "TylerH", + "createdAt": 1651670404000, + "text": "@GautamParmar gmail and others ignore symbols; mail sent to gautam+@gmail.com would end up in gautam@gmail.com's email inbox.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90187", + "creator": "amit kate", + "createdAt": 1350298893000, + "text": "
function validateEmail(elementValue){        \n    var emailPattern = /^[a-zA-Z0-9._]+[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]{2,4}$/;  \n    return emailPattern.test(elementValue);   \n  }   \n
\n\n

It returns true if the email address is valid. Otherwise, it will return false.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9018a", + "creator": "Tugrul", + "createdAt": 1359625199000, + "text": "

\r\n
\r\n
var testresults\n\nfunction checkemail() {\n  var str = document.validation.emailcheck.value\n  var filter = /^([\\w-]+(?:\\.[\\w-]+)*)@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([a-z]{2,6}(?:\\.[a-z]{2})?)$/i\n  if (filter.test(str))\n    testresults = true\n  else {\n    alert(\"Please input a valid email address!\")\n    testresults = false\n  }\n  return (testresults)\n}\n\nfunction checkbae() {\n  if (document.layers || document.getElementById || document.all)\n    return checkemail()\n  else\n    return true\n}
\r\n
<form name=\"validation\" onSubmit=\"return checkbae()\">\n  Please input a valid email address:<br />\n\n  <input type=\"text\" size=18 name=\"emailcheck\">\n  <input type=\"submit\" value=\"Submit\">\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90189", + "creator": "Boldewyn", + "createdAt": 1356016853000, + "text": "

In modern browsers you can build on top of @Sushil's answer with pure JavaScript and the DOM:

\n\n
function validateEmail(value) {\n  var input = document.createElement('input');\n\n  input.type = 'email';\n  input.required = true;\n  input.value = value;\n\n  return typeof input.checkValidity === 'function' ? input.checkValidity() : /\\S+@\\S+\\.\\S+/.test(value);\n}\n
\n\n

I've put together an example in the fiddle http://jsfiddle.net/boldewyn/2b6d5/. Combined with feature detection and the bare-bones validation from Squirtle's Answer, it frees you from the regular expression massacre and does not bork on old browsers.

\n", + "upvotes": 195, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e9138c", + "creator": "Boldewyn", + "createdAt": 1641143928000, + "text": "That’s what the input.required = true; line should take care of ;-)", + "upvotes": 1101, + "upvoterUsernames": [], + "downvotes": 1101, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e9138e", + "creator": "marcb", + "createdAt": 1643474999000, + "text": "HTML5 'email' type accepts an empty string incase the field is not requierd. But if user decide to enter the email, it need to be valide.", + "upvotes": 273, + "upvoterUsernames": [], + "downvotes": 273, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91390", + "creator": "Boldewyn", + "createdAt": 1643548705000, + "text": "I might repeat myself, but have you noticed the input.required = true; line?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91391", + "creator": "oelna", + "createdAt": 1649601955000, + "text": "Also worth thinking about: It's robust against future changes, as it doesn't hard-code the actual regex. I like this aspect.", + "upvotes": 357, + "upvoterUsernames": [], + "downvotes": 357, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9018b", + "creator": "Linkmichiel", + "createdAt": 1366026800000, + "text": "

My knowledge of regular expressions is not that good. That's why I check the general syntax with a simple regular expression first and check more specific options with other functions afterwards. This may not be not the best technical solution, but this way I'm way more flexible and faster.

\n\n

The most common errors I've come across are spaces (especially at the beginning and end) and occasionally a double dot.

\n\n
function check_email(val){\n    if(!val.match(/\\S+@\\S+\\.\\S+/)){ // Jaymon's / Squirtle's solution\n        // Do something\n        return false;\n    }\n    if( val.indexOf(' ')!=-1 || val.indexOf('..')!=-1){\n        // Do something\n        return false;\n    }\n    return true;\n}\n\ncheck_email('check@thiscom'); // Returns false\ncheck_email('check@this..com'); // Returns false\ncheck_email(' check@this.com'); // Returns false\ncheck_email('check@this.com'); // Returns true\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9018c", + "creator": "Bob van Luijt", + "createdAt": 1366032688000, + "text": "

This is the correct RFC822 version.

\n\n
function checkEmail(emailAddress) {\n  var sQtext = '[^\\\\x0d\\\\x22\\\\x5c\\\\x80-\\\\xff]';\n  var sDtext = '[^\\\\x0d\\\\x5b-\\\\x5d\\\\x80-\\\\xff]';\n  var sAtom = '[^\\\\x00-\\\\x20\\\\x22\\\\x28\\\\x29\\\\x2c\\\\x2e\\\\x3a-\\\\x3c\\\\x3e\\\\x40\\\\x5b-\\\\x5d\\\\x7f-\\\\xff]+';\n  var sQuotedPair = '\\\\x5c[\\\\x00-\\\\x7f]';\n  var sDomainLiteral = '\\\\x5b(' + sDtext + '|' + sQuotedPair + ')*\\\\x5d';\n  var sQuotedString = '\\\\x22(' + sQtext + '|' + sQuotedPair + ')*\\\\x22';\n  var sDomain_ref = sAtom;\n  var sSubDomain = '(' + sDomain_ref + '|' + sDomainLiteral + ')';\n  var sWord = '(' + sAtom + '|' + sQuotedString + ')';\n  var sDomain = sSubDomain + '(\\\\x2e' + sSubDomain + ')*';\n  var sLocalPart = sWord + '(\\\\x2e' + sWord + ')*';\n  var sAddrSpec = sLocalPart + '\\\\x40' + sDomain; // complete RFC822 email address spec\n  var sValidEmail = '^' + sAddrSpec + '$'; // as whole string\n\n  var reValidEmail = new RegExp(sValidEmail);\n\n  return reValidEmail.test(emailAddress);\n}\n
\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f7082fcc3049e91394", + "creator": "D.A.H", + "createdAt": 1408297118000, + "text": "IDN addresses are not validated (info@üpöü.com)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91396", + "creator": "pmiranda", + "createdAt": 1612289934000, + "text": "'a@a' returns valid: jsfiddle.net/pmiranda/guoyh4dv", + "upvotes": 303, + "upvoterUsernames": [], + "downvotes": 303, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91398", + "creator": "ruohola", + "createdAt": 1632913284000, + "text": "@pmiranda That is a valid email address.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e91399", + "creator": "ruohola", + "createdAt": 1632919225000, + "text": "@pmiranda E.g. http://ai is someone's valid domain, so they could use e.g. a@ai as their email.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f7082fcc3049e9139a", + "creator": "Bob van Luijt", + "createdAt": 1632924196000, + "text": "Agreed with @ruohola – this is a valid address", + "upvotes": 4700, + "upvoterUsernames": [], + "downvotes": 4700, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9018d", + "creator": "user2496033", + "createdAt": 1372141816000, + "text": "

Simply check out if the entered email address is valid or not using HTML.

\n\n
<input type=\"email\"/>\n
\n\n

There isn't any need to write a function for validation.

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9018e", + "creator": "Colonel Panic", + "createdAt": 1373825744000, + "text": "

All email addresses contain an 'at' (i.e. @) symbol. Test that necessary condition:

\n
email.includes('@')\n
\n

Or, if you need to support IE/older browsers:

\n
email.indexOf('@') > 0\n
\n

Don't bother with anything more complicated. Even if you could perfectly determine whether an email is RFC-syntactically valid, that wouldn't tell you whether it belongs to the person who supplied it. That's what really matters.

\n

To test that, send a validation message.

\n", + "upvotes": 133, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f8082fcc3049e9139c", + "creator": "iwazovsky", + "createdAt": 1430043250000, + "text": "what if there will be more than one '@' symbol? other restricted symbols? This validation cannot be trusted...", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f8082fcc3049e9139e", + "creator": "Aravin", + "createdAt": 1608459444000, + "text": "is a@b valid email ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322f8082fcc3049e913a0", + "creator": "ruohola", + "createdAt": 1632913308000, + "text": "@Aravin Yes It is.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9018f", + "creator": "Simon Steele", + "createdAt": 1378816630000, + "text": "

If you're using Closure you can use the built-in goog.format.EmailAddress type:

\n\n
\n

http://docs.closure-library.googlecode.com/git/class_goog_format_EmailAddress.html

\n
\n\n

For example:

\n\n
goog.format.EmailAddress.isValidAddrSpec(\"blah@blah.com\")\n
\n\n

Note that by reading the source (linked above) you can see the comments state that IDN are not supported and that it only aims to cover most addresses:

\n\n
// This is a fairly naive implementation, but it covers 99% of use cases.\n// For more details, see http://en.wikipedia.org/wiki/Email_address#Syntax\n// TODO(mariakhomenko): we should also be handling i18n domain names as per\n// http://en.wikipedia.org/wiki/Internationalized_domain_name\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90190", + "creator": "pera", + "createdAt": 1378866100000, + "text": "

This is how node-validator does it:

\n\n
/^(?:[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$/\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90192", + "creator": "Orchid", + "createdAt": 1385727974000, + "text": "

Use this code inside your validator function:

\n\n
var emailID = document.forms[\"formName\"][\"form element id\"].value;\natpos = emailID.indexOf(\"@\");\ndotpos = emailID.lastIndexOf(\".\");\nif (atpos < 1 || ( dotpos - atpos < 2 ))\n{\n    alert(\"Please enter correct email ID\")\n    return false;\n}\n
\n\n

Else you can use jQuery. Inside rules define:

\n\n
eMailId: {\n    required: true,\n    email: true\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90191", + "creator": "Calahad", + "createdAt": 1383261826000, + "text": "

W3Schools gives a good simple and efficient script to validate an email:

\n\n
function validateEmail(email) {\n    var atpos=email.indexOf(\"@\");\n    var dotpos=email.lastIndexOf(\".\");\n    if (atpos < 1 || dotpos < atpos+2 || dotpos+2 >= email.length) {\n        alert(\"Not a valid e-mail address\");\n        return false;\n    }\n    return true;\n}\n
\n\n

Note that you will have to remove spaces if there are any though, with something like this:

\n\n
.replace(/ /g,'')\n
\n\n

Source: JavaScript Form Validation

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90194", + "creator": "Ferrakkem Bhuiyan", + "createdAt": 1390887607000, + "text": "
<pre>\n**The personal_info part contains the following ASCII characters.\n1.Uppercase (A-Z) and lowercase (a-z) English letters.\n2.Digits (0-9).\n3.Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~\n4.Character . ( period, dot or fullstop) provided that it is not the first or last character and it will not come one after the other.**\n</pre>\n*Example of valid email id*\n<pre>\nyoursite@ourearth.com\nmy.ownsite@ourearth.org\nmysite@you.me.net\nxxxx@gmail.com\nxxxxxx@yahoo.com\n</pre>\n<pre>\nxxxx.ourearth.com [@ is not present] \nxxxx@.com.my [ tld (Top Level domain) can not start with dot \".\" ]\n@you.me.net [ No character before @ ]\nxxxx123@gmail.b [ \".b\" is not a valid tld ]\nxxxx@.org.org [ tld can not start with dot \".\" ]\n.xxxx@mysite.org [ an email should not be start with \".\" ]\nxxxxx()*@gmail.com [ here the regular expression only allows character, digit, underscore and dash ]\nxxxx..1234@yahoo.com [double dots are not allowed\n</pre>\n**javascript mail code**\n\n    function ValidateEmail(inputText)\n    {\n    var mailformat = /^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/;\n    if(inputText.value.match(mailformat))\n    {\n    document.form1.text1.focus();\n    return true;\n    }\n    else\n    {\n    alert(\"You have entered an invalid email address!\");\n    document.form1.text1.focus();\n    return false;\n    }\n    }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90193", + "creator": "Neil Thompson", + "createdAt": 1386153871000, + "text": "

The regular expression provided by Microsoft within ASP.NET MVC is

\n\n
/^[\\w-]+(\\.[\\w-]+)*@([a-z0-9-]+(\\.[a-z0-9-]+)*?\\.[a-z]{2,6}|(\\d{1,3}\\.){3}\\d{1,3})(:\\d{4})?$/\n
\n\n

Which I post here in case it's flawed - though it's always been perfect for my needs.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f8082fcc3049e913a7", + "creator": "Paul Go", + "createdAt": 1425914387000, + "text": "Doesn't allow +'s in the name part of the email.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90195", + "creator": "Sakthi Karthik", + "createdAt": 1393411761000, + "text": "

Very simple in JavaScript. follow this code.

\n\n
function validate(){\n    var email = document.getElementById('Email');\n    var filter = /^([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$/;\n\n    if (!filter.test(email.value))\n    {\n        alert('Please Enter the valid email address');\n        email.focus;\n        return false;\n    }\n    else\n{\n        return true;\n    }\n
\n\n

HTML code for this:

\n\n
form name=\"form\"\n     enctype=\"multipart/form-data\"\n     name=\"form\"\n     action=\"register.php\"\n     method=\"POST\" onsubmit=\"return validate();\" >\n    <input type=\"text\" placeholder=\"Enter ur Email Id\" id=\"Email\" name=\"Email\" />\n    <input type=\"submit\" id=\"submit\" value=\"save\" name=\"Like\" class=\"button\" />\n</form>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f8082fcc3049e913aa", + "creator": "kjpires", + "createdAt": 1393614237000, + "text": "This filter invalidates many common valid e-mail addresses... For example: user+tag@gmail.com", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90196", + "creator": "Yogesh Jindal", + "createdAt": 1405103793000, + "text": "

One of my coworker shared this regex with me. I like it a lot.

\n\n
function isValidEmailAddress (email) {\n    var validEmail = false;\n    if (email) {\n        email = email.trim().toLowerCase();\n        var pattern = /^[\\w-']+(\\.[\\w-']+)*@([a-zA-Z0-9]+[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*?\\.[a-zA-Z]{2,6}|(\\d{1,3}\\.){3}\\d{1,3})(:\\d{4})?$/;\n        validEmail = pattern.exec(email);\n    }\n\n    return validEmail;\n}\n\nif (typeof String.prototype.trim !== 'function') {\n    String.prototype.trim = function() {\n        return this.replace(/^\\s+|\\s+$/g, '');\n    };\n}\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90198", + "creator": "zeros-and-ones", + "createdAt": 1407177238000, + "text": "

Here is a function I use for front end email validation. (The Regular Expression came from parsley.js)

\n\n
<!DOCTYPE html>\n<html>\n<head>\n    <title>Our Company</title>\n    <style>\n        .form-style {\n            color: #ccc;\n        }\n    </style>\n</head>\n<body>\n    <h1>Email Validation Form Example</h1>\n    <input type=\"text\" name=\"email\" id=\"emailInput\" class=\"form-style\">\n    <script>\n        function validateEmail(emailAddress) {\n            var regularExpression = /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))){2,6}$/i;\n             return regularExpression.test(emailAddress);\n        }\n\n        function showEmailValidationState(event) {\n            if (validateEmail(event.target.value)) {\n                document.getElementById(\"emailInput\").style.color = 'black';\n            }\n        }\n    document.getElementById(\"emailInput\").addEventListener(\"keyup\", showEmailValidationState);\n    </script>\n</body>\n</html>\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90197", + "creator": "Racoon", + "createdAt": 1405607968000, + "text": "
function validatecontactEmail(email) { \n\n if (/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(email))  \n  {  \n    return (true)  \n  }  \n\n    return (false)  \n\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f9082fcc3049e913ae", + "creator": "Michael Schilling", + "createdAt": 1415935472000, + "text": "How about just return /^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(email)?", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90199", + "creator": "Vitalii Fedorenko", + "createdAt": 1418606472000, + "text": "

If you are using AngularJS, just add type=\"email\" to the input element:

\n\n
\n

https://docs.angularjs.org/api/ng/input/input%5Bemail%5D

\n
\n\n

In case there is no input element, it can be created dynamically:

\n\n
var isEmail = $compile('<input ng-model=\"m\" type=\"email\">')($rootScope.$new()).\n    controller('ngModel').$validators[\"email\"];\n\nif (isEmail('email@gmail.com')) {\n  console.log('valid');\n} \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f9082fcc3049e913b1", + "creator": "Bergi", + "createdAt": 1418609252000, + "text": "But what is this email parser function? Could you post it so that one can use it without angular?", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f322f9082fcc3049e913b2", + "creator": "Eric Bishard", + "createdAt": 1440382179000, + "text": "Yes, if you are going to post a black box answer please dig up the RegEx behind the email parser function and show us what it's using..", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9019a", + "creator": "Anil Singhania", + "createdAt": 1421799654000, + "text": "

Validation regex for email:

\n\n
var rex_email = /^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n\nif(email==\"\") {\n    window.plugins.toast.showShortBottom( \"Please enter the details. \", function(a) {\n        console.log('toast success: ' + a);\n    }, function(b) { });\n} else if(!rex_email.test(email)) {\n    window.plugins.toast.showShortBottom( \"Please enter the valid email id. \", function(a) {\n        console.log('toast success: ' + a);\n    }, function(b) { });\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9019b", + "creator": "user2081554", + "createdAt": 1424248431000, + "text": "

I know its not regex but any way...

\n\n

This is example with node and npm package email-existence this is ultimate checking if email exist and if its in the right form :)

\n\n

This will ping the email if its responding if it got no response it will return false or else true.

\n\n
function doesEmailExist(email) {\n    var emailExistence = require('email-existence');\n    return emailExistence.check(email,function (err,status) {\n            if (status) {\n                return status;\n            }\n            else {\n                throw new Error('Email does not exist');\n            }\n        });\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9019c", + "creator": "Code Spy", + "createdAt": 1425280761000, + "text": "

Following Regex validations:

\n\n\n\n\n\n
function validateEmail(email) {\nvar chrbeforAt = email.substr(0, email.indexOf('@'));\nif (!($.trim(email).length > 127)) {\n    if (chrbeforAt.length >= 2) {\n        var re = /^(([^<>()[\\]{}'^?\\\\.,!|//#%*-+=&;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;\n        //var re = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;\n        return re.test(email);\n    } else {\n        return false;\n    }\n} else {\n    return false;\n}\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9019d", + "creator": "Code Spy", + "createdAt": 1425280862000, + "text": "

Following Regex validations:

\n\n\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9019e", + "creator": "jaydev thakkar", + "createdAt": 1436429959000, + "text": "
\\b[a-z][\\w\\d_\\.]+@\\w+\\.[a-z]{2}[a-z]?\\.?[a-z]{,2}\\s\n
\n\n

It allows:

\n\n
abcxyz123@qwert.com    \nabc123xyz@asdf.co.in   \nabc1_xyz1@gmail1.com   \nabc.xyz@gmail.com.in\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9019f", + "creator": "DestyNova", + "createdAt": 1439398732000, + "text": "

the best one :D (RFC-friendly & no error \"too complex\") :

\n\n
function    isMail(mail)\n{\n    pattuser = /^([A-Z0-9_%+\\-!#$&'*\\/=?^`{|}~]+\\.?)*[A-Z0-9_%+\\-!#$&'*\\/=?^`{|}~]+$/i;\n    pattdomain = /^([A-Z0-9-]+\\.?)*[A-Z0-9-]+(\\.[A-Z]{2,9})+$/i;\n\n    tab = mail.split(\"@\");\n    if (tab.length != 2)\n        return false;\n    return (pattuser.test(tab[0]) && pattdomain.test(tab[1]));\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a0", + "creator": "Dinesh Devkota", + "createdAt": 1440512685000, + "text": "

If you want to use Jquery and want to have modern approach then use JQuery input mask with validation.

\n\n

http://bseth99.github.io/projects/jquery-ui/5-jquery-masks.html

\n\n

Demo on how simple jquery input mask is here: http://codepen.io/anon/pen/gpRyBp

\n\n

Example of simple input mask for date forexample NOT full validation

\n\n
 <input id=\"date\" type=\"text\" placeholder=\"YYYY-MM-DD\"/>\n
\n\n

and the script:

\n\n
 $(\"#date\").mask(\"9999-99-99\",{placeholder:\"YYYY-MM-DD\"});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ab", + "creator": "bman", + "createdAt": 1482893497000, + "text": "

A solution that does not check the existence of the TLD is incomplete.

\n\n

Almost all answers to this questions suggest using Regex to validate emails addresses. I think Regex is only good for a rudimentary validation. It seems that the checking validation of email addresses is actually two separate problems:

\n\n

1- Validation of email format: Making sure if the email complies with the format and pattern of emails in RFC 5322 and if the TLD actually exists. A list of all valid TLDs can be found here.

\n\n

For example, although the address example@example.ccc will pass the regex, it is not a valid email, because ccc is not a top-level domain by IANA.

\n\n

2- Making sure the email actually exists: For doing this, the only option is to send the users an email.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ac", + "creator": "Negin", + "createdAt": 1483421923000, + "text": "

I was looking for a Regex in JS that passes all Email Address test cases:

\n\n\n\n

Here we go :

\n\n

http://regexr.com/3f07j

\n\n

OR regex:

\n\n
Regex = /(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@[*[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+]*/\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ae", + "creator": "Jangli Coder", + "createdAt": 1489823831000, + "text": "

If you define your regular expression as a string then all backslashes need to be escaped, so instead of '\\w' you should have '\\w'.

\n\n

Alternatively, define it as a regular expression:

\n\n
var pattern = /^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$/; \n
\n", + "upvotes": 3512, + "upvoterUsernames": [], + "downvotes": 3512, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ad", + "creator": "Himanshu Teotia", + "createdAt": 1486965870000, + "text": "

In nodeJS you can also use validator node module and simply use like that

\n\n

Install the library with npm install validator

\n\n
var validator = require('validator');\n\nvalidator.isEmail('foo@bar.com'); //=> true \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b0", + "creator": "takatan", + "createdAt": 1496389929000, + "text": "

I'd like to add a short note about non-ASCII characters. Rnevius's (and co.) solution is brilliant, but it allows to add Cyrillic, Japanese, Emoticons and other unicode symbols which may be restricted by some servers.

\n\n

The code below will print true though it contains UTF-8 character Ё.

\n\n

\r\n
\r\n
console.log (/^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/.test ('Ё@example.org'))
\r\n
\r\n
\r\n

\n\n

In my case all non-ASCII symbols are prohibited so I have modified the original expression to exclude all characters above U+007F:

\n\n

\r\n
\r\n
/^(([^\\u0080-\\uffff<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^\\u0080-\\uffff<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/
\r\n
\r\n
\r\n

\n\n

Maybe this will help someone to prevent undesired behaviour.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32337082fcc3049e913be", + "creator": "jcollum", + "createdAt": 1502646494000, + "text": "Nah I think this is an unusual enough use case that it should be its own answer", + "upvotes": 736, + "upvoterUsernames": [], + "downvotes": 736, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901af", + "creator": "Abhay Shiro", + "createdAt": 1491452861000, + "text": "

There are some complex RegEx written here, that also works.

\n\n

I tested this one and it works too:

\n\n
[a-zA-Z0-9._]+[@]+[a-zA-Z0-9]+[.]+[a-zA-Z]{2,6}\n
\n\n

Please test this here : http://www.regextester.com/?fam=97334

\n\n

Hope this helps.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32337082fcc3049e913c1", + "creator": "Agi Hammerthief", + "createdAt": 1517519322000, + "text": "me@localhost fails.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901b2", + "creator": "Liberateur", + "createdAt": 1498036218000, + "text": "

Wikipedia standard mail syntax :

\n

https://en.wikipedia.org/wiki/Email_address#Examples\nhttps://fr.wikipedia.org/wiki/Adresse_%C3%A9lectronique#Syntaxe_exacte

\n

Function :

\n
function validMail(mail)\n{\n    return /^(([^<>()\\[\\]\\.,;:\\s@\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()\\.,;\\s@\\"]+\\.{0,1})+([^<>()\\.,;:\\s@\\"]{2,}|[\\d\\.]+))$/.test(mail);\n}\n
\n

Valids mails :

\n
validMail('Abc@example.com') // Return true\nvalidMail('Abc@example.com.') // Return true\nvalidMail('Abc@10.42.0.1') // Return true\nvalidMail('user@localserver') // Return true\nvalidMail('Abc.123@example.com') // Return true\nvalidMail('user+mailbox/department=shipping@example.com') // Return true\nvalidMail('"very.(),:;<>[]\\".VERY.\\"very@\\\\ \\"very\\".unusual"@strange.example.com') // Return true\nvalidMail('!#$%&\\'*+-/=?^_`.{|}~@example.com') // Return true\nvalidMail('"()<>[]:,;@\\\\\\"!#$%&\\'-/=?^_`{}| ~.a"@example.org') // Return true\nvalidMail('"Abc@def"@example.com') // Return true\nvalidMail('"Fred Bloggs"@example.com') // Return true\nvalidMail('"Joe.\\\\Blow"@example.com') // Return true\nvalidMail('Loïc.Accentué@voilà.fr') // Return true\nvalidMail('" "@example.org') // Return true\nvalidMail('user@[IPv6:2001:DB8::1]') // Return true\n
\n

Invalids mails :

\n
validMail('Abc.example.com') // Return false\nvalidMail('A@b@c@example.com') // Return false\nvalidMail('a"b(c)d,e:f;g<h>i[j\\k]l@example.com') // Return false\nvalidMail('just"not"right@example.com') // Return false\nvalidMail('this is"not\\allowed@example.com') // Return false\nvalidMail('this\\ still\\"not\\\\allowed@example.com') // Return false\nvalidMail('john..doe@example.com') // Return false\nvalidMail('john.doe@example..com') // Return false\n
\n

Show this test : https://regex101.com/r/LHJ9gU/1

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b1", + "creator": "Alireza", + "createdAt": 1497682544000, + "text": "

How about creating a function which will test any string against emails' pattern using regular expression in JavaScript, as we know email addresses can be quite different in different regions, like in UK and Australia it usually ends up with .co.uk or .com.au, so I tried to cover those as well, also check if the string passed to the function, something like this:

\n\n
var isEmail = function(str) {\n  return typeof str==='string' && /^[\\w+\\d+._]+\\@[\\w+\\d+_+]+\\.[\\w+\\d+._]{2,8}$/.test(str);\n}\n
\n\n

and check if it's email like below:

\n\n
isEmail('alex@example.com'); //true\nisEmail('alireza@test.co.uk'); //true\nisEmail('peter.example@yahoo.com.au'); //true\nisEmail('alex@example.com'); //true\nisEmail('peter_123@news.com'); //true\nisEmail('hello7___@ca.com.pt'); //true\nisEmail('example@example.co'); //true\nisEmail('hallo@example.coassjj#sswzazaaaa'); //false\nisEmail('hallo2ww22@example....caaaao'); //false\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32337082fcc3049e913c5", + "creator": "Agi Hammerthief", + "createdAt": 1517519003000, + "text": "What happens with alireza@test.uk or phil.h\\@\\@cked+liked~it@haacked.com?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901b4", + "creator": "Kirit Modi", + "createdAt": 1499941718000, + "text": "

Now ReactNative Version 0.46 Use Below code for email Validation.

\n\n
 validateEmail = (email) => {\n     var re = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n     if (re.test(email)) {\n     } else {\n       alert('email: ' + \"Please enter valid emailID.\")\n     }\n }\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b3", + "creator": "Behnam Mohammadi", + "createdAt": 1499605515000, + "text": "

ES6 sample

\n\n
const validateEmail=(email)=> /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/.test(email);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b5", + "creator": "hmharsh3", + "createdAt": 1500694393000, + "text": "

You can also try

\n\n
var string = \"hmharsh3@gmail.com\"\nvar exp = /(\\w(=?@)\\w+\\.{1}[a-zA-Z]{2,})/i\nalert(exp.test(string))\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b6", + "creator": "Juan Pablo", + "createdAt": 1517949729000, + "text": "

Regex updated! try this

\n
let val = 'email@domain.com';\nif(/^[a-z0-9][a-z0-9-_\\.]+@([a-z]|[a-z0-9]?[a-z0-9-]+[a-z0-9])\\.[a-z0-9]{2,10}(?:\\.[a-z]{2,10})?$/.test(val)) {\n   console.log('passed');\n}\n
\n

typscript version complete

\n
//\nexport const emailValid = (val:string):boolean => /^[a-z0-9][a-z0-9-_\\.]+@([a-z]|[a-z0-9]?[a-z0-9-]+[a-z0-9])\\.[a-z0-9]{2,10}(?:\\.[a-z]{2,10})?$/.test(val);\n
\n

more info https://git.io/vhEfc

\n

\"regex

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32338082fcc3049e913c9", + "creator": "Agung Sudrajat Supriatna", + "createdAt": 1659611354000, + "text": "this one passes all my email. :D", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901b7", + "creator": "Mizanur Rahaman", + "createdAt": 1519725375000, + "text": "
 <input type=\"email\" class=\"form-control\" required=\"required\" placeholder=\"Email Address\" name=\"Email\" id=\"Email\" autocomplete=\"Email\">\n <button class=\"btn-1 shadow-0 full-width\" type=\"button\" id=\"register\">Register account</button>\n
\n\n

\n\n
 $(\"#register\").click(function(){       \n    var rea = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n    var Email = $(\"#Email\").val();\n    var x = rea.test(Email);\n    if (!x) {\n        alert('Type Your valid Email');\n        return false;\n    }           \n </script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b8", + "creator": "Menelaos Kotsollaris", + "createdAt": 1519777101000, + "text": "

Here's a simple regex that would just check for the basic format of an email e.g., X@Y.C:

\n\n

\\S+@\\S+\\.\\S+

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ba", + "creator": "Sbbs", + "createdAt": 1529583200000, + "text": "

This is a JavaScript translation of the validation suggested by the official Rails guide used by thousands of websites:

\n\n
/^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$/i\n
\n\n

Relatively simple but tests against most common errors.

\n\n

Tested on a dataset of thousands of emails and it had zero false negatives/positives.

\n\n

Example usage:

\n\n
const emailRegex = /^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$/i;\n\nemailRegex.test('email@example.com');    // true\n\n// Multi-word domains\nemailRegex.test('email@example.co.uk');  // true\nemailRegex.test('email@mail.gmail.com'); // true\n\n// Valid special characters\nemailRegex.test('unusual+but+valid+email1900=/!#$%&\\'*+-/=?^_`.{|}~@example.com') // true\n\n// Trailing dots\nemailRegex.test('email@example.co.uk.'); // false\n\n// No domain\nemailRegex.test('email@example');        // false\n\n// Leading space\nemailRegex.test(' email@example.com');   // false\n\n// Trailing space\nemailRegex.test('email@example.com ');   // false\n\n// Incorrect domains\nemailRegex.test('email@example,com ');   // false\n\n// Other invalid emails\nemailRegex.test('invalid.email.com')        // false\nemailRegex.test('invalid@email@domain.com') // false\nemailRegex.test('email@example..com')       // false\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901b9", + "creator": "Prince Dholakiya", + "createdAt": 1528804995000, + "text": "

Here's a list of several commonly used regular expressions valid in Android and Java:

\n
    \n
  1. User names: "^[A-Za-z0-9_-]{min number of character,max number of character}$";

    \n
  2. \n
  3. Telephone numbers: "(^\\\\+)?[0-9()-]*";

    \n
  4. \n
  5. Telephone numbers (optional):^($|(^\\\\+)?[0-9()-]*)$";

    \n
  6. \n
  7. Email addresses: "[a-zA-Z0-9_\\\\.\\\\+-]+@[a-zA-Z0-9-]+\\\\.[a-zA-Z0-9-\\\\.]+";

    \n
  8. \n
  9. Email addresses (optional): "^($|[a-zA-Z0-9_\\\\.\\\\+-]+@[a-zA-Z0-9-]+\\\\.[a-zA-Z0-9-\\\\.]+)$";

    \n
  10. \n
  11. Web URL: "^($|(http:\\\\/\\\\/|https:\\\\/\\\\/)?(www.)?([a-zA-Z0-9]+).[a-zA-Z0-9]*.[a-z]{3}.?([a-z]+)?)$";

    \n
  12. \n
  13. URL for YouTube shortener (youtu.be) URLs: "https?\\\\:\\\\/\\\\/(www\\\\.)?youtu(\\\\.)?be(\\\\.com)?\\\\/.*(\\\\?v=|\\\\/v\\\\/)?[a-zA-Z0-9_\\\\-]+";

    \n
  14. \n
  15. Postal address: "[a-zA-Z\\\\d\\\\s\\\\-\\\\,\\\\#\\\\.\\\\+]+";

    \n
  16. \n
  17. Non-empty fields: "[^\\\\s]*";

    \n
  18. \n
  19. 6-digit PIN: "^([0-9]{6})?$";

    \n
  20. \n
  21. IFSC codes: "^[^\\\\s]{4}\\\\d{7}$";

    \n
  22. \n
  23. SWIFT codes: "^([0-9]{10})?$";

    \n
  24. \n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32338082fcc3049e913cf", + "creator": "gre_gor", + "createdAt": 1654077588000, + "text": "The question is about JavaScript not Java.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901bb", + "creator": "aabiro", + "createdAt": 1531908954000, + "text": "

Here is a solution that works and includes validation/notification fuctionality in a form:

\n\n

You can run it at this link

\n\n

JAVASCRIPT

\n\n
(function() {\n  'use strict';\n\n  window.addEventListener('load', function() {\n    var form = document.getElementById('needs-validation');\n    form.addEventListener('submit', function(event) {\n      if (form.checkValidity() === false) {\n        event.preventDefault();\n      }\n      form.classList.add('was-validated');\n      event.preventDefault();              \n    }, false);\n  }, false);\n})();\n
\n\n

HTML

\n\n
<p class='title'>\n    <b>Email validation</b>\n  <hr size=\"30px;\">\n</p>\n<br>\n\n<form id=\"needs-validation\" novalidate>\n  <p class='form_text'>Try it out!</p>\n  <div class=\"form-row\">\n    <div class=\"col-12\">\n      <input type=\"email\" class=\"form-control\" placeholder=\"Email Address\" required>\n        <div class=\"invalid-feedback\">\n          Please enter a valid email address.\n        </div>\n    </div>\n  <div class=\"row\">\n    <div class=\"col-12\">\n      <button type=\"submit\" \n          class=\"btn btn-default btn-block\">Sign up now\n      </button>\n    </div>\n   </div>\n</form>\n
\n\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901bc", + "creator": "mpyw", + "createdAt": 1532256787000, + "text": "

I wrote a JavaScript email validator which is fully compatile with PHP's filter_var($value, FILTER_VALIDATE_EMAIL) implementation.

\n\n

https://github.com/mpyw/FILTER_VALIDATE_EMAIL.js

\n\n
import validateEmail from 'filter-validate-email'\n\nconst value = '...'\nconst result = validateEmail(value)\n
\n\n

is equivalent to:

\n\n
<?php\n\n$value = '...';\n$result = (bool)filter_var($value, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901bd", + "creator": "egor518", + "createdAt": 1534859445000, + "text": "
function ValidateEmail(mail) \n{\n  if (/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(myForm.emailAddr.value))\n  {\n    return (true)\n  }\n  alert(\"You have entered an invalid email address!\")\n  return (false)\n}\n
\n\n

Ref URL: https://www.w3resource.com/javascript/form/email-validation.php

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901be", + "creator": "Kavan Fatehi", + "createdAt": 1536406914000, + "text": "

This works for me:

\n\n
function Email(mail)\n{\n if (/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(myForm.emailAddr.value))\n  {\n    return (true)\n  }\n    alert(\"Invalid email address!\")\n    return (false)\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901bf", + "creator": "Khaliq Izrail", + "createdAt": 1542641636000, + "text": "

The personal_info part contains the following ASCII characters.

\n\n
    \n
  1. Uppercase (A-Z) and lowercase (a-z) English letters. Digits (0-9).
  2. \n
  3. Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  4. \n
  5. Character . ( period, dot or fullstop) provided that it is not the\nfirst or last character and it will not come one after the other.
  6. \n
\n\n

The domain name [for example com, org, net, in, us, info] part contains letters, digits, hyphens, and dots.

\n\n
 function ValidateEmail(mail) \n{\n if (/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(myForm.emailAddr.value))\n  {\n    return (true)\n  }\n    alert(\"You have entered an invalid email address!\")\n    return (false)\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c0", + "creator": "tecnocrata", + "createdAt": 1544637903000, + "text": "

You could also use RegExp:

\n
function validateEmail(str) {\n    return new RegExp(/([\\w\\.\\-_]+)?\\w+@[\\w-_]+(\\.\\w+){1,}/, 'igm').test(str);\n}\n
\n

See the Regular Expressions guide on MDN for more info.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32339082fcc3049e913d7", + "creator": "David Mårtensson", + "createdAt": 1613499990000, + "text": "No this will not work for a lot of the active emails of the world and will accept emails that are not possible, do not use!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901c1", + "creator": "isapir", + "createdAt": 1547237066000, + "text": "

Here is the recommended Regex pattern for HTML5 on MDN:

\n\n
\n

Browsers that support the email input type automatically provide validation to ensure that only text that matches the standard format for Internet e-mail addresses is entered into the input box. Browsers that implement the specification should be using an algorithm equivalent to the following regular expression:

\n
\n\n
/^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}\n[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/\n
\n\n

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#Validation

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c2", + "creator": "ranieribt", + "createdAt": 1548708562000, + "text": "

You cold use https://github.com/chriso/validator.js and simply do:

\n\n
var validator = require('validator');\n\nvalidator.isEmail('foo@bar.com'); //=> true\n
\n\n

Note that this can work on the client.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c3", + "creator": "karlzafiris", + "createdAt": 1550953152000, + "text": "

Here's how I do it. I'm using match() to check for the standard email pattern and I'm adding a class to the input text to notify the user accordingly. Hope that helps!

\n\n

\r\n
\r\n
$(document).ready(function(){\r\n  $('#submit').on('click', function(){\r\n      var email = $('#email').val();\r\n      var pat = /^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/;\r\n      if (email.match(pat)){\r\n        $('#email')\r\n          .addClass('input-valid');\r\n        return false;\r\n      } else {\r\n        $('#email')\r\n        \t.addClass('input-error')\r\n          .val('');\r\n        return false;\r\n      }\r\n  });\r\n});
\r\n
.input-error {\r\n  border: 1px solid red;\r\n  color: red;\r\n}\r\n\r\n.input-valid {\r\n  border: 1px solid green;\r\n  color: green;\r\n}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n<form>\r\n    <input type=\"text\" id=\"email\" placeholder=\"name@service.xx\" class=\"\">\r\n    <input type=\"submit\" id=\"submit\" value=\"Send\"/>\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32339082fcc3049e913dc", + "creator": "Iman", + "createdAt": 1551718087000, + "text": "Have you ever tried DeBounce Email Validation? I suggest taking a look at this.", + "upvotes": 424, + "upvoterUsernames": [], + "downvotes": 424, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901c4", + "creator": "B. Bohdan", + "createdAt": 1551457660000, + "text": "

There is my version of an email validator. This code is done with object-oriented programming and realized as a class with static methods. You will find two versions of the validators: strict(EmailValidator.validate) and kind(EmailValidator.validateKind).

\n\n

The first throws an error if an email is invalid and returns email otherwise. The second returns Boolean value that says if an email is valid. I prefer the strict version in most of the cases.

\n\n
export class EmailValidator {\n    /**\n     * @param {string} email\n     * @return {string}\n     * @throws {Error}\n     */\n    static validate(email) {\n        email = this.prepareEmail(email);\n\n        const isValid = this.validateKind(email);\n\n        if (isValid)\n            return email;\n\n        throw new Error(`Got invalid email: ${email}.`);\n    }\n\n    /**\n     * @param {string} email\n     * @return {boolean}\n     */\n    static validateKind(email) {\n        email = this.prepareEmail(email);\n\n        const regex = this.getRegex();\n\n        return regex.test(email);\n    }\n\n    /**\n     * @return {RegExp}\n     * @private\n     */\n    static getRegex() {\n        return /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n    }\n\n    /**\n     * @param {string} email\n     * @return {string}\n     * @private\n     */\n    static prepareEmail(email) {\n        return String(email).toLowerCase();\n    }\n}\n
\n\n

To validate an email you can follow these ways:

\n\n
// First way.\n\ntry {\n    EmailValidator.validate('balovbohdan@gmail.com');\n} catch (e) {\n    console.error(e.message);\n}\n
\n\n
// Second way.\n\nconst email = 'balovbohdan@gmail.com';\nconst isValid = EmailValidator.validateKind(email);\n\nif (isValid)\n    console.log(`Email is valid: ${email}.`);\nelse\n    console.log(`Email is invalid: ${email}.`);\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c6", + "creator": "Antonio", + "createdAt": 1556203578000, + "text": "

General email regex (RFC 5322 Official Standard): https://emailregex.com/

\n\n

JavaScript:

\n\n
/^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c5", + "creator": "Raheel", + "createdAt": 1556009029000, + "text": "

I am using this function

\n\n
/**\n * @param {*} email\n */\nexport const validateEmail = email => {\n    return new RegExp(/[\\w-]+@([\\w-]+\\.)+[\\w-]+/gm).test(email);\n};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c7", + "creator": "Pax", + "createdAt": 1558950898000, + "text": "

Search for the @ sign in the input field.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c8", + "creator": "Nicolas Zozol", + "createdAt": 1559729683000, + "text": "

If you want something a human can read and maintain, I would recommend Masala Parser (I'm one of the creators of it).

\n\n
import {C,Streams} from '@masala/parser'\n\nconst illegalCharset = ' @\\u00A0\\n\\t';\nconst extendedIllegalCharset = illegalCharset + '.';\n\n\n// Assume 'nicolas@internal.masala.co.uk'\nexport function simpleEmail() {\n\n    return C.charNotIn(illegalCharset).rep() // 'nicolas'\n        .then(C.char('@'))\n        .then(subDns())  //'internal.masala.co.'\n        .then(C.charNotIn(extendedIllegalCharset).rep()) //'uk'\n        .eos(); // Must be end of the char stream\n}\n\n// x@internal.masala.co.uk => extract 'internal.masala.co.'\nfunction  subDns() {\n    return C.charNotIn(extendedIllegalCharset).rep().then(C.char('.')).rep()\n}\n\nfunction validateEmail(email:string) {\n    console.log(email + ': ' + (simpleEmail().parse(Streams.ofString(email)).isAccepted()));\n}\n\n\nvalidateEmail('nicolas@internal.masala.co.uk'); // True\nvalidateEmail('nz@co.'); // False, trailing \".\"\n
\n\n

If you want to accept the ultimate ugly email version, you can add in quotes in the first part:

\n\n
\nfunction inQuote() {\n    return C.char('\"')\n        .then(C.notChar('\"').rep())\n        .then(C.char('\"'))\n}\n\nfunction allEmail() {\n\n    return inQuote().or(C.charNotIn(illegalCharset))\n        .rep() // repeat (inQuote or anyCharacter)\n        .then(C.char('@'))\n        .then(subDns())\n        .then(C.charNotIn(extendedIllegalCharset).rep())\n        .eos() // Must be end of the character stream\n        // Create a structure\n        .map(function (characters) { return ({ email: characters.join('') }); });\n}\n
\n\n

'\"nicolas\"\"love-quotes\"@masala.co.uk' is officially valid, but should it be in your system?

\n\n

At least with Masala, you give yourself a chance to understand it. And so for the next year, colleague.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901c9", + "creator": "Idan", + "createdAt": 1565022211000, + "text": "

I add my Regex - i solved for me more little issues like characters from other languages or capital letters

\n\n
^[a-zA-Z0-9][a-zA-Z0-9-_\\.]+@([a-zA-Z]|[a-zA-Z0-9]?[a-zA-Z0-9-]+[a-zA-Z0-9])\\.[a-zA-Z0-9]{2,10}(?:\\.[a-zA-Z]{2,10})?$\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ca", + "creator": "jimmont", + "createdAt": 1568689232000, + "text": "

Use the browser/runtime to handle parsing the input by prepending a protocol and pass it to the URL API, trapping any errors and check the resulting username and hostname properties of the result. It will handle basically all transformations and possibilities (punycode of character sets, etc). This only establishes that the input is parsable, not that is valid--that is only possible through checking if the destination machine receives messages for that alias. This provides a close (imo reasonable) guess though, and can be expanded to be more specific and realistic if you're comfortable both maintaining it and also risking invalid rejections. (Note it doesn't attempt to address IPv4 or IPv6 addresses, simply the broad range of customer-facing scenarios using a domain.)

\n\n
function validEmail(email=''){\n    var $0, url, isValid = false, emailPatternInput = /^[^@]{1,64}@[^@]{4,253}$/, emailPatternUrl = /^[^@]{1,64}@[a-z][a-z0-9\\.-]{3,252}$/i;\n    email = email.trim();\n    try{\n        url = new URL('http://'+email);\n        $0 = `${url.username}@${url.hostname}`;\n        isValid = emailPatternInput.test( email );\n        if(!isValid) throw 'invalid email pattern on input:' + email;\n        isValid = emailPatternUrl.test( $0 );\n        if(!isValid) throw 'invalid email pattern on url:' + $0;\n        console.log(`email looks legit \"${email}\" checking url-parts: \"${$0 === email ? '-SAME-':$0}\"`);\n    }catch(err){\n        console.error(`probably not an email address: \"${email}\"`, err);\n    };\n    return isValid;\n}\n\n['user+this@はじめよう.みんな', 'stuff@things', 'user+that@host.com', 'Jean+François@anydomain.museum','هيا@יאללה', '试@例子.测试.مثال.آزمایشی', 'not@@really', 'no'].forEach(email=>console.log(validEmail(email), email));\n
\n\n

This is the both the simplest and most generally permissive example I can come up with. Please edit it in cases where it can be made to be more accurate while maintain its simplicity and reasonable generally permissive validity.

\n\n

Also see MDN URL docs URL, window.URL and Nodejs for URL APIs.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901cb", + "creator": "Eboubaker", + "createdAt": 1574029344000, + "text": "

These will work with the top used emails(they match exactly the rules of each one).\n

\nGmail\n
\n/^[a-z]((?!\\.\\.)([a-z\\.])){4,28}[a-z0-9]@gmail.com$/i\n

\nYahoo\n
\n/^[a-z]((?!\\.\\.)([\\w\\.])){3,30}[\\w]@yahoo.com$/i\n

\nOutlook/Hotmail\n
\n/[a-z]((?!\\.\\.)([\\w\\.])){0,62}[\\w]@(outlook.com|hotmail.com)$/i

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3233a082fcc3049e913e6", + "creator": "Eboubaker", + "createdAt": 1574353849000, + "text": "@Toto that's right, i don't know how i lost it there, i actually used this regex in my database handaling :\\, thanks again.", + "upvotes": 636, + "upvoterUsernames": [], + "downvotes": 636, + "downvoterUsernames": [] + }, + { + "_id": "62f3233a082fcc3049e913e8", + "creator": "David Wheatley", + "createdAt": 1579628209000, + "text": "Gmail's domain is not @google.com, it's @gmail.com or (for very old accounts) @googlemail.com", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 143, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901cc", + "creator": "Rajib Chy", + "createdAt": 1574762948000, + "text": "

You may try RegExp

\n\n

\r\n
\r\n
function isValidEmail( value ) {\r\n\treturn /^[\\w\\-\\.\\+]+\\@[a-zA-Z0-9\\.\\-]+\\.[a-zA-z0-9]{2,5}$/.test( value );\r\n}\r\n\r\nconsole.log( isValidEmail(\"mymail@mydomain.com\") )
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901cd", + "creator": "Renish Gotecha", + "createdAt": 1577436773000, + "text": "

In my case, I wanted to avoid ~ and # that's why I have used another solution:

\n
function validEmail(email){\n  const regex = /^((?!\\.)[\\w\\-_.]*[^.])(@\\w+)(\\.\\w+(\\.\\w+)?[^.\\W])$/;\n  return regex.test(email);\n}\n
\n

\r\n
\r\n
function validEmail(email){\n  const regex = /^((?!\\.)[\\w\\-_.]*[^.])(@\\w+)(\\.\\w+(\\.\\w+)?[^.\\W])$/;\n  return regex.test(email);\n}\n\nconst emails = [\n'pio_pio@factory.com',\n'~pio_pio@factory.com',\n'pio_~pio@factory.com',\n'pio_#pio@factory.com',\n'pio_pio@#factory.com',\n'pio_pio@factory.c#om',\n'pio_pio@factory.c*om',\n'pio^_pio@factory.com'\n]\n\nfor(const email of emails){\n  document.write(email+' : '+validEmail(email)+'</br>');\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ce", + "creator": "Mattia Rasulo", + "createdAt": 1579887147000, + "text": "

You can use this regex (from w3resource (*not related to W3C)):

\n
/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/.test(emailValue)\n
\n

If you use Node you can use this in the back-end as well as the front-end.

\n

I don't know other back-end languages so I cannot evaluate for other use cases.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3233a082fcc3049e913ec", + "creator": "Xizam", + "createdAt": 1585048549000, + "text": "This regex fails on 'user+addition@gmail.com' and on 'user@email.longdomain'. Do not use.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3233a082fcc3049e913ee", + "creator": "Mattia Rasulo", + "createdAt": 1610119858000, + "text": "user+addition is a valid email and longdomain is not necessarily invalid..", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901d0", + "creator": "user1843640", + "createdAt": 1580744281000, + "text": "

I prefer to keep it simple and keep my users happy. I also prefer code which is easy to understand. RegEx is not.

\n\n
function isValidEmail(value) {\n    const atLocation = value.lastIndexOf(\"@\");\n    const dotLocation = value.lastIndexOf(\".\"); \n    return (\n        atLocation > 0 &&\n        dotLocation > atLocation + 1 &&\n        dotLocation < value.length - 1\n    );\n};\n
\n\n\n\n

Will this allow invalid email addresses to pass? Sure, but I don't think you need much more for a good user experience that allows you to enable/disable a button, display an error message, etc. You only know for sure that an email address is valid when you attempt to send an email to that address.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901cf", + "creator": "endyourif", + "createdAt": 1580067401000, + "text": "

Wow, there are a lot of answers that contain slightly different regular expressions. I've tried many that I've got different results and a variety of different issues with all of them.

\n

For UI validation, I'm good with the most basic check of looking for an @ sign. It's important to note, that I always do server-side validation with a standard "validate email" that contains a unique link for the user to confirm their email address.

\n
if (email.indexOf('@') > 0)\n
\n

I have purposely chosen 0 even with zero-based as it also ensures there is a single character before the @.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d1", + "creator": "Jaskaran Singh", + "createdAt": 1586738818000, + "text": "

If you get this error: Using regular expressions is security-sensitive.

\n

Then here is what you are looking for. This solution is free from " Regular expression Denial of Service (ReDoS) "

\n

Regex to validate emails without (ReDoS):

\n
/^[a-z0-9](?!.*?[^\\na-z0-9]{2})[^\\s@]+@[^\\s@]+\\.[^\\s@]+[a-z0-9]$/\n
\n

Please let me know if this solution works for you.\nThanks.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d2", + "creator": "Ebrahim", + "createdAt": 1594133736000, + "text": "

Most of the answers here are not linter friendly, it's a mess! Some of them are also outdated!\nAfter a lot of time spending, I decided to use an external library named email-validator, install it easily by npm for example and import/require it in your own project:

\n

https://www.npmjs.com/package/email-validator

\n
//NodeJs\nconst validator = require("email-validator");\nvalidator.validate("test@email.com"); // true\n\n//TypeScript/JavaScript\nimport * as EmailValidator from 'email-validator';\nEmailValidator.validate("test@email.com"); // true\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d4", + "creator": "FDisk", + "createdAt": 1618393320000, + "text": "

Yet another perfect regexp for email validation

\n
/^([^\\s\\@])+\\@(([^\\s\\@\\.])+\\.)+([^\\s\\.]{2,})+$/\n
\n

You can test it here https://regex101.com/r/FV3pUI/2

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d3", + "creator": "Nazmul Haque", + "createdAt": 1604819390000, + "text": "
     // Html form call function name at submit button\n\n    <form name="form1" action="#"> \n    <input type='text' name='text1'/>\n    <input type="submit" name="submit" value="Submit" \n    onclick="ValidateEmail(document.form1.text1)"/>\n   </from>\n\n    // Write the function name ValidateEmail below\n\n    <script>\n     function ValidateEmail(inputText)\n    {\n  var mailformat = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/;\n    if(inputText.value.match(mailformat))\n    {\n    alert("Valid email address!");\n    document.form1.text1.focus();\n    return true;\n    }\n    else\n   {\n    alert("You have entered an invalid email address!");\n    document.form1.text1.focus();\n    return false;\n    }\n    }\n   </script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d6", + "creator": "Force Bolt", + "createdAt": 1619592233000, + "text": "
// Try this regular Expression by ES6 function\n\nconst emailValidate = (email) => {\n  const regexp= /^[\\w.%+-]+@[\\w.-]+\\.[\\w]{2,6}$/;\n  return regexp.test(email);\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d5", + "creator": "danday74", + "createdAt": 1618707680000, + "text": "

This works well for a simple email regex:

\n

anythingExceptAtOrSpace@anythingExceptAtOrSpace.anythingExceptAtOrSpace

\n
/^[^@ ]+@[^@ ]+\\.[^@ ]+$/\n
\n

To test:

\n

\r\n
\r\n
const emailRegex = /^[^@ ]+@[^@ ]+\\.[^@ ]+$/\nconst result1 = emailRegex.test('hello@there.com')\nconsole.log(result1) // true\nconst result2 = emailRegex.test('hel@lo@there.com')\nconsole.log(result2) // false
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d8", + "creator": "Roman Bondar", + "createdAt": 1639079638000, + "text": "

Found the perfect regular expression.\nIt excludes double @, two "." in a row, languages other than English and extra characters at the beginning of the line (".", "-", "!", etc.)

\n
const regexp = /^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/;\nif (!this.value.match(regexp)) {\n// on error, we get into the condition\n    this.classList.add('error');\n}\n// this - email input\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901d7", + "creator": "Guilherme Santana", + "createdAt": 1621521212000, + "text": "

Simple suggestion, since i've seen confusion through the answers.

\n
function validaEmail(email){\n\n  if(email.includes("@")){\n\n    if(email.split("@")[1].includes(".")){\n      return true;\n    }\n\n  }\n\n  return false;\n  \n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a2", + "creator": "Andrew", + "createdAt": 1442800350000, + "text": "

I have found this to be the best solution:

\n\n
/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n
\n\n

It allows the following formats:

\n\n
\n1.  prettyandsimple@example.com\n2.  very.common@example.com\n3.  disposable.style.email.with+symbol@example.com\n4.  other.email-with-dash@example.com\n9.  #!$%&'*+-/=?^_`{}|~@example.org\n6.  \"()[]:,;@\\\\\\\"!#$%&'*+-/=?^_`{}| ~.a\"@example.org\n7.  \" \"@example.org (space between the quotes)\n8.  üñîçøðé@example.com (Unicode characters in local part)\n9.  üñîçøðé@üñîçøðé.com (Unicode characters in domain part)\n10. Pelé@example.com (Latin)\n11. δοκιμή@παράδειγμα.δοκιμή (Greek)\n12. 我買@屋企.香港 (Chinese)\n13. 甲斐@黒川.日本 (Japanese)\n14. чебурашка@ящик-с-апельсинами.рф (Cyrillic)\n
\n\n

It's clearly versatile and allows the all-important international characters, while still enforcing the basic anything@anything.anything format. It will block spaces which are technically allowed by RFC, but they are so rare that I'm happy to do this.

\n", + "upvotes": 241, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a1", + "creator": "cyberrspiritt", + "createdAt": 1441521116000, + "text": "

The best practice is to either use HTML5 built-in email tag.

\n\n
<input type=\"email\" name=\"email\">\n
\n\n

or the common email syntax as recognizing @ and . from the string is given below.

\n\n
^[a-zA-Z0-9_\\-.]+@[a-zA-Z0-9\\-]+\\.[a-zA-Z0-9\\-.]+$\n
\n\n
\n

Note that this would still produce invalid email that will still match\n the regex, its almost impossible to catch them all but this will\n improve the situation a little.

\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a4", + "creator": "Vinod Ranga", + "createdAt": 1451486909000, + "text": "

Simple regex for email-Id

\n\n
 String EMAIL_PATTERN =\"^(([^<>()\\[\\]\\.,;:\\s@\\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@(([^<>()[\\]\\.,;:\\s@\\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\\"]{2,})$\";\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329ba082fcc3049e92dca", + "creator": "Kamal Nayan", + "createdAt": 1457951921000, + "text": "According to your regex "_..............kamal@gmail.com" is valid, which should not be!", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901a3", + "creator": "Brijeshkumar", + "createdAt": 1446536809000, + "text": "

This regexp prevents duplicate domain names like abc@abc.com.com.com.com, it will allow only domain two time like abc@abc.co.in. It also does not allow statring from number like 123abc@abc.com

\n\n
regexp: /^([a-zA-Z])+([a-zA-Z0-9_.+-])+\\@(([a-zA-Z])+\\.+?(com|co|in|org|net|edu|info|gov|vekomy))\\.?(com|co|in|org|net|edu|info|gov)?$/,  \n
\n\n

All The Best !!!!!

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329ba082fcc3049e92dcd", + "creator": "Brijeshkumar", + "createdAt": 1446630816000, + "text": "True !!!!!!But different people can have different requirement and That is why we write validations....isn't it?????????", + "upvotes": 214, + "upvoterUsernames": [], + "downvotes": 214, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901a6", + "creator": "Shubham Kumar", + "createdAt": 1467070595000, + "text": "

I've mixed @mevius and @Boldewyn Code to Create this ultimate code for email verification using JavaScript.

\n\n

\r\n
\r\n
function ValidateEmail(email){\r\n \r\n  var re = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\r\n \r\n  var input = document.createElement('input');\r\n \r\n  input.type = 'email';\r\n  input.value = email;\r\n \r\n  return typeof input.checkValidity == 'function' ? input.checkValidity() : re.test(email);\r\n \r\n}
\r\n
\r\n
\r\n

\n\n

I have shared this code on my blog here.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bb082fcc3049e92dda", + "creator": "Etienne Martin", + "createdAt": 1543094960000, + "text": "This will fail if the field is empty. You could add a required attribute and trim the email address to solve the issue.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901a5", + "creator": "Priya", + "createdAt": 1464683422000, + "text": "

Use the regular expression:

\n\n
 /^[a-z][a-zA-Z0-9_.]*(\\.[a-zA-Z][a-zA-Z0-9_.]*)?@[a-z][a-zA-Z-0-9]*\\.[a-z]+(\\.[a-z]+)?$/\n
\n\n

Example:

\n\n
function validateEmail(email) {\n    var re = /^[a-z][a-zA-Z0-9_.]*(\\.[a-zA-Z][a-zA-Z0-9_.]*)?@[a-z][a-zA-Z-0-9]*\\.[a-z]+(\\.[a-z]+)?$/;\n    return re.test(email);\n}\n
\n\n

It should allow only @ , . , _

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a8", + "creator": "Kentonbmax", + "createdAt": 1469293862000, + "text": "

If you are using ng-pattern and material this does the job.

\n\n
vm.validateEmail = '([a-zA-Z0-9_.]{1,})((@[a-zA-Z]{2,})[\\\\\\.]([a-zA-Z]{2}|[a-zA-Z]{3}))';\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a7", + "creator": "CVE-RICK", + "createdAt": 1467348198000, + "text": "

I'm really looking forward to solve this problem.\nSo I modified email validation regular expression above

\n\n\n\n

to pass the examples in Wikipedia Email Address.

\n\n

And you can see the result in here.

\n\n

\"enter

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901a9", + "creator": "chemic", + "createdAt": 1478553667000, + "text": "

Whoever is using @pvl solution and wants it to pass ESLint Prefer-template then here's a version where I used template literals instead of string concatenation.

\n\n
validateEmail(email) {\n    let sQtext = '[^\\\\x0d\\\\x22\\\\x5c\\\\x80-\\\\xff]';\n    let sDtext = '[^\\\\x0d\\\\x5b-\\\\x5d\\\\x80-\\\\xff]';\n    let sAtom = '[^\\\\x00-\\\\x20\\\\x22\\\\x28\\\\x29\\\\x2c\\\\x2e\\\\x3a-\\\\x3c\\\\x3e\\\\x40\\\\x5b-\\\\x5d\\\\x7f-\\\\xff]+';\n    let sQuotedPair = '\\\\x5c[\\\\x00-\\\\x7f]';\n    let sDomainLiteral = `\\\\x5b(${sDtext}|${sQuotedPair})*\\\\x5d`;\n    let sQuotedString = `\\\\x22(${sQtext}|${sQuotedPair})*\\\\x22`;\n    let sDomainRef = sAtom;\n    let sSubDomain = `(${sDomainRef}|${sDomainLiteral})`;\n    let sWord = `(${sAtom}|${sQuotedString})`;\n    let sDomain = `${sSubDomain}(\\\\x2e${sSubDomain})*`;\n    let sLocalPart = `${sWord}(\\\\x2e${sWord})*`;\n    let sAddrSpec = `${sLocalPart}\\\\x40${sDomain}`; // complete RFC822 email address spec\n    let sValidEmail = `^${sAddrSpec}$`; // as whole string\n\n    let reValidEmail = new RegExp(sValidEmail);\n\n    return reValidEmail.test(email);\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901aa", + "creator": "Prabhat Kasera", + "createdAt": 1482392689000, + "text": "

Regex for validating email address

\n\n
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])+\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f8d", + "creator": "jimmont", + "createdAt": 1650995466000, + "text": "

Use the URL interface in JavaScript to parse the address in the minimum practical expected format user@host then check that it looks reasonable. Next send a message to it and see if that works (for example require the recipient validate a one-time token via the address). Note that this handles punycode, internationalization, as shown in the samples below.

\n

https://developer.mozilla.org/en-US/docs/Web/API/URL

\n

an example with simple tests:

\n
function validEmail(input=''){\n    const emailPatternInput = /^[^@]{1,64}@[^@]{4,253}$/, emailPatternUrl = /^[^@]{1,64}@[a-z][a-z0-9\\.-]{3,252}$/i;\n    let email, url, valid = false, error, same = false;\n    try{\n        email = input.trim();\n        // handles punycode, etc using browser's own maintained implementation\n        url = new URL('http://'+email);\n        let urlderived = `${url.username}@${url.hostname}`;\n        same = urlderived === email;\n        valid = emailPatternInput.test( email );\n        if(!valid) throw new Error('invalid email pattern on input:' + email);\n        valid = emailPatternUrl.test( urlderived );\n        if(!valid) throw new Error('invalid email pattern on url:' + urlderived);\n    }catch(err){\n        error = err;\n    };\n    return {email, url, same, valid, error};\n}\n\n[\n 'user+this@はじめよう.みんな'\n, 'stuff@things.eu'\n, 'stuff@things'\n, 'user+that@host.com'\n, 'Jean+François@anydomain.museum','هيا@יאללה'\n, '试@例子.测试.مثال.آزمایشی'\n, 'not@@really'\n, 'no'\n].forEach(email=>console.log(validEmail(email), email));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f8e", + "creator": "Yauheni Prakapenka", + "createdAt": 1658088970000, + "text": "

Flutter Dart email validation

\n
/// Wrong symbols sequence tested with traceability matrix.\n/// ------------------\n/// |   | . | _ | - |\n/// | . | v | v | v |\n/// | _ | v | v | v |\n/// | - | v | v | v |\n/// -----------------\nconst String _wrongEmailSymbolsSequencePattern =\n    r'([.]{2,})|([-]{2,})|([@]{2,})|((\\._)+)|((\\.-)+)|((_\\.)+)|((_-)+)|((-_)+)|((-\\.)+)';\nconst String _emailPattern =\n    r'^[a-zA-Z0-9][\\w\\-.]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\-.]*[a-zA-Z0-9]\\.[a-zA-Z]{2,}$';\nconst String _emailRangePattern = r'^[\\w\\-@.]{8,254}$';\n\n/// Return `true` if email valid.\n///\n/// `Email range`\n/// - min 8\n/// - max 256\n/// - minimum valid pattern: xx@xx.xx\n///\n/// `Name`\n/// - should be latin letters, numbers or symbols ".", "_", "-"\n/// - must start and end with a letter or number\n/// - cannot use two symbols together\nbool validateEmail(String email) {\n  if (!RegExp(_emailRangePattern).hasMatch(email)) return false;\n  if (RegExp(_wrongEmailSymbolsSequencePattern).hasMatch(email)) return false;\n  if (RegExp(_emailPattern).hasMatch(email)) return true;\n  return false;\n}\n\nvoid main() {\n  validateEmail('x.x@xx.xx'); // true\n  validateEmail('x.-x@xx.xx'); // false\n}\n
\n

Unit tests for validateEmail here:\nhttps://gist.github.com/yauheniprakapenka/f08c4e0d8366dbdfae9c78fb4ed836f9

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321bd082fcc3049e90129", + "creator": "Peter Mortensen", + "createdAt": 1645053489000, + "text": "Very similar: How can I validate an email address using a regular expression?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321bd082fcc3049e9012a", + "creator": "Braiam", + "createdAt": 1651671896000, + "text": "If this question is to stay open it needs to remove "validation", otherwise it should to closed.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 4186931, + "uvac": 4187031 + } + }, + { + "_id": "62f321bb082fcc3049e8febd", + "title": "How do I create a GUID / UUID?", + "title-lowercase": "how do i create a guid / uuid?", + "creator": "Jason Cohen", + "createdAt": 1221854460000, + "status": "open", + "text": "

How do I create GUIDs (globally-unique identifiers) in JavaScript? The GUID / UUID should be at least 32 characters and should stay in the ASCII range to avoid trouble when passing them around.

\n

I'm not sure what routines are available on all browsers, how "random" and seeded the built-in random number generator is, etc.

\n", + "upvotes": 7222, + "upvoterUsernames": [], + "downvotes": 2244, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2579201, + "answers": 60, + "answerItems": [ + { + "_id": "62f321bf082fcc3049e90366", + "creator": "Prestaul", + "createdAt": 1221854763000, + "text": "

From sagi shkedy's technical blog:

\n
function generateGuid() {\n  var result, i, j;\n  result = '';\n  for(j=0; j<32; j++) {\n    if( j == 8 || j == 12 || j == 16 || j == 20)\n      result = result + '-';\n    i = Math.floor(Math.random()*16).toString(16).toUpperCase();\n    result = result + i;\n  }\n  return result;\n}\n
\n

There are other methods that involve using an ActiveX control, but stay away from these!

\n

I thought it was worth pointing out that no GUID generator can guarantee unique keys (check the Wikipedia article). There is always a chance of collisions. A GUID simply offers a large enough universe of keys to reduce the change of collisions to almost nil.

\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323da082fcc3049e9165b", + "creator": "Daniel Marschall", + "createdAt": 1639273325000, + "text": "These GUIDs are invalid because they don't specify version and variant required by the ITU-T | ISO recommendation.", + "upvotes": 2446, + "upvoterUsernames": [], + "downvotes": 2446, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e9165d", + "creator": "Prestaul", + "createdAt": 1639506977000, + "text": "FWIW, this answer was edited early on to recommend using one of the better solutions but that recommendation was removed later by the community.", + "upvotes": 1333, + "upvoterUsernames": [], + "downvotes": 1333, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90365", + "creator": "John Millikin", + "createdAt": 1221854725000, + "text": "

UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDentifier), according to RFC 4122, are identifiers designed to provide certain uniqueness guarantees.

\n

While it is possible to implement RFC-compliant UUIDs in a few lines of JavaScript code (e.g., see @broofa's answer, below) there are several common pitfalls:

\n\n

Thus, developers writing code for production environments are encouraged to use a rigorous, well-maintained implementation such as the uuid module.

\n", + "upvotes": 3122, + "upvoterUsernames": [], + "downvotes": 531, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f323da082fcc3049e9165f", + "creator": "Phil", + "createdAt": 1601387291000, + "text": "@AbhiBeckert the answer is from 2008 and for node.js projects it might be valid to choose a dependency more over project size", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e91660", + "creator": "JSON", + "createdAt": 1653711617000, + "text": "Or even better use asm.js. if we're going to offer irrelevant answers we might as well go hard on it", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90368", + "creator": "Mathieu Pagé", + "createdAt": 1251130366000, + "text": "

This creates a version 4 UUID (created from pseudo random numbers):

\n
function uuid()\n{\n   var chars = '0123456789abcdef'.split('');\n\n   var uuid = [], rnd = Math.random, r;\n   uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';\n   uuid[14] = '4'; // version 4\n\n   for (var i = 0; i < 36; i++)\n   {\n      if (!uuid[i])\n      {\n         r = 0 | rnd()*16;\n\n         uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf];\n      }\n   }\n\n   return uuid.join('');\n}\n
\n

Here is a sample of the UUIDs generated:

\n
682db637-0f31-4847-9cdf-25ba9613a75c\n97d19478-3ab2-4aa1-b8cc-a1c3540f54aa\n2eed04c9-2692-456d-a0fd-51012f947136\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90367", + "creator": "Kevin Hakanson", + "createdAt": 1242531563000, + "text": "

Here's some code based on RFC 4122, section 4.4 (Algorithms for Creating a UUID from Truly Random or Pseudo-Random Number).

\n\n
function createUUID() {\n    // http://www.ietf.org/rfc/rfc4122.txt\n    var s = [];\n    var hexDigits = \"0123456789abcdef\";\n    for (var i = 0; i < 36; i++) {\n        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);\n    }\n    s[14] = \"4\";  // bits 12-15 of the time_hi_and_version field to 0010\n    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01\n    s[8] = s[13] = s[18] = s[23] = \"-\";\n\n    var uuid = s.join(\"\");\n    return uuid;\n}\n
\n", + "upvotes": 258, + "upvoterUsernames": [], + "downvotes": 75, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323da082fcc3049e91664", + "creator": "MgSam", + "createdAt": 1364241839000, + "text": "You should declare the array size beforehand rather than sizing it dynamically as you build the GUID. var s = new Array(36);", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9036a", + "creator": "jablko", + "createdAt": 1279150252000, + "text": "
  // RFC 4122\n  //\n  // A UUID is 128 bits long\n  //\n  // String representation is five fields of 4, 2, 2, 2, and 6 bytes.\n  // Fields represented as lowercase, zero-filled, hexadecimal strings, and\n  // are separated by dash characters\n  //\n  // A version 4 UUID is generated by setting all but six bits to randomly\n  // chosen values\n  var uuid = [\n    Math.random().toString(16).slice(2, 10),\n    Math.random().toString(16).slice(2, 6),\n\n    // Set the four most significant bits (bits 12 through 15) of the\n    // time_hi_and_version field to the 4-bit version number from Section\n    // 4.1.3\n    (Math.random() * .0625 /* 0x.1 */ + .25 /* 0x.4 */).toString(16).slice(2, 6),\n\n    // Set the two most significant bits (bits 6 and 7) of the\n    // clock_seq_hi_and_reserved to zero and one, respectively\n    (Math.random() * .25 /* 0x.4 */ + .5 /* 0x.8 */).toString(16).slice(2, 6),\n\n    Math.random().toString(16).slice(2, 14)].join('-');\n
\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90369", + "creator": "broofa", + "createdAt": 1264167620000, + "text": "

[Edited 2021-10-16 to reflect latest best-practices for producing RFC4122-compliant UUIDs]

\n

Most readers here will want to use the uuid module. It is well-tested and supported.

\n

The crypto.randomUUID() function is an emerging standard that is supported in Node.js and an increasing number of browsers.

\n

If neither of those work for you, there is this method (based on the original answer to this question):\n

\r\n
\r\n
function uuidv4() {\n  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>\n    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)\n  );\n}\n\nconsole.log(uuidv4());
\r\n
\r\n
\r\n

\n

Note: The use of any UUID generator that relies on Math.random() is strongly discouraged (including snippets featured in previous versions of this answer) for reasons best-explained here. TL;DR: Math.random()-based solutions do not provide good uniqueness guarantees.

\n", + "upvotes": 8777, + "upvoterUsernames": [], + "downvotes": 3657, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323da082fcc3049e91668", + "creator": "Slothario", + "createdAt": 1605128214000, + "text": ""uuidv4" is a little cryptic -- I'm naming it "gitGudGuids" in my project (joking).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e9166a", + "creator": "manuell", + "createdAt": 1635181446000, + "text": "@Ayyash In my Chrome F12 console [1e7]+-1e3 evaluates to a string: '10000000-1000'", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e9166c", + "creator": "manuell", + "createdAt": 1635181880000, + "text": "@Ayyash see destroyallsoftware.com/talks/wat", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e9166d", + "creator": "Roope Hakulinen", + "createdAt": 1645458388000, + "text": "@Ayyash this seems to produce the same result with valid JS/TS: 1e7 + '-' + 1e3", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e9166f", + "creator": "dota2pro", + "createdAt": 1649246951000, + "text": "This doesn't work in Typescript it says can't add number[] and number", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e91671", + "creator": "PLG", + "createdAt": 1652184379000, + "text": "nodejs/React gives the warning: Use parentheses to clarify the intended order of operations: no-mixed-operators; great solution though.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e91673", + "creator": "cognophile", + "createdAt": 1657461033000, + "text": "Thumbs up for the solution that doesn't rely upon external packages.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9036c", + "creator": "sleeplessnerd", + "createdAt": 1314544644000, + "text": "
var uuid = function() {\n    var buf = new Uint32Array(4);\n    window.crypto.getRandomValues(buf);\n    var idx = -1;\n    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n        idx++;\n        var r = (buf[idx>>3] >> ((idx%8)*4))&15;\n        var v = c == 'x' ? r : (r&0x3|0x8);\n        return v.toString(16);\n    });\n};\n
\n

This version is based on Briguy37's answer and some bitwise operators to extract nibble sized windows from the buffer.

\n

It should adhere to the RFC Type 4 (random) schema, since I had problems last time parsing non-compliant UUIDs with Java's UUID.

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9036b", + "creator": "Jed Schmidt", + "createdAt": 1313375255000, + "text": "

Here's a solution dated Oct. 9, 2011 from a comment by user jed at https://gist.github.com/982883:

\n\n
UUIDv4 = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}\n
\n\n

This accomplishes the same goal as the current highest-rated answer, but in 50+ fewer bytes by exploiting coercion, recursion, and exponential notation. For those curious how it works, here's the annotated form of an older version of the function:

\n\n
UUIDv4 =\n\nfunction b(\n  a // placeholder\n){\n  return a // if the placeholder was passed, return\n    ? ( // a random number from 0 to 15\n      a ^ // unless b is 8,\n      Math.random() // in which case\n      * 16 // a random number from\n      >> a/4 // 8 to 11\n      ).toString(16) // in hexadecimal\n    : ( // or otherwise a concatenated string:\n      [1e7] + // 10000000 +\n      -1e3 + // -1000 +\n      -4e3 + // -4000 +\n      -8e3 + // -80000000 +\n      -1e11 // -100000000000,\n      ).replace( // replacing\n        /[018]/g, // zeroes, ones, and eights with\n        b // random hex digits\n      )\n}\n
\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9036d", + "creator": "ripper234", + "createdAt": 1323684804000, + "text": "

Here is a combination of the top voted answer, with a workaround for Chrome's collisions:

\n
generateGUID = (typeof(window.crypto) != 'undefined' &&\n                typeof(window.crypto.getRandomValues) != 'undefined') ?\n    function() {\n        // If we have a cryptographically secure PRNG, use that\n        // https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript\n        var buf = new Uint16Array(8);\n        window.crypto.getRandomValues(buf);\n        var S4 = function(num) {\n            var ret = num.toString(16);\n            while(ret.length < 4){\n                ret = "0"+ret;\n            }\n            return ret;\n        };\n        return (S4(buf[0])+S4(buf[1])+"-"+S4(buf[2])+"-"+S4(buf[3])+"-"+S4(buf[4])+"-"+S4(buf[5])+S4(buf[6])+S4(buf[7]));\n    }\n\n    :\n\n    function() {\n        // Otherwise, just use Math.random\n        // https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523\n        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n            var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);\n            return v.toString(16);\n        });\n    };\n
\n

It is on jsbin if you want to test it.

\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9036e", + "creator": "Tracker1", + "createdAt": 1326491975000, + "text": "

I adjusted my own UUID/GUID generator with some extras here.

\n

I'm using the following Kybos random number generator to be a bit more cryptographically sound.

\n

Below is my script with the Mash and Kybos methods from baagoe.com excluded.

\n
//UUID/Guid Generator\n// use: UUID.create() or UUID.createSequential()\n// convenience:  UUID.empty, UUID.tryParse(string)\n(function(w){\n  // From http://baagoe.com/en/RandomMusings/javascript/\n  // Johannes Baagøe <baagoe@baagoe.com>, 2010\n  //function Mash() {...};\n\n  // From http://baagoe.com/en/RandomMusings/javascript/\n  //function Kybos() {...};\n\n  var rnd = Kybos();\n\n  //UUID/GUID Implementation from http://frugalcoder.us/post/2012/01/13/javascript-guid-uuid-generator.aspx\n  var UUID = {\n    \"empty\": \"00000000-0000-0000-0000-000000000000\"\n    ,\"parse\": function(input) {\n      var ret = input.toString().trim().toLowerCase().replace(/^[\\s\\r\\n]+|[\\{\\}]|[\\s\\r\\n]+$/g, \"\");\n      if ((/[a-f0-9]{8}\\-[a-f0-9]{4}\\-[a-f0-9]{4}\\-[a-f0-9]{4}\\-[a-f0-9]{12}/).test(ret))\n        return ret;\n      else\n        throw new Error(\"Unable to parse UUID\");\n    }\n    ,\"createSequential\": function() {\n      var ret = new Date().valueOf().toString(16).replace(\"-\",\"\")\n      for (;ret.length < 12; ret = \"0\" + ret);\n      ret = ret.substr(ret.length-12,12); //only least significant part\n      for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));\n      return [ret.substr(0,8), ret.substr(8,4), \"4\" + ret.substr(12,3), \"89AB\"[Math.floor(Math.random()*4)] + ret.substr(16,3),  ret.substr(20,12)].join(\"-\");\n    }\n    ,\"create\": function() {\n      var ret = \"\";\n      for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));\n      return [ret.substr(0,8), ret.substr(8,4), \"4\" + ret.substr(12,3), \"89AB\"[Math.floor(Math.random()*4)] + ret.substr(16,3),  ret.substr(20,12)].join(\"-\");\n    }\n    ,\"random\": function() {\n      return rnd();\n    }\n    ,\"tryParse\": function(input) {\n      try {\n        return UUID.parse(input);\n      } catch(ex) {\n        return UUID.empty;\n      }\n    }\n  };\n  UUID[\"new\"] = UUID.create;\n\n  w.UUID = w.Guid = UUID;\n}(window || this));
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90370", + "creator": "Wojciech Bednarski", + "createdAt": 1341262832000, + "text": "

JavaScript project on GitHub - https://github.com/LiosK/UUID.js

\n\n
\n

UUID.js The RFC-compliant UUID generator for JavaScript.

\n \n

See RFC 4122 http://www.ietf.org/rfc/rfc4122.txt.

\n \n

Features Generates RFC 4122 compliant UUIDs.

\n \n

Version 4 UUIDs (UUIDs from random numbers) and version 1 UUIDs\n (time-based UUIDs) are available.

\n \n

UUID object allows a variety of access to the UUID including access to\n the UUID fields.

\n \n

Low timestamp resolution of JavaScript is compensated by random\n numbers.

\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9036f", + "creator": "Andrea Turri", + "createdAt": 1337798555000, + "text": "

The better way:

\n
function(\n  a, b               // Placeholders\n){\n  for(               // Loop :)\n      b = a = '';    // b - result , a - numeric variable\n      a++ < 36;      //\n      b += a*51&52   // If "a" is not 9 or 14 or 19 or 24\n                  ?  //  return a random number or 4\n           (\n               a^15              // If "a" is not 15,\n                  ?              // generate a random number from 0 to 15\n               8^Math.random() *\n               (a^20 ? 16 : 4)   // unless "a" is 20, in which case a random number from 8 to 11,\n                  :\n               4                 //  otherwise 4\n           ).toString(16)\n                  :\n         '-'                     //  In other cases, (if "a" is 9,14,19,24) insert "-"\n      );\n  return b\n }\n
\n

Minimized:

\n
function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323db082fcc3049e9167a", + "creator": "Peter Mortensen", + "createdAt": 1609299445000, + "text": "Why is it better?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90371", + "creator": "joelpt", + "createdAt": 1353002697000, + "text": "

Here is a totally non-compliant but very performant implementation to generate an ASCII-safe GUID-like unique identifier.

\n\n
function generateQuickGuid() {\n    return Math.random().toString(36).substring(2, 15) +\n        Math.random().toString(36).substring(2, 15);\n}\n
\n\n

Generates 26 [a-z0-9] characters, yielding a UID that is both shorter and more unique than RFC compliant GUIDs. Dashes can be trivially added if human-readability matters.

\n\n

Here are usage examples and timings for this function and several of this question's other answers. The timing was performed under Chrome m25, 10 million iterations each.

\n\n
>>> generateQuickGuid()\n\"nvcjf1hs7tf8yyk4lmlijqkuo9\"\n\"yq6gipxqta4kui8z05tgh9qeel\"\n\"36dh5sec7zdj90sk2rx7pjswi2\"\nruntime: 32.5s\n\n>>> GUID() // John Millikin\n\"7a342ca2-e79f-528e-6302-8f901b0b6888\"\nruntime: 57.8s\n\n>>> regexGuid() // broofa\n\"396e0c46-09e4-4b19-97db-bd423774a4b3\"\nruntime: 91.2s\n\n>>> createUUID() // Kevin Hakanson\n\"403aa1ab-9f70-44ec-bc08-5d5ac56bd8a5\"\nruntime: 65.9s\n\n>>> UUIDv4() // Jed Schmidt\n\"f4d7d31f-fa83-431a-b30c-3e6cc37cc6ee\"\nruntime: 282.4s\n\n>>> Math.uuid() // broofa\n\"5BD52F55-E68F-40FC-93C2-90EE069CE545\"\nruntime: 225.8s\n\n>>> Math.uuidFast() // broofa\n\"6CB97A68-23A2-473E-B75B-11263781BBE6\"\nruntime: 92.0s\n\n>>> Math.uuidCompact() // broofa\n\"3d7b7a06-0a67-4b67-825c-e5c43ff8c1e8\"\nruntime: 229.0s\n\n>>> bitwiseGUID() // jablko\n\"baeaa2f-7587-4ff1-af23-eeab3e92\"\nruntime: 79.6s\n\n>>>> betterWayGUID() // Andrea Turri\n\"383585b0-9753-498d-99c3-416582e9662c\"\nruntime: 60.0s\n\n>>>> UUID() // John Fowler\n\"855f997b-4369-4cdb-b7c9-7142ceaf39e8\"\nruntime: 62.2s\n
\n\n

Here is the timing code.

\n\n
var r;\nconsole.time('t'); \nfor (var i = 0; i < 10000000; i++) { \n    r = FuncToTest(); \n};\nconsole.timeEnd('t');\n
\n", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90374", + "creator": "Anatoly Mironov", + "createdAt": 1371052803000, + "text": "

If your environment is SharePoint, there is a utility function called SP.Guid.newGuid (MSDN link which creates a new GUID. This function is inside the sp.init.js file. If you rewrite this function (to remove some other dependencies from other private functions), and it looks like this:

\n
var newGuid = function () {\n    var result = '';\n    var hexcodes = "0123456789abcdef".split("");\n\n    for (var index = 0; index < 32; index++) {\n        var value = Math.floor(Math.random() * 16);\n\n        switch (index) {\n        case 8:\n            result += '-';\n            break;\n        case 12:\n            value = 4;\n            result += '-';\n            break;\n        case 16:\n            value = value & 3 | 8;\n            result += '-';\n            break;\n        case 20:\n            result += '-';\n            break;\n        }\n        result += hexcodes[value];\n    }\n    return result;\n};\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323db082fcc3049e9167d", + "creator": "Peter Mortensen", + "createdAt": 1609299273000, + "text": "The redirected URL says "Applies to: SharePoint Foundation 2010"", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90373", + "creator": "kayz1", + "createdAt": 1359818826000, + "text": "

Simple JavaScript module as a combination of best answers in this question.

\n

\r\n
\r\n
var crypto = window.crypto || window.msCrypto || null; // IE11 fix\n\nvar Guid = Guid || (function() {\n\n  var EMPTY = '00000000-0000-0000-0000-000000000000';\n\n  var _padLeft = function(paddingString, width, replacementChar) {\n    return paddingString.length >= width ? paddingString : _padLeft(replacementChar + paddingString, width, replacementChar || ' ');\n  };\n\n  var _s4 = function(number) {\n    var hexadecimalResult = number.toString(16);\n    return _padLeft(hexadecimalResult, 4, '0');\n  };\n\n  var _cryptoGuid = function() {\n    var buffer = new window.Uint16Array(8);\n    crypto.getRandomValues(buffer);\n    return [_s4(buffer[0]) + _s4(buffer[1]), _s4(buffer[2]), _s4(buffer[3]), _s4(buffer[4]), _s4(buffer[5]) + _s4(buffer[6]) + _s4(buffer[7])].join('-');\n  };\n\n  var _guid = function() {\n    var currentDateMilliseconds = new Date().getTime();\n    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(currentChar) {\n      var randomChar = (currentDateMilliseconds + Math.random() * 16) % 16 | 0;\n      currentDateMilliseconds = Math.floor(currentDateMilliseconds / 16);\n      return (currentChar === 'x' ? randomChar : (randomChar & 0x7 | 0x8)).toString(16);\n    });\n  };\n\n  var create = function() {\n    var hasCrypto = crypto != 'undefined' && crypto !== null,\n      hasRandomValues = typeof(window.crypto.getRandomValues) != 'undefined';\n    return (hasCrypto && hasRandomValues) ? _cryptoGuid() : _guid();\n  };\n\n  return {\n    newGuid: create,\n    empty: EMPTY\n  };\n})();\n\n// DEMO: Create and show GUID\nconsole.log('1. New Guid:   ' + Guid.newGuid());\n\n// DEMO: Show empty GUID\nconsole.log('2. Empty Guid: ' + Guid.empty);
\r\n
\r\n
\r\n

\n

Usage:

\n
\n
\n

Guid.newGuid()

\n
\n
\n
\n
\n

"c6c2d12f-d76b-5739-e551-07e6de5b0807"

\n
\n
\n
\n
\n

Guid.empty

\n
\n
\n
\n
\n

"00000000-0000-0000-0000-000000000000"

\n
\n
\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323db082fcc3049e9167f", + "creator": "Matt", + "createdAt": 1521107863000, + "text": "@broofa - What would you suggest to make it strong and RFC-compliant? And why is _cryptoGuid not RFC-compliant?", + "upvotes": 431, + "upvoterUsernames": [], + "downvotes": 431, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90372", + "creator": "John Fowler", + "createdAt": 1353094909000, + "text": "

For those wanting an RFC 4122 version 4 compliant solution with speed considerations (few calls to Math.random()):

\n

\r\n
\r\n
var rand = Math.random;\n\nfunction UUID() {\n    var nbr, randStr = \"\";\n    do {\n        randStr += (nbr = rand()).toString(16).substr(3, 6);\n    } while (randStr.length < 30);\n    return (\n        randStr.substr(0, 8) + \"-\" +\n        randStr.substr(8, 4) + \"-4\" +\n        randStr.substr(12, 3) + \"-\" +\n        ((nbr*4|0)+8).toString(16) + // [89ab]\n        randStr.substr(15, 3) + \"-\" +\n        randStr.substr(18, 12)\n    );\n}\n\nconsole.log( UUID() );
\r\n
\r\n
\r\n

\n

The above function should have a decent balance between speed and randomness.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90376", + "creator": "ling", + "createdAt": 1394105694000, + "text": "

This one is based on date, and adds a random suffix to "ensure" uniqueness.

\n

It works well for CSS identifiers, always returns something like, and is easy to hack:

\n

uid-139410573297741

\n
var getUniqueId = function (prefix) {\n            var d = new Date().getTime();\n            d += (parseInt(Math.random() * 100)).toString();\n            if (undefined === prefix) {\n                prefix = 'uid-';\n            }\n            d = prefix + d;\n            return d;\n        };\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90375", + "creator": "Shital Shah", + "createdAt": 1385940396000, + "text": "

It is important to use well-tested code that is maintained by more than one contributor instead of whipping your own stuff for this.

\n

This is one of the places where you probably want to prefer the most stable code than the shortest possible clever version that works in X browser, but doesn't take in to account idiosyncrasies of Y which would often lead to very-hard-to-investigate bugs than manifests only randomly for some users. Personally I use uuid-js at https://github.com/aurigadl/uuid-js which is Bower enabled so I can take updates easily.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90377", + "creator": "Giridhar", + "createdAt": 1396595614000, + "text": "

I'm using this below function:

\n
function NewGuid()\n{\n    var sGuid = "";\n    for (var i=0; i<32; i++)\n    {\n        sGuid += Math.floor(Math.random()*0xF).toString(0xF);\n    }\n    return sGuid;\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90378", + "creator": "Rishi", + "createdAt": 1402234208000, + "text": "

For my use case, I required id generation that was guaranteed to be unique globally; without exception. I struggled with the problem for a while, and came up with a solution called TUID (truly unique ID). It generates an id with the first 32 characters being system-generated and the remaining digits representing milliseconds since epoch. In situations where I need to generate id's in client-side JavaScript code, it works well.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9037a", + "creator": "mangalbhaskar", + "createdAt": 1432706457000, + "text": "

A simple solution to generate a unique identification is to use a time token and add a random number to it. I prefer to prefix it with "uuid-".

\n

The below function will generate a random string of type: uuid-14d93eb1b9b4533e6. One doesn't need to generate a 32-characters random string. A 16-character random string is more than sufficient in this case to provide the unique UUIDs in JavaScript.

\n
var createUUID = function() {\n  return "uuid-" + ((new Date).getTime().toString(16) + Math.floor(1E7*Math.random()).toString(16));\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90381", + "creator": "Dustin Poissant", + "createdAt": 1470939336000, + "text": "

I found this script useful for creating GUIDs in JavaScript

\n\n

https://github.com/addui/GUIDJS

\n\n
var myGuid = GUID();\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90382", + "creator": "lugreen", + "createdAt": 1472659067000, + "text": "

This may be of use to someone...

\n\n
var d = new Date().valueOf();\nvar n = d.toString();\nvar result = '';\nvar length = 32;\nvar p = 0;\nvar chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\nfor (var i = length; i > 0; --i){\n    result += ((i & 1) && n.charAt(p) ? '<b>' + n.charAt(p) + '</b>' : chars[Math.floor(Math.random() * chars.length)]);\n    if(i & 1) p++;\n};\n
\n\n

https://jsfiddle.net/j0evrdf1/1/

\n", + "upvotes": 727, + "upvoterUsernames": [], + "downvotes": 727, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90383", + "creator": "user2006656", + "createdAt": 1473238055000, + "text": "
function randomHex(length) {\n    var random_string = '';\n    if(!length){\n        length = 1;\n    }\n    for(var i=0; i<length; i+=1){\n        random_string += Math.floor(Math.random() * 15).toString(16);\n    }\n    return random_string;\n}\n\nfunction guid() {\n    return randomHex(8);\n}\n
\n", + "upvotes": 831, + "upvoterUsernames": [], + "downvotes": 831, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32419082fcc3049e91689", + "creator": "Peter Mortensen", + "createdAt": 1617383911000, + "text": "An explanation would be in order.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90384", + "creator": "Pablo Pazos", + "createdAt": 1476593040000, + "text": "

Here you can find a very small function that generates UUIDs.

\n

One of the final versions is:

\n
function b(\n  a                  // Placeholder\n){\n  var cryptoObj = window.crypto || window.msCrypto; // For Internet Explorer 11\n  return a           // If the placeholder was passed, return\n    ? (              // a random number from 0 to 15\n      a ^            // unless b is 8,\n      cryptoObj.getRandomValues(new Uint8Array(1))[0]  // in which case\n      % 16           // a random number from\n      >> a/4         // 8 to 11\n      ).toString(16) // in hexadecimal\n    : (              // or otherwise a concatenated string:\n      [1e7] +        // 10000000 +\n      -1e3 +         // -1000 +\n      -4e3 +         // -4000 +\n      -8e3 +         // -80000000 +\n      -1e11          // -100000000000,\n      ).replace(     // Replacing\n        /[018]/g,    // zeroes, ones, and eights with\n        b            // random hex digits\n      )\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90385", + "creator": "Jonathan Potter", + "createdAt": 1494447226000, + "text": "

If you just need a random 128 bit string in no particular format, you can use:

\n
function uuid() {\n    return crypto.getRandomValues(new Uint32Array(4)).join('-');\n}\n
\n

Which will return something like 2350143528-4164020887-938913176-2513998651.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32419082fcc3049e9168c", + "creator": "vsync", + "createdAt": 1538288843000, + "text": "BTW, why does it generate only numbers and not characters as well? much less secure", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90386", + "creator": "Simon Rigét", + "createdAt": 1495227022000, + "text": "

Use:

\n
let uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n
\n

\r\n
\r\n
document.getElementById(\"unique\").innerHTML =\n  Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
\r\n
<div id=\"unique\">\n</div>
\r\n
\r\n
\r\n

\n

If IDs are generated more than 1 millisecond apart, they are 100% unique.

\n

If two IDs are generated at shorter intervals, and assuming that the random method is truly random, this would generate IDs that are 99.99999999999999% likely to be globally unique (collision in 1 of 10^15).

\n

You can increase this number by adding more digits, but to generate 100% unique IDs you will need to use a global counter.

\n

If you need RFC compatibility, this formatting will pass as a valid version 4 GUID:

\n
let u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);\nlet guid = [u.substr(0,8), u.substr(8,4), '4000-8' + u.substr(13,3), u.substr(16,12)].join('-');\n
\n

\r\n
\r\n
let u = Date.now().toString(16)+Math.random().toString(16)+'0'.repeat(16);\nlet guid = [u.substr(0,8), u.substr(8,4), '4000-8' + u.substr(13,3), u.substr(16,12)].join('-');\ndocument.getElementById(\"unique\").innerHTML = guid;
\r\n
<div id=\"unique\">\n</div>
\r\n
\r\n
\r\n

\n

The above code follow the intention, but not the letter of the RFC. Among other discrepancies it's a few random digits short. (Add more random digits if you need it) The upside is that this is really fast :)\nYou can test validity of your GUID here

\n", + "upvotes": 259, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32419082fcc3049e9168e", + "creator": "Marco Kerwitz", + "createdAt": 1514330886000, + "text": "This is not UUID though?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32419082fcc3049e91690", + "creator": "Simon Rigét", + "createdAt": 1518914925000, + "text": "Relaying on MAC addresses for uniqueness on virtual machines is a bad idea!", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32419082fcc3049e91692", + "creator": "Basj", + "createdAt": 1608022621000, + "text": "For those wondering: toString(36) converts in a base-36 numeration (0..9a..z). Example: (35).toString(36) is z.", + "upvotes": 1710, + "upvoterUsernames": [], + "downvotes": 1710, + "downvoterUsernames": [] + }, + { + "_id": "62f32419082fcc3049e91694", + "creator": "Simon Rigét", + "createdAt": 1627769614000, + "text": "The the many zeros in front are needed, because a random number might be just one digit. Even if its very rare, it still has to work.", + "upvotes": 171, + "upvoterUsernames": [], + "downvotes": 171, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90387", + "creator": "Behnam Mohammadi", + "createdAt": 1499605313000, + "text": "

ES6 sample

\n\n
const guid=()=> {\n  const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);     \n  return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;\n}\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90388", + "creator": "ceving", + "createdAt": 1511532851000, + "text": "

Just another more readable variant with just two mutations.

\n\n
function uuid4()\n{\n  function hex (s, b)\n  {\n    return s +\n      (b >>> 4   ).toString (16) +  // high nibble\n      (b & 0b1111).toString (16);   // low nibble\n  }\n\n  let r = crypto.getRandomValues (new Uint8Array (16));\n\n  r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100\n  r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100\n\n  return r.slice ( 0,  4).reduce (hex, '' ) +\n         r.slice ( 4,  6).reduce (hex, '-') +\n         r.slice ( 6,  8).reduce (hex, '-') +\n         r.slice ( 8, 10).reduce (hex, '-') +\n         r.slice (10, 16).reduce (hex, '-');\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32419082fcc3049e91695", + "creator": "ceving", + "createdAt": 1581673070000, + "text": "@user1529413 Yes. Uniqueness requires an index.", + "upvotes": 259, + "upvoterUsernames": [], + "downvotes": 259, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90389", + "creator": "Stephen Quan", + "createdAt": 1514810998000, + "text": "

For those who are using JavaScript on Windows (e.g., Windows Script Host (WSH), CScript, and HTA). One can use ActiveX. Specifically, the Scriptlet.Typelib object:

\n
WScript.Echo((new ActiveXObject("Scriptlet.TypeLib")).Guid)\n
\n

Note that this answer only works on the technologies I listed. It will not work in any browser, not even Microsoft Edge! So, your mileage will vary with this answer.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32419082fcc3049e91697", + "creator": "alek kowalczyk", + "createdAt": 1524272500000, + "text": "Such approach is still a thing in 2018? Wow :-)", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9038a", + "creator": "nomadev", + "createdAt": 1541609101000, + "text": "

Based on the work of broofa, I've added some more randomness by adding the timestamp to math.random():

\n
function uuidv4() {\n    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n        var r = parseFloat('0.' + Math.random().toString().replace('0.', '') + new Date().getTime()) * 16 | 0,\n            v = c == 'x' ? r : (r & 0x3 | 0x8);\n        return v.toString(16);\n    });\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9038c", + "creator": "amn", + "createdAt": 1544528393000, + "text": "

I couldn't find any answer that uses a single 16-octet TypedArray and a DataView, so I think the following solution for generating a version 4 UUID per the RFC will stand on its own here:

\n
const uuid4 = () => {\n    const ho = (n, p) => n.toString(16).padStart(p, 0); /// Return the hexadecimal text representation of number `n`, padded with zeroes to be of length `p`\n    const data = crypto.getRandomValues(new Uint8Array(16)); /// Fill the buffer with random data\n    data[6] = (data[6] & 0xf) | 0x40; /// Patch the 6th byte to reflect a version 4 UUID\n    data[8] = (data[8] & 0x3f) | 0x80; /// Patch the 8th byte to reflect a variant 1 UUID (version 4 UUIDs are)\n    const view = new DataView(data.buffer); /// Create a view backed by a 16-byte buffer\n    return `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(view.getUint16(8), 4)}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`; /// Compile the canonical textual form from the array data\n};\n
\n

I prefer it because it only relies on functions available to the standard ECMAScript platform, where possible -- which is all but one procedure.

\n

At the time of writing this, getRandomValues is not something implemented for the crypto object in Node.js. However, it has the equivalent randomBytes function which may be used instead.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9038b", + "creator": "Jacob Ochoa", + "createdAt": 1543609428000, + "text": "

The following is not v4 compliant, but it could easily be altered to be. It's just an example of extending the Uint8Array type, and using crypto.getRandomValues() to generate the UUID byte values.

\n
class uuid extends Uint8Array {\n    constructor() {\n        super(16)\n        /* Not v4, just some random bytes */\n        window.crypto.getRandomValues(this)\n    }\n    toString() {\n        let id = new String()\n        for (let i = 0; i < this.length; i++) {\n            /* Convert uint8 to hex string */\n            let hex = this[i].toString(16).toUpperCase()\n\n            /* Add zero padding */\n            while (hex.length < 2) {\n                hex = String(0).concat(hex)\n            }\n            id += hex\n\n            /* Add dashes */\n            if (i == 4 || i == 6 || i == 8 || i == 10 || i == 16) {\n                id += '-'\n            }\n        }\n        return id\n    }\n}\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9038e", + "creator": "user9342572809", + "createdAt": 1552906394000, + "text": "

We can use replace and crypto.getRandomValues to get an output like this:

\n

xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx

\n

\"Enter

\n

If we are looking for an opti solution, we have to replace crypto.getRandomValues(new Uint8Array(1))[0] by an array(32).

\n
const uuidv4 = () =>\n  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>\n    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)\n  );\n\nconsole.log(uuidv4());\n
\n

To get this code:

\n

\r\n
\r\n
function uuidv4() {\n  let bytes = window.crypto.getRandomValues(new Uint8Array(32));\n  const randomBytes = () => (bytes = bytes.slice(1)) && bytes[0];\n\n  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>\n      (c ^ randomBytes() & 15 >> c / 4).toString(16)\n    );\n}\n\n\nfor (var i = 0; i < 10; i++)\n  console.log(uuidv4());
\r\n
\r\n
\r\n

\n

Collision:

\n

We can do like google analytics and add a timestamp with : uuidv4() + "." + (+new Date()).

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9038d", + "creator": "Alireza", + "createdAt": 1547795818000, + "text": "

OK, using the uuid package, and its support for version 1, 3, 4 and 5 UUIDs, do:

\n
yarn add uuid\n
\n

And then:

\n
const uuidv1 = require('uuid/v1');\nuuidv1(); // ⇨ '45745c60-7b1a-11e8-9c9c-2d42b21b1a3e'\n
\n

You can also do it with fully-specified options:

\n
const v1options = {\n  node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],\n  clockseq: 0x1234,\n  msecs: new Date('2011-11-01').getTime(),\n  nsecs: 5678\n};\nuuidv1(v1options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab'\n
\n

For more information, visit the npm page here.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9038f", + "creator": "li x", + "createdAt": 1570439020000, + "text": "

The UUID currently has a proposal for addition to the standard library and can be supported here ECMAScript proposal: JavaScript standard library UUID

\n

The proposal encompasses having UUID as the following:

\n
// We're not yet certain as to how the API will be accessed (whether it's in the global, or a\n// future built-in module), and this will be part of the investigative process as we continue\n// working on the proposal.\nuuid(); // "52e6953d-edbe-4953-be2e-65ed3836b2f0"\n
\n

This implementation follows the same layout as the V4 random UUID generation found here: https://www.npmjs.com/package/uuid

\n
const uuidv4 = require('uuid/v4');\nuuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'\n
\n

I think it's noteworthy to understand how much bandwidth could be saved by this having an official implementation in the standard library. The authors of the proposal have also noted:

\n

The 12  kB uuid module is downloaded from npm > 62,000,000 times a month (June 2019); making it available in the standard library eventually saves TBs of bandwidth globally. If we continue to address user needs, such as uuid, with the standard library, bandwidth savings add up.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90390", + "creator": "dgellow", + "createdAt": 1585914779000, + "text": "

A TypeScript version of broofa's update from 2017-06-28, based on crypto API:

\n
function genUUID() {\n    // Reference: https://stackoverflow.com/a/2117523/709884\n    return ("10000000-1000-4000-8000-100000000000").replace(/[018]/g, s => {\n        const c = Number.parseInt(s, 10)\n        return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)\n    })\n}\n
\n

Reasons:

\n\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90392", + "creator": "Aral Roca", + "createdAt": 1592048523000, + "text": "

The native URL.createObjectURL is generating an UUID. You can take advantage of this.

\n
function uuid() {\n  const url = URL.createObjectURL(new Blob())\n  const [id] = url.toString().split('/').reverse()\n  URL.revokeObjectURL(url)\n  return id\n}\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241a082fcc3049e916a0", + "creator": "Paulo Henrique Queiroz", + "createdAt": 1607710525000, + "text": "works like a charm. Better than trying to generate manually. Very clever!", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f3241a082fcc3049e916a2", + "creator": "Aral Roca", + "createdAt": 1608050276000, + "text": "The performance is quite worst, but depending on the case it can be enough", + "upvotes": 5455, + "upvoterUsernames": [], + "downvotes": 5455, + "downvoterUsernames": [] + }, + { + "_id": "62f3241a082fcc3049e916a4", + "creator": "M. Justin", + "createdAt": 1626379850000, + "text": "Is the inclusion of a UUID here guaranteed, or is it just something that the current browser implementations all happen to do?", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90391", + "creator": "Mohan Ram", + "createdAt": 1591166843000, + "text": "

Don't use Math.random in any case since it generates a non-cryptographic source of random numbers.

\n

The solution below using crypto.getRandomValues

\n
function uuidv4() {\n  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {\n    // tslint:disable-next-line: no-bitwise\n    const r =\n      (window.crypto.getRandomValues(new Uint32Array(1))[0] *\n        Math.pow(2, -32) * 16) |\n      0;\n    // tslint:disable-next-line: no-bitwise\n    const v = c === "x" ? r : (r & 0x3) | 0x8;\n    return v.toString(16);\n  });\n}\n
\n

This link helps you to understand the insecure randomness thrown by Fortify Scanner.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90393", + "creator": "incureforce", + "createdAt": 1594405400000, + "text": "

This works for Node.js too, if you replace let buffer = new Uint8Array(); crypto.getRandomValues with let buffer = crypto.randomBytes(16)

\n

It should beat most regular expression solutions in performance.

\n

\r\n
\r\n
const hex = '0123456789ABCDEF'\n\nlet generateToken = function() {\n    let buffer = new Uint8Array(16)\n\n    crypto.getRandomValues(buffer)\n\n    buffer[6] = 0x40 | (buffer[6] & 0xF)\n    buffer[8] = 0x80 | (buffer[8] & 0xF)\n\n    let segments = []\n\n    for (let i = 0; i < 16; ++i) {\n        segments.push(hex[(buffer[i] >> 4 & 0xF)])\n        segments.push(hex[(buffer[i] >> 0 & 0xF)])\n\n        if (i == 3 || i == 5 || i == 7 || i == 9) {\n            segments.push('-')\n        }\n    }\n\n    return segments.join('')\n}\n\nfor (let i = 0; i < 100; ++i) {\n  console.log(generateToken())\n}
\r\n
\r\n
\r\n

\n

Performance charts (everybody loves them): jsbench

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90394", + "creator": "Yudner", + "createdAt": 1596878181000, + "text": "
var guid = createMyGuid();\n\nfunction createMyGuid()  \n{  \n   return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {  \n      var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);  \n      return v.toString(16);  \n   });  \n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241a082fcc3049e916a8", + "creator": "fcdt", + "createdAt": 1596881990000, + "text": "What's the difference to this post?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3241a082fcc3049e916aa", + "creator": "dbc", + "createdAt": 1596925837000, + "text": "@fcdt - the difference is c === 'x' instead of c == 'x'.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f3241a082fcc3049e916ac", + "creator": "Peter Mortensen", + "createdAt": 1617387145000, + "text": "An explanation would be in order.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90396", + "creator": "vanowm", + "createdAt": 1612686392000, + "text": "

Here is a function that generates a static UUID from a string or a random UUID if no string supplied:

\n

\r\n
\r\n
function stringToUUID (str)\n{\n  if (str === undefined || !str.length)\n    str = \"\" + Math.random() * new Date().getTime() + Math.random();\n\n  let c = 0,\n      r = \"\";\n\n  for (let i = 0; i < str.length; i++)\n    c = (c + (str.charCodeAt(i) * (i + 1) - 1)) & 0xfffffffffffff;\n\n  str = str.substr(str.length / 2) + c.toString(16) + str.substr(0, str.length / 2);\n  for(let i = 0, p = c + str.length; i < 32; i++)\n  {\n    if (i == 8 || i == 12 || i == 16 || i == 20)\n      r += \"-\";\n\n    c = p = (str[(i ** i + p + 1) % str.length]).charCodeAt(0) + p + i;\n    if (i == 12)\n      c = (c % 5) + 1; //1-5\n    else if (i == 16)\n      c = (c % 4) + 8; //8-B\n    else\n      c %= 16; //0-F\n\n    r += c.toString(16);\n  }\n  return r;\n}\n\nconsole.log(\"Random       :\", stringToUUID());\nconsole.log(\"Static [1234]:\", stringToUUID(\"1234\")); //29c2c73b-52de-4344-9cf6-e6da61cb8656\nconsole.log(\"Static [test]:\", stringToUUID(\"test\")); //e39092c6-1dbb-3ce0-ad3a-2a41db98778c
\r\n
\r\n
\r\n

\n

jsfiddle

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90395", + "creator": "Stefan Steiger", + "createdAt": 1600349880000, + "text": "

Effectively, a GUID, or UUID as it is called in non-Microsoft-circles, is just a 128-Bit cryptographic random number, with the UUID version number (1-5) being at a fixed location byte.

\n

So when you just generate a bunch of random numbers between 0 and 65535 and hex-encode them, like this:

\n
function guid()\n{\n    function s4()\n    {\n        return Math.floor(Math.random() * 65536).toString(16).padStart(4, '0')\n    } // End Function s4\n\n    return s4() + s4() + '-' + s4() + '-' + "4" + s4().substr(1) + '-' + s4() + '-' + s4() + s4() + s4();\n} // End Function guid\n
\n

You get a valid GUID, but due to the random-implementation, it's not cryptographically secure.

\n

To generate a cryptographically secure GUID, you need to use window.crypto (or window.msCrypto for Internet Explorer).

\n

That goes like this:

\n
function cryptGuid()\n{\n    var array = new Uint16Array(8);\n    (window.crypto || window.msCrypto).getRandomValues(array);\n    var dataView = new DataView(array.buffer);\n\n    var parts = [];\n\n    for(var i = 0; i < array.length; ++i)\n    {\n        // 0&1,2,3,4,5-7 dataView.getUint16(0-7)\n        if(i>1 && i<6) parts.push("-");\n        parts.push(dataView.getUint16(i).toString(16).padStart(4, '0'));\n    }\n\n    parts[5] = "4" + parts[5].substr(1);\n    // console.log(parts);\n    return parts.join('').toUpperCase();// .toLowerCase();\n}\n\ncryptGuid();\n
\n

Plus you have to decide, if you return the number as lower-or upper-case character string.\nCertain software require lowercase characters (e.g., Reporting Service), while others generate uppercase characters (SQL Server).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90398", + "creator": "Fernando Teles", + "createdAt": 1615416538000, + "text": "

The most simple function to do this:

\n
function createGuid(){  \n   let S4 = () => Math.floor((1+Math.random())*0x10000).toString(16).substring(1); \n   let guid = `${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;\n   \n   return guid.toLowerCase();  \n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90397", + "creator": "Tikolu", + "createdAt": 1614079895000, + "text": "

One line solution using Blobs.

\n
window.URL.createObjectURL(new Blob([])).substring(31);\n
\n

The value at the end (31) depends on the length of the URL.

\n
\n

EDIT:

\n

A more compact and universal solution, as suggested by rinogo:

\n
URL.createObjectURL(new Blob([])).substr(-36);\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241b082fcc3049e916af", + "creator": "Peter Mortensen", + "createdAt": 1617388425000, + "text": "What is "Blob"/"Blobs"?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916b0", + "creator": "Charaf", + "createdAt": 1647132406000, + "text": "What's the drawback of this solution?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916b2", + "creator": "Tikolu", + "createdAt": 1647220020000, + "text": "@Charaf Doesn't seem to have many drawbacks except for no support on Internet Explorer", + "upvotes": 4726, + "upvoterUsernames": [], + "downvotes": 4726, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916b4", + "creator": "lissajous", + "createdAt": 1649419494000, + "text": "On Safari 15.3 you will get: "ikdev.com/98455e8b-7f1c-44b3-8424-91e06214cb50", so this is not good solution.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916b6", + "creator": "asduj", + "createdAt": 1651234164000, + "text": "instead of substr which is deprecated now, the slice might be used", + "upvotes": 2948, + "upvoterUsernames": [], + "downvotes": 2948, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90399", + "creator": "unsynchronized", + "createdAt": 1623852847000, + "text": "

this offering returns 5 groups of 8 digits from a-z,0-9\nmost of it is random, however incorprates time of day, and has a randomly incrementing counter.\nyou can specify any base you like (hex, decimal, 36), by default chooses a random base for each group of 8, in the range of base 16 to 36

\n

\r\n
\r\n
function newId(base) {\nreturn[\n Math.random,\n function (){ return (newId.last ? windowId.last + Math.random() : Math.random() ) },\n Math.random,\n Date.now,\n Math.random\n].map(function(fn){\n    return fn().toString(base||(16+(Math.random()*20))).substr(-8);\n}).join('-');\n}\n\nvar demo = function(base){\n    document.getElementById('uuid').textContent = newId(base);\n}\ndemo(16);
\r\n
#uuid { font-family: monospace; font-size: 1.5em; }
\r\n
<p id=\"uuid\"></p>\n<button onclick=\"demo(16);\">Hex (base 16)</button>\n<button onclick=\"demo(36);\">Base 36</button>\n<button onclick=\"demo(10);\">Decimal (base 10)</button>\n<button onclick=\"demo();\">Random base</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9039a", + "creator": "blubberdiblub", + "createdAt": 1624700655000, + "text": "

Inspired by broofa's answer I had my own take on it:

\n

Here's the cryptographically stronger version using crypto.getRandomValues.

\n

\r\n
\r\n
function uuidv4() {\n    const a = crypto.getRandomValues(new Uint16Array(8));\n    let i = 0;\n    return '00-0-4-1-000'.replace(/[^-]/g, \n            s => (a[i++] + s * 0x10000 >> s).toString(16).padStart(4, '0')\n    );\n}\n\nconsole.log(uuidv4());
\r\n
\r\n
\r\n

\n

and here's the faster version using Math.random using almost the same principle:

\n

\r\n
\r\n
function uuidv4() {\n    return '00-0-4-1-000'.replace(/[^-]/g,\n            s => ((Math.random() + ~~s) * 0x10000 >> s).toString(16).padStart(4, '0')\n    );\n}\n\nconsole.log(uuidv4());
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9039b", + "creator": "Explosion", + "createdAt": 1630356577000, + "text": "

The coolest way:

\n
function uuid(){\n    var u = URL.createObjectURL(new Blob([""]))\n    URL.revokeObjectURL(u);\n    return u.split("/").slice(-1)[0]\n}\n
\n

It's probably not fast, efficient, or supported in IE2 but it sure is cool

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9039c", + "creator": "Pier-Luc Gendreau", + "createdAt": 1631986877000, + "text": "

Added in: v15.6.0, v14.17.0 there is a built-in crypto.randomUUID() function.

\n
import * as crypto from "crypto";\n\nconst uuid = crypto.randomUUID();\n
\n

In the browser, crypto.randomUUID() is currently supported in Chromium 92+ and Firefox 95+.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241b082fcc3049e916bb", + "creator": "Brian Cannard", + "createdAt": 1645038825000, + "text": "And Safari is coming as well!", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9039e", + "creator": "Slava", + "createdAt": 1634546958000, + "text": "

I use this version. It is safe and simple. It is not to generate formatted uids, it is just to generate random strings of chars you need.

\n
export function makeId(length) {\n  let result = '';\n  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n  const charactersLength = characters.length;\n\n  for (let i = 0; i < length; i++) {\n    let letterPos = crypto.getRandomValues(new Uint8Array(1))[0] / 255 * charactersLength - 1\n    result += characters[letterPos]\n  }\n  return result;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9039d", + "creator": "amn", + "createdAt": 1632437239000, + "text": "

For situations where you already have created a URL for some resource through utilizing the URL.createObjectURL method, you probably won't do faster or shorter than the following:

\n
const uuid = url => url.substr(-36);\n
\n

The above will work with any compliant implementation of createObjectURL, since the specification explicitly mandates a UUID be added to the end of the URL returned by the former. So you are guaranteed the last 36 characters are the UUID part of the generated URL.

\n

To be clear, sometimes -- heck, perhaps most of the time, everything considered -- you want to generate a UUID for something else than resources you create URLs for with createObjectURL. In those cases, calling the latter method on some new Blob() is going to absolutely tank the performance (and leak memory unless you clean up after yourself using the corresponding revokeObjectURL). It is still quite a "one-liner" though.

\n

I do not recommend you use the above method just for generating UUIDs unless you already have URLs obtained through createObjectURL or something that has a UUID at the end.

\n

I just wanted to mention the above variant for completeness.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241b082fcc3049e916bf", + "creator": "vsync", + "createdAt": 1642004145000, + "text": "Do you have a working example that can run on the browser?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916c0", + "creator": "amn", + "createdAt": 1643373221000, + "text": "The answer is certainly valid for Web browsers and so the example above will work.", + "upvotes": 3855, + "upvoterUsernames": [], + "downvotes": 3855, + "downvoterUsernames": [] + }, + { + "_id": "62f3241b082fcc3049e916c1", + "creator": "vsync", + "createdAt": 1643374602000, + "text": "I don't see any code example here which can be copy-pasted and tried in the console", + "upvotes": 2246, + "upvoterUsernames": [], + "downvotes": 2246, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e903a0", + "creator": "Rambler", + "createdAt": 1644219971000, + "text": "

Easy to do with a simple uuid package\nhttps://www.npmjs.com/package/uuid

\n
const { v4: uuidv4 } = require('uuid');\nuuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9039f", + "creator": "Hashbrown", + "createdAt": 1642223556000, + "text": "

Here's a method that generates RFC4122 using true random via random.org. If the fetch fails it falls back to the browser's inbuilt crypto library which should almost be just as good. And finally, if the user's browser in question doesn't support that, it uses Math.random().

\n
async function UUID() {\n    //get 31 random hex characters\n    return (await (async () => {\n        let output;\n        try {\n            //try from random.org\n            output = (await (\n                await fetch('https://www.random.org/integers/?num=31&min=0&max=15&col=31&base=16&format=plain&rnd=new')\n            ).text())\n                //get rid of whitespace\n                .replace(/[^0-9a-fA-F]+/g, '')\n            ;\n            if (output.length != 31)\n                throw '';\n        }\n        catch {\n            output = '';\n            try {\n                //failing that, try getting 16 8-bit digits from crypto\n                for (let num of crypto.getRandomValues(new Uint8Array(16)))\n                    //interpret as 32 4-bit hex numbers\n                    output += (num >> 4).toString(16) + (num & 15).toString(16);\n                //we only want 31\n                output = output.substr(1);\n            }\n            catch {\n                //failing THAT, use Math.random\n                while (output.length < 31)\n                    output += (0 | Math.random() * 16).toString(16);\n            }\n        }\n        return output;\n    })())\n        //split into appropriate sections, and set the 15th character to 4\n        .replace(/^(.{8})(.{4})(.{3})(.{4})/, '$1-$2-4$3-$4-')\n        //force character 20 to the correct range\n        .replace(/(?<=-)[^89abAB](?=[^-]+-[^-]+$)/, (num) => (\n            (parseInt(num, 16) % 4 + 8).toString(16)\n        ))\n    ;\n}\n
\n

\"enter

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241c082fcc3049e916c3", + "creator": "Charaf", + "createdAt": 1647132210000, + "text": "One should never rely on external sources to generate one's randomness, especially when it comes to generating wallet addresses for instance.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90379", + "creator": "Andrew", + "createdAt": 1425775171000, + "text": "

I wanted to understand broofa's answer, so I expanded it and added comments:

\n
var uuid = function () {\n    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(\n        /[xy]/g,\n        function (match) {\n            /*\n            * Create a random nibble. The two clever bits of this code:\n            *\n            * - Bitwise operations will truncate floating point numbers\n            * - For a bitwise OR of any x, x | 0 = x\n            *\n            * So:\n            *\n            * Math.random * 16\n            *\n            * creates a random floating point number\n            * between 0 (inclusive) and 16 (exclusive) and\n            *\n            * | 0\n            *\n            * truncates the floating point number into an integer.\n            */\n            var randomNibble = Math.random() * 16 | 0;\n\n            /*\n            * Resolves the variant field. If the variant field (delineated\n            * as y in the initial string) is matched, the nibble must\n            * match the mask (where x is a do-not-care bit):\n            *\n            * 10xx\n            *\n            * This is achieved by performing the following operations in\n            * sequence (where x is an intermediate result):\n            *\n            * - x & 0x3, which is equivalent to x % 3\n            * - x | 0x8, which is equivalent to x + 8\n            *\n            * This results in a nibble between 8 inclusive and 11 exclusive,\n            * (or 1000 and 1011 in binary), all of which satisfy the variant\n            * field mask above.\n            */\n            var nibble = (match == 'y') ?\n                (randomNibble & 0x3 | 0x8) :\n                randomNibble;\n\n            /*\n            * Ensure the nibble integer is encoded as base 16 (hexadecimal).\n            */\n            return nibble.toString(16);\n        }\n    );\n};\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bc082fcc3049e92dee", + "creator": "Egor Litvinchuk", + "createdAt": 1586604415000, + "text": "Thank you for detailed description! Specifically nibble caged between 8 and 11 with equivalents explanation is super helpful.", + "upvotes": 227, + "upvoterUsernames": [], + "downvotes": 227, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9037c", + "creator": "Matthew Riches", + "createdAt": 1439540562000, + "text": "

Yet another way of doing the same thing:

\n
function guid() {\n  var chars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];\n  var str = "";\n  for(var i=0; i<36; i++) {\n    var str = str + ((i == 8 || i == 13 || i == 18 || i == 23) ? "-" : chars[Math.floor(Math.random()*chars.length)]);\n  };\n  return str;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9037b", + "creator": "robocat", + "createdAt": 1433293125000, + "text": "

The following is simple code that uses crypto.getRandomValues(a) on supported browsers (Internet Explorer 11+, iOS 7+, Firefox 21+, Chrome, and Android Chrome).

\n

It avoids using Math.random(), because that can cause collisions (for example 20 collisions for 4000 generated UUIDs in a real situation by Muxa).

\n
function uuid() {\n    function randomDigit() {\n        if (crypto && crypto.getRandomValues) {\n            var rands = new Uint8Array(1);\n            crypto.getRandomValues(rands);\n            return (rands[0] % 16).toString(16);\n        } else {\n            return ((Math.random() * 16) | 0).toString(16);\n        }\n    }\n\n    var crypto = window.crypto || window.msCrypto;\n    return 'xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx'.replace(/x/g, randomDigit);\n}\n
\n

Notes:

\n\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9037d", + "creator": "Kyros Koh", + "createdAt": 1444701019000, + "text": "

You can use node-uuid. It provides simple, fast generation of RFC4122 UUIDS.

\n

Features:

\n\n
\n

Install Using NPM:

\n
npm install uuid\n
\n
\n

Or using uuid via a browser:

\n

Download Raw File (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js\nDownload Raw File (uuid v4): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v4.js

\n
\n

Want even smaller? Check this out: https://gist.github.com/jed/982883

\n
\n

Usage:

\n
// Generate a v1 UUID (time-based)\nconst uuidV1 = require('uuid/v1');\nuuidV1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'\n\n// Generate a v4 UUID (random)\nconst uuidV4 = require('uuid/v4');\nuuidV4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'\n\n// Generate a v5 UUID (namespace)\nconst uuidV5 = require('uuid/v5');\n\n// ... using predefined DNS namespace (for domain names)\nuuidV5('hello.example.com', v5.DNS)); // -> 'fdda765f-fc57-5604-a269-52a7df8164ec'\n\n// ... using predefined URL namespace (for, well, URLs)\nuuidV5('http://example.com/hello', v5.URL); // -> '3bbcee75-cecc-5b56-8031-b6641c1ed1f1'\n\n// ... using a custom namespace\nconst MY_NAMESPACE = '(previously generated unique uuid string)';\nuuidV5('hello', MY_NAMESPACE); // -> '90123e1c-7512-523e-bb28-76fab9f2f73d'\n
\n
\n

ECMAScript 2015 (ES6):

\n
import uuid from 'uuid/v4';\nconst id = uuid();\n
\n", + "upvotes": 87, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9037e", + "creator": "andersh", + "createdAt": 1445934781000, + "text": "

You could use the npm package guid, a GUID generator and validator.

\n

Example:

\n
Guid.raw();\n// -> '6fdf6ffc-ed77-94fa-407e-a7b86ed9e59d'\n
\n

Note: This package has been deprecated. Use uuid instead.

\n

Example:

\n
const uuidv4 = require('uuid/v4');\nuuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9037f", + "creator": "MaxPRafferty", + "createdAt": 1454343968000, + "text": "

Just in case anyone dropping by Google is seeking a small utility library, ShortId meets all the requirements of this question. It allows specifying allowed characters and length, and guarantees non-sequential, non-repeating strings.

\n

To make this more of a real answer, the core of that library uses the following logic to produce its short ids:

\n
function encode(lookup, number) {\n    var loopCounter = 0;\n    var done;\n\n    var str = '';\n\n    while (!done) {\n        str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() );\n        done = number < (Math.pow(16, loopCounter + 1 ) );\n        loopCounter++;\n    }\n    return str;\n}\n\n/* Generates the short id */\nfunction generate() {\n\n    var str = '';\n\n    var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001);\n\n    if (seconds === previousSeconds) {\n        counter++;\n    } else {\n        counter = 0;\n        previousSeconds = seconds;\n    }\n\n    str = str + encode(alphabet.lookup, version);\n    str = str + encode(alphabet.lookup, clusterWorkerId);\n    if (counter > 0) {\n        str = str + encode(alphabet.lookup, counter);\n    }\n    str = str + encode(alphabet.lookup, seconds);\n\n    return str;\n}\n
\n

I have not edited this to reflect only the most basic parts of this approach, so the above code includes some additional logic from the library. If you are curious about everything it is doing, take a look at the source: https://github.com/dylang/shortid/tree/master/lib

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90380", + "creator": "Ashish Yadav", + "createdAt": 1458628657000, + "text": "

Here is a working example. It generates a 32-digit unique UUID.

\n
function generateUUID() {\n    var d = new Date();\n    var k = d.getTime();\n    var str = k.toString(16).slice(1)\n    var UUID = 'xxxx-xxxx-4xxx-yxxx-xzx'.replace(/[xy]/g, function (c)\n    {\n        var r = Math.random() * 16 | 0;\n        v = c == 'x' ? r : (r & 3 | 8);\n        return v.toString(16);\n    });\n\n    var newString = UUID.replace(/[z]/, str)\n    return newString;\n}\n\nvar x = generateUUID()\nconsole.log(x, x.length)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2586423, + "uvac": 2586483 + } + }, + { + "_id": "62f321bb082fcc3049e8feb0", + "title": "How do JavaScript closures work?", + "title-lowercase": "how do javascript closures work?", + "creator": "e-satis", + "createdAt": 1222006327000, + "status": "open", + "text": "

How would you explain JavaScript closures to someone with a knowledge of the concepts they consist of (for example functions, variables and the like), but does not understand closures themselves?

\n\n

I have seen the Scheme example given on Wikipedia, but unfortunately it did not help.

\n", + "upvotes": 14356, + "upvoterUsernames": [], + "downvotes": 6733, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1580406, + "answers": 65, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e90000", + "creator": "Konrad Rudolph", + "createdAt": 1222007086000, + "text": "

Closures are hard to explain because they are used to make some behaviour work that everybody intuitively expects to work anyway. I find the best way to explain them (and the way that I learned what they do) is to imagine the situation without them:

\n

\r\n
\r\n
const makePlus = function(x) {\n    return function(y) { return x + y; };\n}\n\nconst plus5 = makePlus(5);\nconsole.log(plus5(3));
\r\n
\r\n
\r\n

\n

What would happen here if JavaScript didn't know closures? Just replace the call in the last line by its method body (which is basically what function calls do) and you get:

\n
console.log(x + 3);\n
\n

Now, where's the definition of x? We didn't define it in the current scope. The only solution is to let plus5 carry its scope (or rather, its parent's scope) around. This way, x is well-defined and it is bound to the value 5.

\n", + "upvotes": 720, + "upvoterUsernames": [], + "downvotes": 183, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90002", + "creator": "dlaliberte", + "createdAt": 1270734880000, + "text": "

This is an attempt to clear up several (possible) misunderstandings about closures that appear in some of the other answers.

\n\n\n", + "upvotes": 523, + "upvoterUsernames": [], + "downvotes": 144, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90001", + "creator": "Rakesh Pai", + "createdAt": 1222033198000, + "text": "

A closure is where an inner function has access to variables in its outer function. That's probably the simplest one-line explanation you can get for closures.

\n", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90004", + "creator": "Chris S", + "createdAt": 1271751406000, + "text": "

Can you explain closures to a 5-year-old?*

\n

I still think Google's explanation works very well and is concise:

\n
/*\n*    When a function is defined in another function and it\n*    has access to the outer function's context even after\n*    the outer function returns.\n*\n* An important concept to learn in JavaScript.\n*/\n\nfunction outerFunction(someNum) {\n    var someString = 'Hey!';\n    var content = document.getElementById('content');\n    function innerFunction() {\n        content.innerHTML = someNum + ': ' + someString;\n        content = null; // Internet Explorer memory leak for DOM reference\n    }\n    innerFunction();\n}\n\nouterFunction(1);​\n
\n

\"Proof

\n

*A C# question

\n", + "upvotes": 265, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90003", + "creator": "someisaac", + "createdAt": 1271751017000, + "text": "

Example for the first point by dlaliberte:

\n\n
\n

A closure is not only created when you return an inner function. In fact, the enclosing function does not need to return at all. You might instead assign your inner function to a variable in an outer scope, or pass it as an argument to another function where it could be used immediately. Therefore, the closure of the enclosing function probably already exists at the time that enclosing function was called since any inner function has access to it as soon as it is called.

\n
\n\n
var i;\nfunction foo(x) {\n    var tmp = 3;\n    i = function (y) {\n        console.log(x + y + (++tmp));\n    }\n}\nfoo(2);\ni(3);\n
\n", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90005", + "creator": "Nathan Long", + "createdAt": 1297112256000, + "text": "

I wrote a blog post a while back explaining closures. Here's what I said about closures in terms of why you'd want one.

\n\n
\n

Closures are a way to let a function\n have persistent, private variables -\n that is, variables that only one\n function knows about, where it can\n keep track of info from previous times\n that it was run.

\n
\n\n

In that sense, they let a function act a bit like an object with private attributes.

\n\n

Full post:

\n\n

So what are these closure thingys?

\n", + "upvotes": 463, + "upvoterUsernames": [], + "downvotes": 214, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90006", + "creator": "John Pick", + "createdAt": 1298511473000, + "text": "

JavaScript functions can access their:

\n\n
    \n
  1. Arguments
  2. \n
  3. Locals (that is, their local variables and local functions)
  4. \n
  5. Environment, which includes:\n\n
      \n
    • globals, including the DOM
    • \n
    • anything in outer functions
    • \n
  6. \n
\n\n

If a function accesses its environment, then the function is a closure.

\n\n

Note that outer functions are not required, though they do offer benefits I don't discuss here. By accessing data in its environment, a closure keeps that data alive. In the subcase of outer/inner functions, an outer function can create local data and eventually exit, and yet, if any inner function(s) survive after the outer function exits, then the inner function(s) keep the outer function's local data alive.

\n\n

Example of a closure that uses the global environment:

\n\n

Imagine that the Stack Overflow Vote-Up and Vote-Down button events are implemented as closures, voteUp_click and voteDown_click, that have access to external variables isVotedUp and isVotedDown, which are defined globally. (For simplicity's sake, I am referring to StackOverflow's Question Vote buttons, not the array of Answer Vote buttons.)

\n\n

When the user clicks the VoteUp button, the voteUp_click function checks whether isVotedDown == true to determine whether to vote up or merely cancel a down vote. Function voteUp_click is a closure because it is accessing its environment.

\n\n
var isVotedUp = false;\nvar isVotedDown = false;\n\nfunction voteUp_click() {\n  if (isVotedUp)\n    return;\n  else if (isVotedDown)\n    SetDownVote(false);\n  else\n    SetUpVote(true);\n}\n\nfunction voteDown_click() {\n  if (isVotedDown)\n    return;\n  else if (isVotedUp)\n    SetUpVote(false);\n  else\n    SetDownVote(true);\n}\n\nfunction SetUpVote(status) {\n  isVotedUp = status;\n  // Do some CSS stuff to Vote-Up button\n}\n\nfunction SetDownVote(status) {\n  isVotedDown = status;\n  // Do some CSS stuff to Vote-Down button\n}\n
\n\n

All four of these functions are closures as they all access their environment.

\n", + "upvotes": 141, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90008", + "creator": "StewShack", + "createdAt": 1311133887000, + "text": "

You're having a sleep over and you invite Dan.\nYou tell Dan to bring one XBox controller.

\n\n

Dan invites Paul.\nDan asks Paul to bring one controller. How many controllers were brought to the party?

\n\n
function sleepOver(howManyControllersToBring) {\n\n    var numberOfDansControllers = howManyControllersToBring;\n\n    return function danInvitedPaul(numberOfPaulsControllers) {\n        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;\n        return totalControllers;\n    }\n}\n\nvar howManyControllersToBring = 1;\n\nvar inviteDan = sleepOver(howManyControllersToBring);\n\n// The only reason Paul was invited is because Dan was invited. \n// So we set Paul's invitation = Dan's invitation.\n\nvar danInvitedPaul = inviteDan(howManyControllersToBring);\n\nalert(\"There were \" + danInvitedPaul + \" controllers brought to the party.\");\n
\n", + "upvotes": 132, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90007", + "creator": "Jacob Swartwood", + "createdAt": 1308941356000, + "text": "

FOREWORD: this answer was written when the question was:

\n\n
\n

Like the old Albert said : \"If you can't explain it to a six-year old, you really don't understand it yourself.”. Well I tried to explain JS closures to a 27 years old friend and completely failed.

\n \n

Can anybody consider that I am 6 and strangely interested in that subject ?

\n
\n\n

I'm pretty sure I was one of the only people that attempted to take the initial question literally. Since then, the question has mutated several times, so my answer may now seem incredibly silly & out of place. Hopefully the general idea of the story remains fun for some.

\n\n
\n\n

I'm a big fan of analogy and metaphor when explaining difficult concepts, so let me try my hand with a story.

\n\n

Once upon a time:

\n\n

There was a princess...

\n\n
function princess() {\n
\n\n

She lived in a wonderful world full of adventures. She met her Prince Charming, rode around her world on a unicorn, battled dragons, encountered talking animals, and many other fantastical things.

\n\n
    var adventures = [];\n\n    function princeCharming() { /* ... */ }\n\n    var unicorn = { /* ... */ },\n        dragons = [ /* ... */ ],\n        squirrel = \"Hello!\";\n\n    /* ... */\n
\n\n

But she would always have to return back to her dull world of chores and grown-ups.

\n\n
    return {\n
\n\n

And she would often tell them of her latest amazing adventure as a princess.

\n\n
        story: function() {\n            return adventures[adventures.length - 1];\n        }\n    };\n}\n
\n\n

But all they would see is a little girl...

\n\n
var littleGirl = princess();\n
\n\n

...telling stories about magic and fantasy.

\n\n
littleGirl.story();\n
\n\n

And even though the grown-ups knew of real princesses, they would never believe in the unicorns or dragons because they could never see them. The grown-ups said that they only existed inside the little girl's imagination.

\n\n

But we know the real truth; that the little girl with the princess inside...

\n\n

...is really a princess with a little girl inside.

\n", + "upvotes": 4445, + "upvoterUsernames": [], + "downvotes": 1867, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3229c082fcc3049e91182", + "creator": "Tiago Martins Peres", + "createdAt": 1603438448000, + "text": "Oh nice, I was that close to make an edit to remove the what I thought to be the extra space in the beginning. Nice job, +1", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e90009", + "creator": "Nathan Whitehead", + "createdAt": 1311655052000, + "text": "

I put together an interactive JavaScript tutorial to explain how closures work.\nWhat's a Closure?

\n\n

Here's one of the examples:

\n\n
var create = function (x) {\n    var f = function () {\n        return x; // We can refer to x here!\n    };\n    return f;\n};\n// 'create' takes one argument, creates a function\n\nvar g = create(42);\n// g is a function that takes no arguments now\n\nvar y = g();\n// y is 42 here\n
\n", + "upvotes": 217, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000a", + "creator": "mykhal", + "createdAt": 1312036054000, + "text": "

Wikipedia on closures:

\n\n
\n

In computer science, a closure is a function together with a referencing environment for the nonlocal names (free variables) of that function.

\n
\n\n

Technically, in JavaScript, every function is a closure. It always has an access to variables defined in the surrounding scope.

\n\n

Since scope-defining construction in JavaScript is a function, not a code block like in many other languages, what we usually mean by closure in JavaScript is a function working with nonlocal variables defined in already executed surrounding function.

\n\n

Closures are often used for creating functions with some hidden private data (but it's not always the case).

\n\n
var db = (function() {\n    // Create a hidden object, which will hold the data\n    // it's inaccessible from the outside.\n    var data = {};\n\n    // Make a function, which will provide some access to the data.\n    return function(key, val) {\n        if (val === undefined) { return data[key] } // Get\n        else { return data[key] = val } // Set\n    }\n    // We are calling the anonymous surrounding function,\n    // returning the above inner function, which is a closure.\n})();\n\ndb('x')    // -> undefined\ndb('x', 1) // Set x to 1\ndb('x')    // -> 1\n// It's impossible to access the data object itself.\n// We are able to get or set individual it.\n
\n\n

ems

\n\n

The example above is using an anonymous function, which was executed once. But it does not have to be. It can be named (e.g. mkdb) and executed later, generating a database function each time it is invoked. Every generated function will have its own hidden database object. Another usage example of closures is when we don't return a function, but an object containing multiple functions for different purposes, each of those function having access to the same data.

\n", + "upvotes": 220, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000c", + "creator": "Gerardo Lima", + "createdAt": 1336069014000, + "text": "

I know there are plenty of solutions already, but I guess that this small and simple script can be useful to demonstrate the concept:

\n\n
// makeSequencer will return a \"sequencer\" function\nvar makeSequencer = function() {\n    var _count = 0; // not accessible outside this function\n    var sequencer = function () {\n        return _count++;\n    }\n    return sequencer;\n}\n\nvar fnext = makeSequencer();\nvar v0 = fnext();     // v0 = 0;\nvar v1 = fnext();     // v1 = 1;\nvar vz = fnext._count // vz = undefined\n
\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000d", + "creator": "goonerify", + "createdAt": 1343251588000, + "text": "

After a function is invoked, it goes out of scope. If that function contains something like a callback function, then that callback function is still in scope. If the callback function references some local variable in the immediate environment of the parent function, then naturally you'd expect that variable to be inaccessible to the callback function and return undefined.

\n\n

Closures ensure that any property that is referenced by the callback function is available for use by that function, even when its parent function may have gone out of scope.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000e", + "creator": "ketan", + "createdAt": 1354359225000, + "text": "

Closures are a means through which inner functions can refer to the variables present in their outer enclosing function after their parent functions have already terminated.

\n\n
// A function that generates a new function for adding numbers.\nfunction addGenerator( num ) {\n    // Return a simple function for adding two numbers\n    // with the first number borrowed from the generator\n    return function( toAdd ) {\n        return num + toAdd\n    };\n}\n\n// addFive now contains a function that takes one argument,\n// adds five to it, and returns the resulting number.\nvar addFive = addGenerator( 5 );\n// We can see here that the result of the addFive function is 9,\n// when passed an argument of 4.\nalert( addFive( 4 ) == 9 );\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000b", + "creator": "dlaliberte", + "createdAt": 1314977019000, + "text": "

Taking the question seriously, we should find out what a typical 6-year-old is capable of cognitively, though admittedly, one who is interested in JavaScript is not so typical.

\n\n

On Childhood Development: 5 to 7 Years it says:

\n\n
\n

Your child will be able to follow two-step directions. For example, if you say to your child, \"Go to the kitchen and get me a trash bag\" they will be able to remember that direction.

\n
\n\n

We can use this example to explain closures, as follows:

\n\n
\n

The kitchen is a closure that has a local variable, called trashBags. There is a function inside the kitchen called getTrashBag that gets one trash bag and returns it.

\n
\n\n

We can code this in JavaScript like this:

\n\n

\r\n
\r\n
function makeKitchen() {\r\n  var trashBags = ['A', 'B', 'C']; // only 3 at first\r\n\r\n  return {\r\n    getTrashBag: function() {\r\n      return trashBags.pop();\r\n    }\r\n  };\r\n}\r\n\r\nvar kitchen = makeKitchen();\r\n\r\nconsole.log(kitchen.getTrashBag()); // returns trash bag C\r\nconsole.log(kitchen.getTrashBag()); // returns trash bag B\r\nconsole.log(kitchen.getTrashBag()); // returns trash bag A
\r\n
\r\n
\r\n

\n\n

Further points that explain why closures are interesting:

\n\n\n", + "upvotes": 957, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9000f", + "creator": "mjmoody383", + "createdAt": 1362799475000, + "text": "

I'd simply point them to the Mozilla Closures page. It's the best, most concise and simple explanation of closure basics and practical usage that I've found. It is highly recommended to anyone learning JavaScript.

\n\n

And yes, I'd even recommend it to a 6-year old -- if the 6-year old is learning about closures, then it's logical they're ready to comprehend the concise and simple explanation provided in the article.

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90010", + "creator": "Jean-Baptiste Yunès", + "createdAt": 1363009064000, + "text": "

If you want to explain it to a six-year old child then you must find something very much simpler and NO code.

\n\n

Just tell the child that he is \"open\", which says that he is able to have relations with some others, his friends. At some point in time, he has determined friends (we can know the names of his friends), that is a closure. If you take a picture of him and his friends then he is \"closed\" relatively to his friendship ability. But in general, he is \"open\". During his whole life he will have many different sets of friends. One of these sets is a closure.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90011", + "creator": "Jim", + "createdAt": 1366854072000, + "text": "

I found very clear chapter 8 section 6, \"Closures,\" of JavaScript: The Definitive Guide by David Flanagan, 6th edition, O'Reilly, 2011. I'll try to paraphrase.

\n\n
    \n
  1. When a function is invoked, a new object is created to hold the local variables for that invocation.

  2. \n
  3. A function's scope depends on its declaration location, not its execution location.

  4. \n
\n\n

Now, assume an inner function declared within an outer function and referring to variables of that outer function. Further assume the outer function returns the inner function, as a function. Now there is an external reference to whatever values were in the inner function's scope (which, by our assumptions, includes values from the outer function).

\n\n

JavaScript will preserve those values, as they have remained in scope of the current execution thanks to being passed out of the completed outer function. All functions are closures, but the closures of interest are the inner functions which, in our assumed scenario, preserve outer function values within their \"enclosure\" (I hope I'm using language correctly here) when they (the inner functions) are returned from outer functions. I know this doesn't meet the six-year-old requirement, but hopefully it is still helpful.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90012", + "creator": "moha297", + "createdAt": 1367683560000, + "text": "

A function is executed in the scope of the object/function in which it is defined. The said function can access the variables defined in the object/function where it has been defined while it is executing.

\n\n

And just take it literally.... as the code is written :P

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90013", + "creator": "Charlie", + "createdAt": 1368109082000, + "text": "

For a six-year-old?

\n\n

You and your family live in the mythical town of Ann Ville. You have a friend who lives next door, so you call them and ask them to come out and play. You dial:

\n\n
\n

000001 (jamiesHouse)

\n
\n\n

After a month, you and your family move out of Ann Ville to the next town, but you and your friend still keep in touch, so now you have to dial the area code for the town that your friend lives in, before dialling their 'proper' number:

\n\n
\n

001 000001 (annVille.jamiesHouse)

\n
\n\n

A year after that, your parents move to a whole new country, but you and your friend still keep in touch, so after bugging your parents to let you make international rate calls, you now dial:

\n\n
\n

01 001 000001 (myOldCountry.annVille.jamiesHouse)

\n
\n\n

Strangely though, after moving to your new country, you and your family just so happen to move to a new town called Ann Ville... and you just so happen to make friends with some new person called Jamie... You give them a call...

\n\n
\n

000001 (jamiesHouse)

\n
\n\n

Spooky...

\n\n

So spooky in fact, that you tell Jamie from your old country about it... You have a good laugh about it. So one day, you and your family take a holiday back to the old country. You visit your old town (Ann Ville), and go to visit Jamie...

\n\n\n\n
\n

02 001 000001 (myNewCountry.annVille.jamiesHouse)

\n
\n\n

Opinions?

\n\n

What's more, I have a load of questions about the patience of a modern six-year-old...

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90014", + "creator": "Stupid Stupid", + "createdAt": 1368737526000, + "text": "

An answer for a six-year-old (assuming he knows what a function is and what a variable is, and what data is):

\n\n

Functions can return data. One kind of data you can return from a function is another function. When that new function gets returned, all the variables and arguments used in the function that created it don't go away. Instead, that parent function \"closes.\" In other words, nothing can look inside of it and see the variables it used except for the function it returned. That new function has a special ability to look back inside the function that created it and see the data inside of it.

\n\n
function the_closure() {\n  var x = 4;\n  return function () {\n    return x; // Here, we look back inside the_closure for the value of x\n  }\n}\n\nvar myFn = the_closure();\nmyFn(); //=> 4\n
\n\n

Another really simple way to explain it is in terms of scope:

\n\n

Any time you create a smaller scope inside of a larger scope, the smaller scope will always be able to see what is in the larger scope.

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90015", + "creator": "Arman", + "createdAt": 1370450389000, + "text": "

I'm sure, Einstein didn't say it with a direct expectation for us to pick any esoteric brainstormer thing and run over six-year-olds with futile attempts to get those 'crazy' (and what is even worse for them-boring) things to their childish minds :) If I were six years old I wouldn't like to have such parents or wouldn't make friendship with such boring philanthropists, sorry :)

\n\n

Anyway, for babies, closure is simply a hug, I guess, whatever way you try to explain :) And when you hug a friend of yours then you both kind of share anything you guys have at the moment. It's a rite of passage, once you've hugged somebody you're showing her trust and willingness to let her do with you a lot of things you don't allow and would hide from others. It's an act of friendship :).

\n\n

I really don't know how to explain it to 5-6 years old babies. I neither think they will appreciate any JavaScript code snippets like:

\n\n
function Baby(){\n    this.iTrustYou = true;\n}\n\nBaby.prototype.hug = function (baby) {\n    var smiles = 0;\n\n    if (baby.iTrustYou) {\n        return function() {\n            smiles++;\n            alert(smiles);\n        };\n    }\n};\n\nvar\n   arman = new Baby(\"Arman\"),\n   morgan = new Baby(\"Morgana\");\n\nvar hug = arman.hug(morgan);\nhug();\nhug();\n
\n\n

For children only:

\n\n

Closure is hug

\n\n

Bug is fly

\n\n

KISS is smooch! :)

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90016", + "creator": "Max Tkachenko", + "createdAt": 1370514175000, + "text": "

OK, 6-year-old closures fan. Do you want to hear the simplest example of closure?

\n\n

Let's imagine the next situation: a driver is sitting in a car. That car is inside a plane. Plane is in the airport. The ability of driver to access things outside his car, but inside the plane, even if that plane leaves an airport, is a closure. That's it. When you turn 27, look at the more detailed explanation or at the example below.

\n\n

Here is how I can convert my plane story into the code.

\n\n

\r\n
\r\n
var plane = function(defaultAirport) {\r\n\r\n  var lastAirportLeft = defaultAirport;\r\n\r\n  var car = {\r\n    driver: {\r\n      startAccessPlaneInfo: function() {\r\n        setInterval(function() {\r\n          console.log(\"Last airport was \" + lastAirportLeft);\r\n        }, 2000);\r\n      }\r\n    }\r\n  };\r\n  car.driver.startAccessPlaneInfo();\r\n\r\n  return {\r\n    leaveTheAirport: function(airPortName) {\r\n      lastAirportLeft = airPortName;\r\n    }\r\n  }\r\n}(\"Boryspil International Airport\");\r\n\r\nplane.leaveTheAirport(\"John F. Kennedy\");
\r\n
\r\n
\r\n

\n", + "upvotes": 601, + "upvoterUsernames": [], + "downvotes": 200, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90017", + "creator": "Vitim.us", + "createdAt": 1371928114000, + "text": "

Given the following function

\n\n
function person(name, age){\n\n    var name = name;\n    var age = age;\n\n    function introduce(){\n        alert(\"My name is \"+name+\", and I'm \"+age);\n    }\n\n    return introduce;\n}\n\nvar a = person(\"Jack\",12);\nvar b = person(\"Matt\",14);\n
\n\n

Everytime the function person is called a new closure is created. While variables a and b have the same introduce function, it is linked to different closures. And that closure will still exist even after the function person finishes execution.

\n\n

\"Enter

\n\n
a(); //My name is Jack, and I'm 12\nb(); //My name is Matt, and I'm 14\n
\n\n

An abstract closures could be represented to something like this:

\n\n
closure a = {\n    name: \"Jack\",\n    age: 12,\n    call: function introduce(){\n        alert(\"My name is \"+name+\", and I'm \"+age);\n    }\n}\n\nclosure b = {\n    name: \"Matt\",\n    age: 14,\n    call: function introduce(){\n        alert(\"My name is \"+name+\", and I'm \"+age);\n    }\n}\n
\n\n
\n\n

Assuming you know how a class in another language work, I will make an analogy.

\n\n

Think like

\n\n\n\n

Everytime a function is called

\n\n\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90018", + "creator": "Raul Martins", + "createdAt": 1371943853000, + "text": "

Considering the question is about explaining it simply as if to a 6-year-old, my answer would be:

\n\n

\"When you declare a function in JavaScript it has forever access to all the variables and functions that were available in the line before that function declaration. The function and all the outer variables and functions that it has access to is what we call a closure.\"

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90019", + "creator": "Taye", + "createdAt": 1376668848000, + "text": "

A closure is basically creating two things :\n- a function\n- a private scope that only that function can access

\n\n

It is like putting some coating around a function.

\n\n

So to a 6-years-old, it could be explained by giving an analogy. Let's say I build a robot. That robot can do many things. Among those things, I programmed it to count the number of birds he sees in the sky. Each time he has seen 25 birds, he should tell me how many birds he has seen since the beginning.

\n\n

I don't know how many birds he has seen unless he has told me. Only he knows. That's the private scope. That's basically the robot's memory. Let's say I gave him 4 GB.

\n\n

Telling me how many birds he has seen is the returned function. I also created that.

\n\n

That analogy is a bit sucky, but someone could improve it I guess.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001a", + "creator": "cube", + "createdAt": 1382363279000, + "text": "

The word closure simply refers to being able to access objects (six-year-old: things) that are closed (six-year-old: private) within a function (six-year-old: box). Even if the function (six-year-old: box) is out of scope (six-year-old: sent far away).

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001b", + "creator": "roland", + "createdAt": 1390668194000, + "text": "

The more I think about closure the more I see it as a 2-step process: init - action

\n\n
init: pass first what's needed...\naction: in order to achieve something for later execution.\n
\n\n

To a 6-year old, I'd emphasize on the practical aspect of closure:

\n\n
Daddy: Listen. Could you bring mum some milk (2).\nTom: No problem.\nDaddy: Take a look at the map that Daddy has just made: mum is there and daddy is here.\nDaddy: But get ready first. And bring the map with you (1), it may come in handy\nDaddy: Then off you go (3). Ok?\nTom: A piece of cake!\n
\n\n

Example: Bring some milk to mum (=action). First get ready and bring the map (=init).

\n\n
function getReady(map) {\n    var cleverBoy = 'I examine the ' + map;\n    return function(what, who) {\n        return 'I bring ' + what + ' to ' + who + 'because + ' cleverBoy; //I can access the map\n    }\n}\nvar offYouGo = getReady('daddy-map');\noffYouGo('milk', 'mum');\n
\n\n

Because if you bring with you a very important piece of information (the map), you're knowledgeable enough to execute other similar actions:

\n\n
offYouGo('potatoes', 'great mum');\n
\n\n

To a developer I'd make a parallel between closures and OOP.\nThe init phase is similar to passing arguments to a constructor in a traditional OO language; the action phase is ultimately the method you call to achieve what you want. And the method has access these init arguments using a mechanism called closure.

\n\n

See my another answer illustrating the parallelism between OO and closures:

\n\n

How to "properly" create a custom object in JavaScript?

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001c", + "creator": "Magne", + "createdAt": 1392671648000, + "text": "

The original question had a quote:

\n
\n

If you can't explain it to a six-year old, you really don't understand it yourself.

\n
\n

This is how I'd try to explain it to an actual six-year-old:

\n

You know how grown-ups can own a house, and they call it home? When a mom has a child, the child doesn't really own anything, right? But its parents own a house, so whenever someone asks the child "Where's your home?", he/she can answer "that house!", and point to the house of its parents. A "Closure" is the ability of the child to always (even if abroad) be able to say it has a home, even though it's really the parent's who own the house.

\n", + "upvotes": 277, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001e", + "creator": "Juan Garcia", + "createdAt": 1395237696000, + "text": "

If you understand it well you can explain it simple. And the simplest way is abstracting it from the context. Code aside, even programming aside. A metaphor example will do it better.

\n\n

Let's imagine that a function is a room whose walls are of glass, but they are special glass, like the ones in an interrogation room. From outside they are opaque, from inside they are transparent. It can be rooms inside other rooms, and the only way of contact is a phone.

\n\n

If you call from the outside, you don't know what is in it, but you know that the people inside will do a task if you give them certain information. They can see outside, so they can ask you for stuff that are outside and make changes to that stuff, but you can't change what it is inside from the outside, you don't even see (know) what it is inside. The people inside that room you are calling see what it is outside, but not what it is inside the rooms in that room, so they interact with them the way you are doing from outside. The people inside the most inner rooms can see many things, but the people of the most outer room don't even know about the most inner rooms' existence.

\n\n

For each call to an inner room, the people in that room keeps a record of the information about that specific call, and they are so good doing that that they never mistake one call stuff with other call stuff.

\n\n

Rooms are functions, visibility is scope, people doing task is statements, stuff are objects, phone calls are function calls, phone call information is arguments, call records are scope instances, the most outer room is the global object.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90020", + "creator": "Pawel Furmaniak", + "createdAt": 1400329830000, + "text": "

Closure is when a function is closed in a way that it was defined in a namespace which is immutable by the time the function is called.

\n\n

In JavaScript, it happens when you:

\n\n\n\n\n\n
// 'name' is resolved in the namespace created for one invocation of bindMessage\n// the processor cannot enter this namespace by the time displayMessage is called\nfunction bindMessage(name, div) {\n\n    function displayMessage() {\n        alert('This is ' + name);\n    }\n\n    $(div).click(displayMessage);\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001d", + "creator": "Nick Manning", + "createdAt": 1394663018000, + "text": "

The simplest, shortest, most-easy-to-understand answer:

\n

A closure is a block of code where each line can reference the same set of variables with the same variable names.

\n

If "this" means something different than it does somewhere else, then you know it is two different closures.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9001f", + "creator": "Bhojendra Rauniyar", + "createdAt": 1396516301000, + "text": "

A closure is created when the inner function is somehow made available to any scope outside the outer function.

\n\n

Example:

\n\n
var outer = function(params){ //Outer function defines a variable called params\n    var inner = function(){ // Inner function has access to the params variable of the outer function\n        return params;\n    }\n    return inner; //Return inner function exposing it to outer scope\n},\nmyFunc = outer(\"myParams\");\nmyFunc(); //Returns \"myParams\"\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90021", + "creator": "nomen", + "createdAt": 1401655154000, + "text": "

I think it is valuable to take a step back, and examine a more general notion of a \"closure\" -- the so-called \"join operator\".

\n\n

In mathematics, a \"join\" operator is a function on a partially ordered set which returns the smallest object greater than or equal to its arguments. In symbols, join [a,b] = d such that d >= a and d >= b, but there does not exist an e such that d > e >= a or d > e >= b.

\n\n

So the join gives you the smallest thing \"bigger\" than the parts.

\n\n

Now, note that JavaScript scopes are a partially ordered structure. So that there is a sensible notion of a join. In particular, a join of scopes is the smallest scope bigger than the original scopes. That scope is called the closure.

\n\n

So a closure for the variables a, b, c is the smallest scope (in the lattice of scopes for your program!) that brings a, b, and c into scope.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90022", + "creator": "Magne", + "createdAt": 1402327598000, + "text": "

A closure is a block of code which meets three criteria:

\n\n\n\n

(The word \"closure\" actually has an imprecise meaning, and some people don't think that criterion #1 is part of the definition. I think it is.)

\n\n

Closures are a mainstay of functional languages, but they are present in many other languages as well (for example, Java's anonymous inner classes). You can do cool stuff with them: they allow deferred execution and some elegant tricks of style.

\n\n

By: Paul Cantrell, @ http://innig.net/software/ruby/closures-in-ruby

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90023", + "creator": "b_dev", + "createdAt": 1406240453000, + "text": "

Imagine there is a very large park in your town where you see a magician called Mr. Coder starting baseball games in different corners of the park using his magic wand, called JavaScript.

\n\n

Naturally each baseball game has the exact same rules and each game has its own score board.

\n\n

Naturally, the scores of one baseball game are completely separate from the other games.

\n\n

A closure is the special way Mr.Coder keeps the scoring of all his magical baseball games separate.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90024", + "creator": "Mayur Randive", + "createdAt": 1414477328000, + "text": "

Here is a simple real-time scenario. Just read it through, and you will understand how we have used closure here (see how seat number is changing).

\n\n

All other examples explained previously are also very good to understand the concept.

\n\n
function movieBooking(movieName) {\n    var bookedSeatCount = 0;\n    return function(name) {\n        ++bookedSeatCount ;\n        alert( name + \" - \" + movieName + \", Seat - \" + bookedSeatCount )\n    };\n};\n\nvar MI1 = movieBooking(\"Mission Impossible 1 \");\nvar MI2 = movieBooking(\"Mission Impossible 2 \");\n\nMI1(\"Mayur\");\n// alert\n// Mayur - Mission Impossible 1, Seat - 1\n\nMI1(\"Raju\");\n// alert\n// Raju - Mission Impossible 1, Seat - 2\n\nMI2(\"Priyanka\");\n// alert\n// Raja - Mission Impossible 2, Seat - 1\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90026", + "creator": "PsyChip", + "createdAt": 1423603739000, + "text": "

Maybe you should consider an object-oriented structure instead of inner functions. For example:

\n
    var calculate = {\n        number: 0,\n        init: function (num) {\n            this.number = num;\n        },\n        add: function (val) {\n            this.number += val;\n        },\n        rem: function (val) {\n            this.number -= val;\n        }\n    };\n
\n

And read the result from the calculate.number variable, who needs "return" anyway.

\n
//Addition\nFirst think about scope which defines what variable you have to access to (In Javascript);\n\n//there are two kinds of scope\nGlobal Scope which include variable declared outside function or curly brace\n\nlet globalVariable = "foo";\n
\n

One thing to keep in mind is once you've declared a global variable you can use it anywhere in your code even in function;

\n

Local Scope which include variable that are usable only in a specific part of your code:

\n

Function scope is when you declare a variable in a function you can access the variable only within the function

\n
function User(){\n    let name = "foo";\n    alert(name);\n}\nalert(name);//error\n\n//Block scope is when you declare a variable within a block then you can  access that variable only within a block \n{\n    let user = "foo";\n    alert(user);\n}\nalert(user);\n//Uncaught ReferenceError: user is not defined at.....\n\n//A Closure\n\nfunction User(fname){\n    return function(lname){\n        return fname + " " lname;\n    }\n}\nlet names = User("foo");\nalert(names("bar"));\n\n//When you create a function within a function you've created a closure, in our example above since the outer function is returned the inner function got access to outer function's scope\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90025", + "creator": "Shushanth Pallegar", + "createdAt": 1415693011000, + "text": "

In JavaScript closures are awesome and unique, where variables or arguments are available to inner functions, and they will be alive even after the outer function has returned. Closures are used in most of the design patterns in JS

\n
function getFullName(a, b) {\n  return a + b;\n}\n\nfunction makeFullName(fn) {\n\n  return function(firstName) {\n\n    return function(secondName) {\n\n      return fn(firstName, secondName);\n\n    }\n  }\n}\n\nmakeFullName(getFullName)("Stack")("overflow"); // Stackoverflow\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90028", + "creator": "Rafael Eyng", + "createdAt": 1428528472000, + "text": "

(I am not taking the 6-years-old thing into account.)

\n\n

In a language like JavaScript, where you can pass functions as parameters to other functions (languages where functions are first class citizens), you will often find yourself doing something like:

\n\n
var name = 'Rafael';\n\nvar sayName = function() {\n  console.log(name);\n};\n
\n\n

You see, sayName doesn't have the definition for the name variable, but it does use the value of name that was defined outside of sayName (in a parent scope).

\n\n

Let's say you pass sayName as a parameter to another function, that will call sayName as a callback:

\n\n
functionThatTakesACallback(sayName);\n
\n\n

Note that:

\n\n
    \n
  1. sayName will be called from inside functionThatTakesACallback (assume that, since I haven't implemented functionThatTakesACallback in this example).
  2. \n
  3. When sayName is called, it will log the value of the name variable.
  4. \n
  5. functionThatTakesACallback doesn't define a name variable (well, it could, but it wouldn't matter, so assume it doesn't).
  6. \n
\n\n

So we have sayName being called inside functionThatTakesACallback and referring to a name variable that is not defined inside functionThatTakesACallback.

\n\n

What happens then? A ReferenceError: name is not defined?

\n\n

No! The value of name is captured inside a closure. You can think of this closure as context associated to a function, that holds the values that were available where that function was defined.

\n\n

So: Even though name is not in scope where the function sayName will be called (inside functionThatTakesACallback), sayName can access the value for name that is captured in the closure associated with sayName.

\n\n

--

\n\n

From the book Eloquent JavaScript:

\n\n
\n

A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees its original environment, not the environment in which the call is made.

\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90027", + "creator": "floribon", + "createdAt": 1423856366000, + "text": "

I do not understand why the answers are so complex here.

\n

Here is a closure:

\n
var a = 42;\n\nfunction b() { return a; }\n
\n

Yes. You probably use that many times a day.

\n
\n
\n

There is no reason to believe closures are a complex design hack to address specific problems. No, closures are just about using a variable that comes from a higher scope from the perspective of where the function was declared (not run).

\n

Now what it allows you to do can be more spectacular, see other answers.

\n
\n", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002a", + "creator": "Dinesh Kanivu", + "createdAt": 1429256475000, + "text": "

I believe in shorter explanations, so see the below image.

\n\n

\"Enter

\n\n

function f1() ..> Light Red Box

\n\n

function f2() ..> Red Small Box

\n\n

Here we have two functions, f1() and f2(). f2() is inner to f1().\nf1() has a variable, var x = 10.

\n\n

When invoking the function f1(), f2() can access the value of var x = 10.

\n\n

Here is the code:

\n\n
function f1() {\n    var x=10;\n\n    function f2() {\n        console.log(x)\n    }\n\n    return f2\n\n}\nf1()\n
\n\n

f1() invoking here:

\n\n

\"Enter

\n", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90029", + "creator": "Andy", + "createdAt": 1429056833000, + "text": "

Here's the most Zen answer I can give:

\n\n

What would you expect this code to do? Tell me in a comment before you run it. I'm curious!

\n\n
function foo() {\n  var i = 1;\n  return function() {\n    console.log(i++);\n  }\n}\n\nvar bar = foo();\nbar();\nbar();\nbar();\n\nvar baz = foo();\nbaz();\nbaz();\nbaz();\n
\n\n

Now open the console in your browser (Ctrl + Shift + I or F12, hopefully) and paste the code in and hit Enter.

\n\n

If this code printed what you expect (JavaScript newbies - ignore the \"undefined\" at the end), then you already have wordless understanding. In words, the variable i is part of the inner function instance's closure.

\n\n

I put it this way because, once I understood that this code is putting instances of foo()'s inner function in bar and baz and then calling them via those variables, nothing else surprised me.

\n\n

But if I'm wrong and the console output surprised you, let me know!

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002c", + "creator": "Mike Robinson", + "createdAt": 1430266892000, + "text": "

Also... Perhaps we should cut your 27-year-old friend a little slack, because the entire concept of \"closures\" really is(!) ... voodoo!

\n\n

By that I mean: (a) you do not, intuitively, expect it ...AND... (b) when someone takes the time to explain it to you, you certainly do not expect it to work!

\n\n

Intuition tells you that \"this must be nonsense... surely it must result in some kind of syntax-error or something!\" How on earth(!) could you, in effect, \"pull a function from 'the middle of' wherever-it's-at,\" such that you could [still!] actually have read/write access to the context of \"wherever-it-was-at?!\"

\n\n

When you finally realize that such a thing is possible, then ... sure ... anyone's after-the-fact reaction would be: \"whoa-a-a-a(!)... kew-el-l-l-l...(!!!)\"

\n\n

But there will be a \"big counter-intuitive hurdle\" to overcome, first. Intuition gives you plenty of utterly-plausible expectations that such a thing would be \"of course, absolutely nonsensical and therefore quite impossible.\"

\n\n

Like I said: \"it's voodoo.\"

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002b", + "creator": "Javier La Banca", + "createdAt": 1430266453000, + "text": "

The easiest use case I can think of to explain JavaScript closures is the Module Pattern. In the Module Pattern you define a function and call it immediately afterwards in what is called an Immediately Invoked Function Expression (IIFE). Everything that you write inside that function has private scope because it's defined inside the closure, thus allowing you to \"simulate\" privacy in JavaScript. Like so:

\n\n
 var Closure = (function () {\n    // This is a closure\n    // Any methods, variables and properties you define here are \"private\"\n    // and can't be accessed from outside the function.\n\n    //This is a private variable\n    var foo = \"\";\n\n    //This is a private method\n    var method = function(){\n\n    }\n})();\n
\n\n

If, on the other hand, you'd like to make one or multiple variables or methods visible outside the closure, you can return them inside an object literal. Like so:

\n\n
var Closure = (function () {\n  // This is a closure\n  // Any methods, variables and properties you define here are \"private\"\n  // and can't be accessed from outside the function.\n\n  //This is a private variable\n  var foo = \"\";\n\n  //This is a private method\n  var method = function(){\n\n  }\n\n  //The method will be accessible from outside the closure\n  return {\n    method: method\n  }\n\n})();\n\nClosure.method();\n
\n\n

Hope it helps.\nRegards,

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002d", + "creator": "enb081", + "createdAt": 1430314573000, + "text": "

A closure is a function within a function that has access to its \"parent\" function's variables and parameters.

\n\n

Example:

\n\n
function showPostCard(Sender, Receiver) {\n\n    var PostCardMessage = \" Happy Spring!!! Love, \";\n\n    function PreparePostCard() {\n        return \"Dear \" + Receiver + PostCardMessage + Sender;\n    }\n\n    return PreparePostCard();\n}\nshowPostCard(\"Granny\", \"Olivia\");\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002e", + "creator": "Tero Tolonen", + "createdAt": 1431362117000, + "text": "
\n

The children will always remember the secrets they have shared with their parents, even after their parents are\n gone. This is what closures are for functions.

\n
\n\n

The secrets for JavaScript functions are the private variables

\n\n
var parent = function() {\n var name = \"Mary\"; // secret\n}\n
\n\n

Every time you call it, local variable \"name\" is created and given name \"Mary\". And every time the function exits the variable is lost and the name is forgotten.

\n\n

As you may guess, because the variables are re-created every time the function is called, and nobody else will know them, there must be a secret place where they are stored. It could be called Chamber of Secrets or stack or local scope but it doesn't really matter. We know they are there, somewhere, hidden in the memory.

\n\n

But, in JavaScript there is this very special thing that functions which are created inside other functions, can also know the local variables of their parents and keep them as long as they live.

\n\n
var parent = function() {\n  var name = \"Mary\";\n  var child = function(childName) {\n    // I can also see that \"name\" is \"Mary\"\n  }\n}\n
\n\n

So, as long as we are in the parent -function, it can create one or more child functions which do share the secret variables from the secret place.

\n\n

But the sad thing is, if the child is also a private variable of its parent function, it would also die when the parent ends, and the secrets would die with them.

\n\n

So to live, the child has to leave before it's too late

\n\n
var parent = function() {\n  var name = \"Mary\";\n  var child = function(childName) {\n    return \"My name is \" + childName  +\", child of \" + name; \n  }\n  return child; // child leaves the parent ->\n}\nvar child = parent(); // < - and here it is outside \n
\n\n

And now, even though Mary is \"no longer running\", the memory of her is not lost and her child will always remember her name and other secrets they shared during their time together.

\n\n

So, if you call the child \"Alice\", she will respond

\n\n
child(\"Alice\") => \"My name is Alice, child of Mary\"\n
\n\n

That's all there is to tell.

\n", + "upvotes": 192, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90030", + "creator": "TastyCode", + "createdAt": 1436757633000, + "text": "

I like Kyle Simpson's definition of a closure:

\n\n
\n

Closure is when a function is able to remember and access its lexical\n scope even when that function is executing outside its lexical scope.

\n
\n\n

Lexical scope is when an inner scope can access its outer scope.

\n\n

Here is a modified example he provides in his book series 'You Don't Know JS: Scopes & Closures'.

\n\n
function foo() {\n  var a = 2;\n\n  function bar() {\n    console.log( a );\n  }\n  return bar;\n}\n\nfunction test() {\n  var bz = foo();\n  bz();\n}\n\n// prints 2. Here function bar referred by var bz is outside \n// its lexical scope but it can still access it\ntest(); \n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9002f", + "creator": "Harry Robbins", + "createdAt": 1432858155000, + "text": "

A closure is something many JavaScript developers use all the time, but we take it for granted. How it works is not that complicated. Understanding how to use it purposefully is complex.

\n\n

At its simplest definition (as other answers have pointed out), a closure is basically a function defined inside another function. And that inner function has access to variables defined in the scope of the outer function. The most common practice that you'll see using closures is defining variables and functions in the global scope, and having access to those variables in the function scope of that function.

\n\n
var x = 1;\nfunction myFN() {\n  alert(x); //1, as opposed to undefined.\n}\n// Or\nfunction a() {\n   var x = 1;\n   function b() {\n       alert(x); //1, as opposed to undefined.\n   }\n   b();\n}\n
\n\n

So what?

\n\n

A closure isn't that special to a JavaScript user until you think about what life would be like without them. In other languages, variables used in a function get cleaned up when that function returns. In the above, x would have been a \"null pointer\", and you'd need to establish a getter and setter and start passing references. Doesn't sound like JavaScript right? Thank the mighty closure.

\n\n

Why should I care?

\n\n

You don't really have to be aware of closures to use them. But as others have also pointed out, they can be leveraged to create faux private variables. Until you get to needing private variables, just use them like you always have.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90032", + "creator": "Dmitry Frank", + "createdAt": 1441182678000, + "text": "

Meet the illustrated explanation: How do JavaScript closures work behind the scenes.

\n\n

The article explains how the scope objects (or LexicalEnvironments) are allocated and used in an intuitive way. Like, for this simple script:

\n\n
\"use strict\";\n\nvar foo = 1;\nvar bar = 2;\n\nfunction myFunc() {\n  //-- Define local-to-function variables\n  var a = 1;\n  var b = 2;\n  var foo = 3;\n}\n\n//-- And then, call it:\nmyFunc();\n
\n\n

When executing the top-level code, we have the following arrangement of scope objects:

\n\n

\"Enter

\n\n

And when myFunc() is called, we have the following scope chain:

\n\n

\"Enter

\n\n

Understanding of how scope objects are created, used and deleted is a key to having a big picture and to understand how do closures work under the hood.

\n\n

See the aforementioned article for all the details.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90031", + "creator": "Mohammed Safeer", + "createdAt": 1437734145000, + "text": "

The following example is a simple illustration of a JavaScript closure.\nThis is the closure function, which returns a function, with access to its local variable x,

\n\n
function outer(x){\n     return function inner(y){\n         return x+y;\n     }\n}\n
\n\n

Invoke the function like this:

\n\n
var add10 = outer(10);\nadd10(20); // The result will be 30\nadd10(40); // The result will be 50\n\nvar add20 = outer(20);\nadd20(20); // The result will be 40\nadd20(40); // The result will be 60\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90033", + "creator": "Eugene Tiurin", + "createdAt": 1446651094000, + "text": "

Closures are simple

\n

You probably shouldn't tell a six-year old about closures, but if you do, you might say that closure gives an ability to gain access to a variable declared in some other function scope.

\n

\"enter

\n

\r\n
\r\n
function getA() {\n  var a = [];\n\n  // this action happens later,\n  // after the function returned\n  // the `a` value\n  setTimeout(function() {\n    a.splice(0, 0, 1, 2, 3, 4, 5);\n  });\n\n  return a;\n}\n\nvar a = getA();\nout('What is `a` length?');\nout('`a` length is ' + a.length);\n\nsetTimeout(function() {\n  out('No wait...');\n  out('`a` length is ' + a.length);\n  out('OK :|')\n});
\r\n
<pre id=\"output\"></pre>\n\n<script>\n  function out(k) {\n    document.getElementById('output').innerHTML += '> ' + k + '\\n';\n  }\n</script>
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90034", + "creator": "Gerard ONeill", + "createdAt": 1446669386000, + "text": "

A closure is a function that has access to information from the environment it was defined in.

\n\n

For some, the information is the value in the environment at the time of creation. For others, the information is the variables in the environment at the time of creation.

\n\n

If the lexical environment that the closure refers to belongs to a function that has exited, then (in the case of a closure referring to the variables in the environment) those lexical variables will continue to exist for reference by the closure.

\n\n

A closure can be thought of a special case of global variables -- with a private copy created just for the function.

\n\n

Or it can be thought of as a method where the environment is a specific instance of an object whose properties are the variables in the environment.

\n\n

The former (closure as environment) similar to the latter where the environment copy is a context variable passed to each function in the former, and the instance variables form a context variable in the latter.

\n\n

So a closure is a way to call a function without having to specify the context explicitly as a parameter or as the object in a method invocation.

\n\n
var closure = createclosure(varForClosure);\nclosure(param1);  // closure has access to whatever createclosure gave it access to,\n                  // including the parameter storing varForClosure.\n
\n\n

vs

\n\n
var contextvar = varForClosure; // use a struct for storing more than one..\ncontextclosure(contextvar, param1);\n
\n\n

vs

\n\n
var contextobj = new contextclass(varForClosure);\ncontextobj->objclosure(param1);\n
\n\n

For maintainable code, I recommend the object oriented way. However for a quick and easy set of tasks (for example creating a callback), a closure can become natural and more clear, especially in the context of lamda or anonymous functions.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90035", + "creator": "Premraj", + "createdAt": 1446725717000, + "text": "
\n

A closure is a function having access to the parent scope, even after the parent function has closed.

\n
\n\n
var add = (function() {\n  var counter = 0;\n  return function() {\n    return counter += 1;\n  }\n})();\n\nadd();\nadd();\nadd();\n// The counter is now 3\n
\n\n

Example explained:

\n\n\n\n

Source

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90036", + "creator": "ejectamenta", + "createdAt": 1446824594000, + "text": "

For a six-year-old ...

\n\n

Do you know what objects are?

\n\n

Objects are things that have properties and do stuff.

\n\n

One of the most important things about closures is that they let you make objects in JavaScript. Objects in JavaScript are just functions and closures that lets JavaScript store the value of the property for the object once it has been created.

\n\n

Objects are very useful and keep everything nice and organised. Different objects can do different jobs and working together objects can do complicated things.

\n\n

It's lucky that JavaScript has closures for making objects, otherwise everything would become a messy nightmare.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90037", + "creator": "NinjaBeetle", + "createdAt": 1448400119000, + "text": "

There once was a caveman

\n\n
function caveman {\n
\n\n

who had a very special rock,

\n\n
var rock = \"diamond\";\n
\n\n

You could not get the rock yourself because it was in the caveman's private cave. Only the caveman knew how to find and get the rock.

\n\n
return {\n    getRock: function() {\n        return rock;\n    }\n};\n}\n
\n\n

Luckily, he was a friendly caveman, and if you were willing to wait for his return, he would gladly get it for you.

\n\n
var friend = caveman();\nvar rock = friend.getRock();\n
\n\n

Pretty smart caveman.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90038", + "creator": "Pao Im", + "createdAt": 1448640709000, + "text": "

Closures in JavaScript are associated with concept of scopes.

\n

Prior to es6, there is no block level scope, there is only function level scope in JS.

\n

That means whenever there is a need for block level scope, we need to wrap it inside a function.

\n

Check this simple and interesting example, how closure solves this issue in ES5

\n

\r\n
\r\n
// let say we can only use a traditional for loop, not the forEach\n\nfor (var i = 0; i < 10; i++) {\n    \n    setTimeout(function() {\n        console.log('without closure the visited index - '+ i)\n    })\n}\n\n// this will print 10 times 'visited index - 10', which is not correct\n\n/**\nExpected output is \n\nvisited index - 0\nvisited index - 1\n.\n.\n.\nvisited index - 9\n\n**/\n\n// we can solve it by using closure concept \n   //by using an IIFE (Immediately Invoked Function Expression)\n\n\n// --- updated code ---\n\nfor (var i = 0; i < 10; i++) {\n    (function (i) {\n      setTimeout(function() {\n        console.log('with closure the visited index - '+ i)\n      })\n    })(i);\n}
\r\n
\r\n
\r\n

\n

NB: this can easily be solved by using es6 let instead of var, as let creates lexical scope.

\n
\n

In simple word, Closure in JS is nothing but accessing function scope.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e90039", + "creator": "devlighted", + "createdAt": 1452977398000, + "text": "

This is how a beginner wrapped one's head around Closures like a function is wrapped inside of a functions body also known as Closures.

\n\n

Definition from the book Speaking JavaScript \"A closure is a function plus the connection to the scope in which the function was created\" -Dr.Axel Rauschmayer

\n\n

So what could that look like? Here is an example

\n\n
function newCounter() {\n  var counter = 0;\n   return function increment() {\n    counter += 1;\n   }\n}\n\nvar counter1 = newCounter();\nvar counter2 = newCounter();\n\ncounter1(); // Number of events: 1\ncounter1(); // Number of events: 2\ncounter2(); // Number of events: 1\ncounter1(); // Number of events: 3\n
\n\n

newCounter closes over increment, counter can be referenced to and accessed by increment.

\n\n

counter1 and counter2 will keep track of their own value.

\n\n

Simple but hopefully a clear perspective of what a closure is around all these great and advanced answers.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003a", + "creator": "soundyogi", + "createdAt": 1454823634000, + "text": "

Functions containing no free variables are called pure functions.

\n

Functions containing one or more free variables are called closures.

\n
var pure = function pure(x){\n  return x \n  // only own environment is used\n}\n\nvar foo = "bar"\n\nvar closure = function closure(){\n  return foo\n  // foo is free variable from the outer environment\n}\n
\n

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003b", + "creator": "Brandon Kent", + "createdAt": 1458061500000, + "text": "

MDN explains it best I think:

\n\n
\n

Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.

\n
\n\n

A closure always has an outer function and an inner function. The inner function is where all the work happens, and the outer function is just the environment that preserves the scope where the inner function was created. In this way, the inner function of a closure 'remembers' the environment/scope in which it was created. The most classic example is a counter function:

\n\n
var closure = function() {\n  var count = 0;\n  return function() {\n    count++;\n    console.log(count);\n  };\n};\n\nvar counter = closure();\n\ncounter() // returns 1\ncounter() // returns 2\ncounter() // returns 3\n
\n\n

In the above code, count is preserved by the outer function (environment function), so that every time you call counter(), the inner function (work function) can increment it.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003c", + "creator": "christian Nguyen", + "createdAt": 1469983917000, + "text": "

Version picture for this answer: [Resolved]

\n\n

Just forget about scope every thing and remember: When a variable needed somewhere, javascript will not destroy it. The variable always point to newest value.

\n\n

Example 1:

\n\n

\"enter

\n\n

Example 2:

\n\n

\"enter

\n\n

Example 3:\n\"enter

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003d", + "creator": "Alexis", + "createdAt": 1471507249000, + "text": "

Closure are not difficult to understand. It depends only from the point of view.

\n\n

I personally like to use them in cases of daily life.

\n\n
function createCar()\n{\n    var rawMaterial = [/* lots of object */];\n    function transformation(rawMaterials)\n    {\n       /* lots of changement here */\n       return transformedMaterial;\n    }\n    var transformedMaterial = transformation(rawMaterial);\n    function assemblage(transformedMaterial)\n    {\n        /*Assemblage of parts*/\n        return car;\n    }\n    return assemblage(transformedMaterial);\n}\n
\n\n

We only need to go through certain steps in particular cases. As for the transformation of materials is only useful when you have the parts.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003f", + "creator": "zak.http", + "createdAt": 1490174298000, + "text": "

A closure is simply when a function have access to its outside scope even after the scope's function has finished executing. \nExample:

\n\n
function multiplier(n) {\n    function multiply(x) {\n          return n*x;\n    }\n    return mutliply;\n}\n\nvar 10xmultiplier = multiplier(10);\nvar x = 10xmultiplier(5); // x= 50\n
\n\n

we can see that even after multiplier has finished executing, the inner function multiply gets still access to the value of x which is 10 in this example.

\n\n

A very common use of closures is currying (the same example above) where we spice our function progressively with parameters instead of supplying all of the arguments at once.

\n\n

We can achieve this because Javascript (in addition to the prototypal OOP) allows as to program in a functional fashion where higher order functions can take other functions as arguments (fisrt class functions).\nfunctional programming in wikipedia

\n\n

I highly recommend you to read this book by Kyle Simpson: 2 one part of the book series is dedicated to closures and it is called scope and closures.\nyou don't know js: free reading on github

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e9003e", + "creator": "poushy", + "createdAt": 1484950442000, + "text": "

My perspective of Closures:

\n\n

Closures can be compared to a book, with a bookmark, on a bookshelf.

\n\n

Suppose you have read a book, and you like some page in the book. You put in a bookmark at that page to track it.

\n\n

Now once you finish reading the book, you do not need the book anymore, except, you want to have access to that page. You could have just cut out the page, but then you would loose the context on the story. So you put the book back in your bookshelf with the bookmark.

\n\n

This is similar to a closure. The book is the outer function, and the page is your inner function, which gets returned, from the outer function. The bookmark is the reference to your page, and the context of the story is the lexical scope, which you need to retain. The bookshelf is the function stack, which cannot be cleaned up of the old books, till you hold onto the page.

\n\n

Code Example:

\n\n
function book() {\n   var pages = [....]; //array of pages in your book\n   var bookMarkedPage = 20; //bookmarked page number\n   function getPage(){\n       return pages[bookMarkedPage];\n   }\n   return getPage;\n}\n\nvar myBook = book(),\n    myPage = myBook.getPage();\n
\n\n

When you run the book() function, you are allocating memory in the stack for the function to run in. But since it returns a function, the memory cannot be released, as the inner function has access to the variables from the context outside it, in this case 'pages' and 'bookMarkedPage'.

\n\n

So effectively calling book() returns a reference to a closure, i.e not only a function, but a reference to the book and it's context, i.e. a reference to the function getPage, state of pages and bookMarkedPage variables.

\n\n

Some points to consider:

\n\n

Point 1:\nThe bookshelf, just like the function stack has limited space, so use it wisely.

\n\n

Point 2:\nThink about the fact, whether you need to hold onto the entire book when you just want to track a single page. You can release part of the memory, by not storing all the pages in the book when the closure is returned.

\n\n

This is my perspective of Closures. Hope it helps, and if anyone thinks that this is not correct, please do let me know, as I am very interested to understand even more about scopes and closures!

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a1082fcc3049e911b8", + "creator": "Robse", + "createdAt": 1617265584000, + "text": "Very clever analogy!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e90040", + "creator": "Shivprasad Koirala", + "createdAt": 1492831140000, + "text": "

This answer is a summary of this youtube video Javascript Closures. So full credits to that video.

\n\n

Closures are nothing but Stateful functions which maintain states of their private variables.

\n\n

Normally when you make a call to a function as shown in the below figure. The variables are created on a stack ( running RAM memory) used and then disallocated.

\n\n

\"enter

\n\n

But now there are situations where we want to maintain this state of the function thats where Javascript closures comes to use. A closure is a function inside function with a return call as shown in the below code.

\n\n

\"enter

\n\n

So the closure code for the counter function above looks something as shown below.Its a function inside function with a return statement.

\n\n
function Counter() {\n           var counter = 0;\n\n           var Increment = function () {\n               counter++;\n               alert(counter);\n           }\n           return {\n               Increment\n           }\n       }\n
\n\n

So now if you make a call the counter will increment in other words the function call maintains states.

\n\n
var x = Counter(); // get the reference of the closure\nx.Increment(); // Displays 1\nx.Increment(); // Display 2 ( Maintains the private variables)\n
\n\n

But now the biggest question whats the use of such stateful function. Stateful functions are building blocks to implement OOP concept like abstraction ,encapsulation and creating self contained modules.

\n\n

So whatever you want encapsulated you can put it as private and things to be exposed to public should be put in return statement. Also these components are self contained isolated objects so they do not pollute global variables.

\n\n

A object which follows OOP principles is self contained , follows abstraction , follows encapsulation and so. With out closures in Javascript this is difficult to implement.

\n\n

\"enter

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1594762, + "uvac": 1594827 + } + }, + { + "_id": "62f321bb082fcc3049e8febb", + "title": "What is the most efficient way to deep clone an object in JavaScript?", + "title-lowercase": "what is the most efficient way to deep clone an object in javascript?", + "creator": "jschrab", + "createdAt": 1222187169000, + "status": "open", + "text": "

What is the most efficient way to clone a JavaScript object? I've seen obj = eval(uneval(o)); being used, but that's non-standard and only supported by Firefox.

I've done things like obj = JSON.parse(JSON.stringify(o)); but question the efficiency.

I've also seen recursive copying functions with various flaws.\n
\nI'm surprised no canonical solution exists.

\n", + "upvotes": 9155, + "upvoterUsernames": [], + "downvotes": 3983, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2611828, + "answers": 62, + "answerItems": [ + { + "_id": "62f321bf082fcc3049e9028e", + "creator": "ConroyP", + "createdAt": 1222187931000, + "text": "

If there wasn't any builtin one, you could try:

\n\n
function clone(obj) {\n    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)\n        return obj;\n\n    if (obj instanceof Date)\n        var temp = new obj.constructor(); //or new Date(obj);\n    else\n        var temp = obj.constructor();\n\n    for (var key in obj) {\n        if (Object.prototype.hasOwnProperty.call(obj, key)) {\n            obj['isActiveClone'] = null;\n            temp[key] = clone(obj[key]);\n            delete obj['isActiveClone'];\n        }\n    }\n    return temp;\n}\n
\n", + "upvotes": 433, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32388082fcc3049e914b8", + "creator": "era s'q", + "createdAt": 1659817183000, + "text": "Can you explain isActiveClone part a bit?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90290", + "creator": "Kamarey", + "createdAt": 1245916386000, + "text": "

Code:

\n\n
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned\nfunction extend(from, to)\n{\n    if (from == null || typeof from != \"object\") return from;\n    if (from.constructor != Object && from.constructor != Array) return from;\n    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||\n        from.constructor == String || from.constructor == Number || from.constructor == Boolean)\n        return new from.constructor(from);\n\n    to = to || new from.constructor();\n\n    for (var name in from)\n    {\n        to[name] = typeof to[name] == \"undefined\" ? extend(from[name], null) : to[name];\n    }\n\n    return to;\n}\n
\n\n

Test:

\n\n
var obj =\n{\n    date: new Date(),\n    func: function(q) { return 1 + q; },\n    num: 123,\n    text: \"asdasd\",\n    array: [1, \"asd\"],\n    regex: new RegExp(/aaa/i),\n    subobj:\n    {\n        num: 234,\n        text: \"asdsaD\"\n    }\n}\n\nvar clone = extend(obj);\n
\n", + "upvotes": 209, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32388082fcc3049e914bc", + "creator": "Gershom Maes", + "createdAt": 1619545325000, + "text": "I don't this handles circular structures", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9028f", + "creator": "Mark Cidade", + "createdAt": 1222188339000, + "text": "
function clone(obj)\n { var clone = {};\n   clone.prototype = obj.prototype;\n   for (property in obj) clone[property] = obj[property];\n   return clone;\n }\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90291", + "creator": "Alan", + "createdAt": 1260571639000, + "text": "

This is what I'm using:

\n\n
function cloneObject(obj) {\n    var clone = {};\n    for(var i in obj) {\n        if(typeof(obj[i])==\"object\" && obj[i] != null)\n            clone[i] = cloneObject(obj[i]);\n        else\n            clone[i] = obj[i];\n    }\n    return clone;\n}\n
\n", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32389082fcc3049e914c0", + "creator": "iMartin", + "createdAt": 1617911880000, + "text": "Trying: var a = {b: 1, c: 3, d: { a: 10, g: 20, h: { today: new Date() }}}; Not working for me. But Object.assign({}, a) did.", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [] + }, + { + "_id": "62f32389082fcc3049e914c2", + "creator": "Gershom Maes", + "createdAt": 1619545357000, + "text": "Worse, try let o = {}; o.o = o; cloneObject(o);", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32389082fcc3049e914c3", + "creator": "Nate Levin", + "createdAt": 1657074381000, + "text": "Note: This will not work with Dates", + "upvotes": 357, + "upvoterUsernames": [], + "downvotes": 357, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90292", + "creator": "Zibri", + "createdAt": 1261839591000, + "text": "
var clone = function() {\n    var newObj = (this instanceof Array) ? [] : {};\n    for (var i in this) {\n        if (this[i] && typeof this[i] == \"object\") {\n            newObj[i] = this[i].clone();\n        }\n        else\n        {\n            newObj[i] = this[i];\n        }\n    }\n    return newObj;\n}; \n\nObject.defineProperty( Object.prototype, \"clone\", {value: clone, enumerable: false});\n
\n", + "upvotes": 96, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90294", + "creator": "Chris Broski", + "createdAt": 1286377739000, + "text": "

Crockford suggests (and I prefer) using this function:

\n\n
function object(o) {\n    function F() {}\n    F.prototype = o;\n    return new F();\n}\n\nvar newObject = object(oldObject);\n
\n\n

It's terse, works as expected and you don't need a library.

\n\n
\n\n

EDIT:

\n\n

This is a polyfill for Object.create, so you also can use this.

\n\n
var newObject = Object.create(oldObject);\n
\n\n

NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty. Because, create create new empty object who inherits oldObject. But it is still useful and practical for cloning objects.

\n\n

For exemple if oldObject.a = 5;

\n\n
newObject.a; // is 5\n
\n\n

but:

\n\n
oldObject.hasOwnProperty(a); // is true\nnewObject.hasOwnProperty(a); // is false\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90293", + "creator": "Dima", + "createdAt": 1272453415000, + "text": "
// obj target object, vals source object\nvar setVals = function (obj, vals) {\n    if (obj && vals) {\n        for (var x in vals) {\n            if (vals.hasOwnProperty(x)) {\n                if (obj[x] && typeof vals[x] === 'object') {\n                    obj[x] = setVals(obj[x], vals[x]);\n                } else {\n                    obj[x] = vals[x];\n                }\n            }\n        }\n    }\n    return obj;\n};\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90295", + "creator": "Page Notes", + "createdAt": 1287288113000, + "text": "

There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

\n\n
function jQueryClone(obj) {\n   return jQuery.extend(true, {}, obj)\n}\n\nfunction JSONClone(obj) {\n   return JSON.parse(JSON.stringify(obj))\n}\n\nvar arrayLikeObj = [[1, \"a\", \"b\"], [2, \"b\", \"a\"]];\narrayLikeObj.names = [\"m\", \"n\", \"o\"];\nvar JSONCopy = JSONClone(arrayLikeObj);\nvar jQueryCopy = jQueryClone(arrayLikeObj);\n\nalert(\"Is arrayLikeObj an array instance?\" + (arrayLikeObj instanceof Array) +\n      \"\\nIs the jQueryClone an array instance? \" + (jQueryCopy instanceof Array) +\n      \"\\nWhat are the arrayLikeObj names? \" + arrayLikeObj.names +\n      \"\\nAnd what are the JSONClone names? \" + JSONCopy.names)\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90296", + "creator": "Sultan Shakir", + "createdAt": 1294128355000, + "text": "

Assuming that you have only properties and not any functions in your object, you can just use:

\n
var newObject = JSON.parse(JSON.stringify(oldObject));\n
\n", + "upvotes": 873, + "upvoterUsernames": [], + "downvotes": 321, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32389082fcc3049e914c9", + "creator": "vsync", + "createdAt": 1603544091000, + "text": "functions and dates as well", + "upvotes": 133, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f32389082fcc3049e914ca", + "creator": "Koushik Shom Choudhury", + "createdAt": 1630996049000, + "text": "Fails for objects with Circular properties", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32389082fcc3049e914cc", + "creator": "Andy Carlson", + "createdAt": 1644294033000, + "text": "or Sets or other non-JSON-serializable properties", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90298", + "creator": "gion_13", + "createdAt": 1301257788000, + "text": "

I think that this is the best solution if you want to generalize your object cloning algorithm.
\nIt can be used with or without jQuery, although I recommend leaving jQuery's extend method out if you want you the cloned object to have the same \"class\" as the original one.

\n\n
function clone(obj){\n    if(typeof(obj) == 'function')//it's a simple function\n        return obj;\n    //of it's not an object (but could be an array...even if in javascript arrays are objects)\n    if(typeof(obj) !=  'object' || obj.constructor.toString().indexOf('Array')!=-1)\n        if(JSON != undefined)//if we have the JSON obj\n            try{\n                return JSON.parse(JSON.stringify(obj));\n            }catch(err){\n                return JSON.parse('\"'+JSON.stringify(obj)+'\"');\n            }\n        else\n            try{\n                return eval(uneval(obj));\n            }catch(err){\n                return eval('\"'+uneval(obj)+'\"');\n            }\n    // I used to rely on jQuery for this, but the \"extend\" function returns\n    //an object similar to the one cloned,\n    //but that was not an instance (instanceof) of the cloned class\n    /*\n    if(jQuery != undefined)//if we use the jQuery plugin\n        return jQuery.extend(true,{},obj);\n    else//we recursivley clone the object\n    */\n    return (function _clone(obj){\n        if(obj == null || typeof(obj) != 'object')\n            return obj;\n        function temp () {};\n        temp.prototype = obj;\n        var F = new temp;\n        for(var key in obj)\n            F[key] = clone(obj[key]);\n        return F;\n    })(obj);            \n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90297", + "creator": "Corban Brook", + "createdAt": 1300389595000, + "text": "

Checkout this benchmark: http://jsben.ch/#/bWfk9

\n\n

In my previous tests where speed was a main concern I found

\n\n
JSON.parse(JSON.stringify(obj))\n
\n\n

to be the slowest way to deep clone an object (it is slower than jQuery.extend with deep flag set true by 10-20%).

\n\n

jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn't copy over undefined properties, etc., but this will also slow you down a little.

\n\n

If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj) loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.

\n\n

Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object.

\n\n

JavaScript trace engines suck at optimizing for..in loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.

\n\n
var clonedObject = {\n  knownProp: obj.knownProp,\n  ..\n}\n
\n\n

Beware using the JSON.parse(JSON.stringify(obj)) method on Date objects - JSON.stringify(new Date()) returns a string representation of the date in ISO format, which JSON.parse() doesn't convert back to a Date object. See this answer for more details.

\n\n

Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.

\n\n

Update for ES6

\n\n

If you are using Javascript ES6 try this native method for cloning or shallow copy.

\n\n
Object.assign({}, obj);\n
\n", + "upvotes": 3577, + "upvoterUsernames": [], + "downvotes": 1151, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9029a", + "creator": "Steve Tomlin", + "createdAt": 1308908498000, + "text": "

This is the fastest method I have created that doesn't use the prototype, so it will maintain hasOwnProperty in the new object.

\n\n

The solution is to iterate the top level properties of the original object, make two copies, delete each property from the original and then reset the original object and return the new copy. It only has to iterate as many times as top level properties. This saves all the if conditions to check if each property is a function, object, string, etc., and doesn't have to iterate each descendant property.

\n\n

The only drawback is that the original object must be supplied with its original created namespace, in order to reset it.

\n\n
copyDeleteAndReset:function(namespace,strObjName){\n    var obj = namespace[strObjName],\n    objNew = {},objOrig = {};\n    for(i in obj){\n        if(obj.hasOwnProperty(i)){\n            objNew[i] = objOrig[i] = obj[i];\n            delete obj[i];\n        }\n    }\n    namespace[strObjName] = objOrig;\n    return objNew;\n}\n\nvar namespace = {};\nnamespace.objOrig = {\n    '0':{\n        innerObj:{a:0,b:1,c:2}\n    }\n}\n\nvar objNew = copyDeleteAndReset(namespace,'objOrig');\nobjNew['0'] = 'NEW VALUE';\n\nconsole.log(objNew['0']) === 'NEW VALUE';\nconsole.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90299", + "creator": "neatonk", + "createdAt": 1301796483000, + "text": "

This isn't generally the most efficient solution, but it does what I need. Simple test cases below...

\n\n
function clone(obj, clones) {\n    // Makes a deep copy of 'obj'. Handles cyclic structures by\n    // tracking cloned obj's in the 'clones' parameter. Functions \n    // are included, but not cloned. Functions members are cloned.\n    var new_obj,\n        already_cloned,\n        t = typeof obj,\n        i = 0,\n        l,\n        pair; \n\n    clones = clones || [];\n\n    if (obj === null) {\n        return obj;\n    }\n\n    if (t === \"object\" || t === \"function\") {\n\n        // check to see if we've already cloned obj\n        for (i = 0, l = clones.length; i < l; i++) {\n            pair = clones[i];\n            if (pair[0] === obj) {\n                already_cloned = pair[1];\n                break;\n            }\n        }\n\n        if (already_cloned) {\n            return already_cloned; \n        } else {\n            if (t === \"object\") { // create new object\n                new_obj = new obj.constructor();\n            } else { // Just use functions as is\n                new_obj = obj;\n            }\n\n            clones.push([obj, new_obj]); // keep track of objects we've cloned\n\n            for (key in obj) { // clone object members\n                if (obj.hasOwnProperty(key)) {\n                    new_obj[key] = clone(obj[key], clones);\n                }\n            }\n        }\n    }\n    return new_obj || obj;\n}\n
\n\n

Cyclic array test...

\n\n
a = []\na.push(\"b\", \"c\", a)\naa = clone(a)\naa === a //=> false\naa[2] === a //=> false\naa[2] === a[2] //=> false\naa[2] === aa //=> true\n
\n\n

Function test...

\n\n
f = new Function\nf.a = a\nff = clone(f)\nff === f //=> true\nff.a === a //=> false\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9029b", + "creator": "Joe", + "createdAt": 1316892491000, + "text": "

I know this is an old post, but I thought this may be of some help to the next person who stumbles along.

\n\n

As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so:

\n\n
var a = function(){\n    return {\n        father:'zacharias'\n    };\n},\nb = a(),\nc = a();\nc.father = 'johndoe';\nalert(b.father);\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9029c", + "creator": "itsadok", + "createdAt": 1323964575000, + "text": "

If you're using it, the Underscore.js library has a clone method.

\n
var newObject = _.clone(oldObject);\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238a082fcc3049e914d4", + "creator": "Diederik", + "createdAt": 1632993335000, + "text": "This does a shallow copy, not a deep copy like OP is looking for.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9029e", + "creator": "user1547016", + "createdAt": 1343079562000, + "text": "

Here is a comprehensive clone() method that can clone any JavaScript object. It handles almost all the cases:

\n\n
function clone(src, deep) {\n\n    var toString = Object.prototype.toString;\n    if (!src && typeof src != \"object\") {\n        // Any non-object (Boolean, String, Number), null, undefined, NaN\n        return src;\n    }\n\n    // Honor native/custom clone methods\n    if (src.clone && toString.call(src.clone) == \"[object Function]\") {\n        return src.clone(deep);\n    }\n\n    // DOM elements\n    if (src.nodeType && toString.call(src.cloneNode) == \"[object Function]\") {\n        return src.cloneNode(deep);\n    }\n\n    // Date\n    if (toString.call(src) == \"[object Date]\") {\n        return new Date(src.getTime());\n    }\n\n    // RegExp\n    if (toString.call(src) == \"[object RegExp]\") {\n        return new RegExp(src);\n    }\n\n    // Function\n    if (toString.call(src) == \"[object Function]\") {\n\n        //Wrap in another method to make sure == is not true;\n        //Note: Huge performance issue due to closures, comment this :)\n        return (function(){\n            src.apply(this, arguments);\n        });\n    }\n\n    var ret, index;\n    //Array\n    if (toString.call(src) == \"[object Array]\") {\n        //[].slice(0) would soft clone\n        ret = src.slice();\n        if (deep) {\n            index = ret.length;\n            while (index--) {\n                ret[index] = clone(ret[index], true);\n            }\n        }\n    }\n    //Object\n    else {\n        ret = src.constructor ? new src.constructor() : {};\n        for (var prop in src) {\n            ret[prop] = deep\n                ? clone(src[prop], true)\n                : src[prop];\n        }\n    }\n    return ret;\n};\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238a082fcc3049e914d7", + "creator": "Danubian Sailor", + "createdAt": 1406887111000, + "text": "It converts primitives into wrapper objects, not a good solution in most cases.", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9029d", + "creator": "Maël Nison", + "createdAt": 1341438266000, + "text": "

Shallow copy one-liner (ECMAScript 5th edition):

\n
var origin = { foo : {} };\nvar copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});\n\nconsole.log(origin, copy);\nconsole.log(origin == copy); // false\nconsole.log(origin.foo == copy.foo); // true\n
\n

And shallow copy one-liner (ECMAScript 6th edition, 2015):

\n
var origin = { foo : {} };\nvar copy = Object.assign({}, origin);\n\nconsole.log(origin, copy);\nconsole.log(origin == copy); // false\nconsole.log(origin.foo == copy.foo); // true\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902a0", + "creator": "Michael Uzquiano", + "createdAt": 1367873128000, + "text": "

I have two good answers depending on whether your objective is to clone a \"plain old JavaScript object\" or not.

\n\n

Let's also assume that your intention is to create a complete clone with no prototype references back to the source object. If you're not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford's pattern).

\n\n

For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply:

\n\n
var clone = JSON.parse(JSON.stringify(obj));\n
\n\n

Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned.

\n\n

Is it efficient? Heck yes. We've tried all kinds of cloning methods and this works best. I'm sure some ninja could conjure up a faster method. But I suspect we're talking about marginal gains.

\n\n

This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time.

\n\n

Now, for non-plain JavaScript objects, there isn't a really simple answer. In fact, there can't be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn't have a standardized way of doing that.

\n\n

The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object.

\n\n

We're written our own, but the best general approach I've seen is covered here:

\n\n

http://davidwalsh.name/javascript-clone

\n\n

This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case.

\n\n

The main idea is that you need to special handle the instantiation of your functions (or prototypal classes, so to speak) on a per-type basis. Here, he's provided a few examples for RegExp and Date.

\n\n

Not only is this code brief, but it's also very readable. It's pretty easy to extend.

\n\n

Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you're going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types.

\n\n

So there you go. Two approaches. Both are efficient in my view.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902a1", + "creator": "opensas", + "createdAt": 1371913439000, + "text": "

Lodash has a nice _.cloneDeep(value) method:

\n\n
var objects = [{ 'a': 1 }, { 'b': 2 }];\n\nvar deep = _.cloneDeep(objects);\nconsole.log(deep[0] === objects[0]);\n// => false\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9029f", + "creator": "pvorb", + "createdAt": 1350498991000, + "text": "

There’s a library (called “clone”), that does this quite well. It provides the most complete recursive cloning/copying of arbitrary objects that I know of. It also supports circular references, which is not covered by the other answers, yet.

\n\n

You can find it on npm, too. It can be used for the browser as well as Node.js.

\n\n

Here is an example on how to use it:

\n\n

Install it with

\n\n
npm install clone\n
\n\n

or package it with Ender.

\n\n
ender build clone [...]\n
\n\n

You can also download the source code manually.

\n\n

Then you can use it in your source code.

\n\n
var clone = require('clone');\n\nvar a = { foo: { bar: 'baz' } };  // inital value of a\nvar b = clone(a);                 // clone a -> b\na.foo.bar = 'foo';                // change a\n\nconsole.log(a);                   // { foo: { bar: 'foo' } }\nconsole.log(b);                   // { foo: { bar: 'baz' } }\n
\n\n

(Disclaimer: I’m the author of the library.)

\n", + "upvotes": 125, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902a4", + "creator": "Robin Whittleton", + "createdAt": 1408914590000, + "text": "

For future reference, the current draft of ECMAScript 6 introduces Object.assign as a way of cloning objects. Example code would be:

\n\n
var obj1 = { a: true, b: 1 };\nvar obj2 = Object.assign(obj1);\nconsole.log(obj2); // { a: true, b: 1 }\n
\n\n

At the time of writing support is limited to Firefox 34 in browsers so it’s not usable in production code just yet (unless you’re writing a Firefox extension of course).

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238a082fcc3049e914de", + "creator": "Oriol", + "createdAt": 1422184253000, + "text": "You probably meant obj2 = Object.assign({}, obj1). Your current code is equivalent to obj2 = obj1.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3238a082fcc3049e914df", + "creator": "Redu", + "createdAt": 1491402694000, + "text": "Object.assign() is not for cloning nested objects.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902a3", + "creator": "Cody", + "createdAt": 1398364546000, + "text": "

I usually use var newObj = JSON.parse( JSON.stringify(oldObje) ); but, here's a more proper way:

\n\n
var o = {};\n\nvar oo = Object.create(o);\n\n(o === oo); // => false\n
\n\n

Watch legacy browsers!

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238a082fcc3049e914e1", + "creator": "user420667", + "createdAt": 1459295675000, + "text": "That's cool and all but suppose o has a property a. Now does oo.hasOwnProperty('a')?", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902a2", + "creator": "Daniel Lorenz", + "createdAt": 1375066225000, + "text": "

There are a lot of answers, but none of them gave the desired effect I needed. I wanted to utilize the power of jQuery's deep copy... However, when it runs into an array, it simply copies the reference to the array and deep copies the items in it. To get around this, I made a nice little recursive function that will create a new array automatically.

\n\n

(It even checks for kendo.data.ObservableArray if you want it to! Though, make sure you make sure you call kendo.observable(newItem) if you want the Arrays to be observable again.)

\n\n

So, to fully copy an existing item, you just do this:

\n\n
var newItem = jQuery.extend(true, {}, oldItem);\ncreateNewArrays(newItem);\n\n\nfunction createNewArrays(obj) {\n    for (var prop in obj) {\n        if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {\n            var copy = [];\n            $.each(obj[prop], function (i, item) {\n                var newChild = $.extend(true, {}, item);\n                createNewArrays(newChild);\n                copy.push(newChild);\n            });\n            obj[prop] = copy;\n        }\n    }\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902a5", + "creator": "Steven Vachon", + "createdAt": 1434682604000, + "text": "

Use Object.create() to get the prototype and support for instanceof, and use a for() loop to get enumerable keys:

\n\n
function cloneObject(source) {\n    var key,value;\n    var clone = Object.create(source);\n\n    for (key in source) {\n        if (source.hasOwnProperty(key) === true) {\n            value = source[key];\n\n            if (value!==null && typeof value===\"object\") {\n                clone[key] = cloneObject(value);\n            } else {\n                clone[key] = value;\n            }\n        }\n    }\n\n    return clone;\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914e5", + "creator": "Steven Vachon", + "createdAt": 1460393275000, + "text": "Interesting. Though, using getPrototypeOf on an Array turns its indexes into keys of a new Object.", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902a6", + "creator": "Tristian", + "createdAt": 1438717126000, + "text": "

Requires new-ish browsers, but...

\n\n

Let's extend the native Object and get a real .extend();

\n\n
Object.defineProperty(Object.prototype, 'extend', {\n    enumerable: false,\n    value: function(){\n        var that = this;\n\n        Array.prototype.slice.call(arguments).map(function(source){\n            var props = Object.getOwnPropertyNames(source),\n                i = 0, l = props.length,\n                prop;\n\n            for(; i < l; ++i){\n                prop = props[i];\n\n                if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){\n                    that[prop] = that[prop].extend(source[prop]);\n                }else{\n                    Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));\n                }\n            }\n        });\n\n        return this;\n    }\n});\n
\n\n

Just pop that in prior to any code that uses .extend() on an object.

\n\n

Example:

\n\n
var obj1 = {\n    node1: '1',\n    node2: '2',\n    node3: 3\n};\n\nvar obj2 = {\n    node1: '4',\n    node2: 5,\n    node3: '6'\n};\n\nvar obj3 = ({}).extend(obj1, obj2);\n\nconsole.log(obj3);\n// Object {node1: \"4\", node2: 5, node3: \"6\"}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914e7", + "creator": "Josh from Qaribou", + "createdAt": 1486391020000, + "text": "Mutating prototypes is generally considered bad practice, with the only exception being for shims.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902a7", + "creator": "nathan rogers", + "createdAt": 1440172277000, + "text": "

The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use.

\n\n
var objToCreate = JSON.parse(JSON.stringify(cloneThis));\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902a8", + "creator": "andrew", + "createdAt": 1445486741000, + "text": "

Only when you can use ECMAScript 6 or transpilers.

\n

Features:

\n\n

Code:

\n
function clone(target, source){\n\n    for(let key in source){\n\n        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.\n        let descriptor = Object.getOwnPropertyDescriptor(source, key);\n        if(descriptor.value instanceof String){\n            target[key] = new String(descriptor.value);\n        }\n        else if(descriptor.value instanceof Array){\n            target[key] = clone([], descriptor.value);\n        }\n        else if(descriptor.value instanceof Object){\n            let prototype = Reflect.getPrototypeOf(descriptor.value);\n            let cloneObject = clone({}, descriptor.value);\n            Reflect.setPrototypeOf(cloneObject, prototype);\n            target[key] = cloneObject;\n        }\n        else {\n            Object.defineProperty(target, key, descriptor);\n        }\n    }\n    let prototype = Reflect.getPrototypeOf(source);\n    Reflect.setPrototypeOf(target, prototype);\n    return target;\n}\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914eb", + "creator": "Zortext", + "createdAt": 1634290281000, + "text": "Problematic for data types like Date", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902aa", + "creator": "Eugene Tiurin", + "createdAt": 1450164398000, + "text": "

The efficient way to clone(not deep-clone) an object in one line of code

\n

An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.

\n
var clone = Object.assign({}, obj);\n
\n
\n

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

\n
\n

Read more...

\n

The polyfill to support older browsers:

\n
if (!Object.assign) {\n  Object.defineProperty(Object, 'assign', {\n    enumerable: false,\n    configurable: true,\n    writable: true,\n    value: function(target) {\n      'use strict';\n      if (target === undefined || target === null) {\n        throw new TypeError('Cannot convert first argument to object');\n      }\n\n      var to = Object(target);\n      for (var i = 1; i < arguments.length; i++) {\n        var nextSource = arguments[i];\n        if (nextSource === undefined || nextSource === null) {\n          continue;\n        }\n        nextSource = Object(nextSource);\n\n        var keysArray = Object.keys(nextSource);\n        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {\n          var nextKey = keysArray[nextIndex];\n          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);\n          if (desc !== undefined && desc.enumerable) {\n            to[nextKey] = nextSource[nextKey];\n          }\n        }\n      }\n      return to;\n    }\n  });\n}\n
\n", + "upvotes": 277, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914ee", + "creator": "mwhite", + "createdAt": 1457467013000, + "text": "This doesn't recursively copy so doesn't really offer a solution to the problem of cloning an object.", + "upvotes": 176, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [] + }, + { + "_id": "62f3238b082fcc3049e914f0", + "creator": "Meirion Hughes", + "createdAt": 1465387683000, + "text": "@mwhite there is a difference between clone and deep-clone. This answer does in fact clone, but it doesn't deep-clone.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f3238b082fcc3049e914f2", + "creator": "johannes_lalala", + "createdAt": 1614822721000, + "text": "the question was about recursive copies. Object.assign, as well as the given custom assign, do not copy recursively", + "upvotes": 3640, + "upvoterUsernames": [], + "downvotes": 3640, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902a9", + "creator": "Buzinas", + "createdAt": 1446134995000, + "text": "

For the people who want to use the JSON.parse(JSON.stringify(obj)) version, but without losing the Date objects, you can use the second argument of parse method to convert the strings back to Date:

\n

\r\n
\r\n
function clone(obj) {\n  var regExp = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/;\n  return JSON.parse(JSON.stringify(obj), function(k, v) {\n    if (typeof v === 'string' && regExp.test(v))\n      return new Date(v)\n    return v;\n  })\n}\n\n// usage:\nvar original = {\n a: [1, null, undefined, 0, {a:null}, new Date()],\n b: {\n   c(){ return 0 }\n }\n}\n\nvar cloned = clone(original)\n\nconsole.log(cloned)
\r\n
\r\n
\r\n

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914f4", + "creator": "vsync", + "createdAt": 1603544876000, + "text": "Not quite a 100% clone", + "upvotes": 6148, + "upvoterUsernames": [], + "downvotes": 6148, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ac", + "creator": "Barry Staes", + "createdAt": 1458733041000, + "text": "

Cloning an object using today's JavaScript: ECMAScript 2015 (formerly known as ECMAScript 6)

\n
var original = {a: 1};\n\n// Method 1: New object with original assigned.\nvar copy1 = Object.assign({}, original);\n\n// Method 2: New object with spread operator assignment.\nvar copy2 = {...original};\n
\n

Old browsers may not support ECMAScript 2015. A common solution is to use a JavaScript-to-JavaScript compiler like Babel to output an ECMAScript 5 version of your JavaScript code.

\n

As pointed out by @jim-hall, this is only a shallow copy. Properties of properties are copied as a reference: changing one would change the value in the other object/instance.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914f6", + "creator": "basickarl", + "createdAt": 1492768784000, + "text": "Wow, this answer is so wrong. Both your methods do a shallow copy of one level. Anyone looking at this answer, move on.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ab", + "creator": "Bodhi Hu", + "createdAt": 1451640915000, + "text": "

As recursion is just too expensive for JavaScript, and most answers I have found are using recursion, while JSON approach will skip the non-JSON-convertible parts (Function, etc.). So I did a little research and found this trampoline technique to avoid it. Here's the code:

\n
/*\n * Trampoline to avoid recursion in JavaScript, see:\n *     https://www.integralist.co.uk/posts/functional-recursive-javascript-programming/\n */\nfunction trampoline() {\n    var func = arguments[0];\n    var args = [];\n    for (var i = 1; i < arguments.length; i++) {\n        args[i - 1] = arguments[i];\n    }\n\n    var currentBatch = func.apply(this, args);\n    var nextBatch = [];\n\n    while (currentBatch && currentBatch.length > 0) {\n        currentBatch.forEach(function(eachFunc) {\n            var ret = eachFunc();\n            if (ret && ret.length > 0) {\n                nextBatch = nextBatch.concat(ret);\n            }\n        });\n\n        currentBatch = nextBatch;\n        nextBatch = [];\n    }\n};\n\n/*\n *  Deep clone an object using the trampoline technique.\n *\n *  @param target {Object} Object to clone\n *  @return {Object} Cloned object.\n */\nfunction clone(target) {\n    if (typeof target !== 'object') {\n        return target;\n    }\n    if (target == null || Object.keys(target).length == 0) {\n        return target;\n    }\n\n    function _clone(b, a) {\n        var nextBatch = [];\n        for (var key in b) {\n            if (typeof b[key] === 'object' && b[key] !== null) {\n                if (b[key] instanceof Array) {\n                    a[key] = [];\n                }\n                else {\n                    a[key] = {};\n                }\n                nextBatch.push(_clone.bind(null, b[key], a[key]));\n            }\n            else {\n                a[key] = b[key];\n            }\n        }\n        return nextBatch;\n    };\n\n    var ret = target instanceof Array ? [] : {};\n    (trampoline.bind(null, _clone))(target, ret);\n    return ret;\n};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914f9", + "creator": "rich remer", + "createdAt": 1455340765000, + "text": "Tail call recursion is actually very efficient in most implementations of JavaScript, and is required to be optimized in ES6.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3238b082fcc3049e914fb", + "creator": "Yichong", + "createdAt": 1484097365000, + "text": "Stack would easily overflow, probably because of the circular reference.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ae", + "creator": "Shishir Arora", + "createdAt": 1468775441000, + "text": "

Single-line ECMAScript 6 solution (special object types like Date/Regex not handled):

\n\n

\r\n
\r\n
const clone = (o) =>\r\n  typeof o === 'object' && o !== null ?      // only clone objects\r\n  (Array.isArray(o) ?                        // if cloning an array\r\n    o.map(e => clone(e)) :                   // clone each of its elements\r\n    Object.keys(o).reduce(                   // otherwise reduce every key in the object\r\n      (r, k) => (r[k] = clone(o[k]), r), {}  // and save its cloned value into a new object\r\n    )\r\n  ) :\r\n  o;                                         // return non-objects as is\r\n\r\nvar x = {\r\n  nested: {\r\n    name: 'test'\r\n  }\r\n};\r\n\r\nvar y = clone(x);\r\n\r\nconsole.log(x.nested !== y.nested);
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ad", + "creator": "Dan Atkinson", + "createdAt": 1463177795000, + "text": "

Just because I didn't see AngularJS mentioned and thought that people might want to know...

\n\n

angular.copy also provides a method of deep copying objects and arrays.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238b082fcc3049e914ff", + "creator": "Galvani", + "createdAt": 1474448862000, + "text": "or it might be used the same way as jQiery extend: angular.extend({},obj);", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902af", + "creator": "user3071643", + "createdAt": 1470423260000, + "text": "

I use the npm clone library. Apparently it also works in the browser.

\n\n

https://www.npmjs.com/package/clone

\n\n
let a = clone(b)\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b0", + "creator": "azerafati", + "createdAt": 1473859575000, + "text": "

AngularJS

\n\n

Well if you're using angular you could do this too

\n\n
var newObject = angular.copy(oldObject);\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b2", + "creator": "Ashutosh Jha", + "createdAt": 1479737122000, + "text": "

For future reference, one can use this code

\n\n

ES6:

\n\n
_clone: function(obj){\n    let newObj = {};\n    for(let i in obj){\n        if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){\n            newObj[i] = clone(obj[i]);\n        } else{\n            newObj[i] = obj[i];\n        }\n    }\n    return Object.assign({},newObj);\n}\n
\n\n

ES5:

\n\n
function clone(obj){\nlet newObj = {};\nfor(let i in obj){\n    if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){\n        newObj[i] = clone(obj[i]);\n    } else{\n        newObj[i] = obj[i];\n    }\n}\nreturn Object.assign({},newObj);\n
\n\n

}

\n\n

E.g

\n\n
var obj ={a:{b:1,c:3},d:4,e:{f:6}}\nvar xc = clone(obj);\nconsole.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}\nconsole.log(xc); //{a:{b:1,c:3},d:4,e:{f:6}}\n\nxc.a.b = 90;\nconsole.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}\nconsole.log(xc); //{a:{b:90,c:3},d:4,e:{f:6}}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238c082fcc3049e91504", + "creator": "Soldeplata Saketos", + "createdAt": 1509350558000, + "text": "this does not handle arrays, that are, indeed, objects as well.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902b1", + "creator": "SAlidadi", + "createdAt": 1477809627000, + "text": "

This is a solution with recursion:

\n\n

\r\n
\r\n
obj = {\r\n  a: { b: { c: { d: ['1', '2'] } } },\r\n  e: 'Saeid'\r\n}\r\nconst Clone = function (obj) {\r\n  \r\n  const container = Array.isArray(obj) ? [] : {}\r\n  const keys  = Object.keys(obj)\r\n   \r\n  for (let i = 0; i < keys.length; i++) {\r\n    const key = keys[i]\r\n    if(typeof obj[key] == 'object') {\r\n      container[key] = Clone(obj[key])\r\n    }\r\n    else\r\n      container[key] = obj[key].slice()\r\n  }\r\n  \r\n  return container\r\n}\r\n console.log(Clone(obj))
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238c082fcc3049e91507", + "creator": "vsync", + "createdAt": 1603545540000, + "text": "fails completely with a simple test of [1,2]", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902b4", + "creator": "Alireza", + "createdAt": 1491233877000, + "text": "

Cloning an object was always a concern in JS, but it was all about before ES6, I list different ways of copying an object in JavaScript below, imagine you have the Object below and would like to have a deep copy of that:

\n
var obj = {a:1, b:2, c:3, d:4};\n
\n

There are few ways to copy this object, without changing the origin:

\n
    \n
  1. ES5+, Using a simple function to do the copy for you:

    \n
    function deepCopyObj(obj) {\n    if (null == obj || "object" != typeof obj) return obj;\n    if (obj instanceof Date) {\n        var copy = new Date();\n        copy.setTime(obj.getTime());\n        return copy;\n    }\n    if (obj instanceof Array) {\n        var copy = [];\n        for (var i = 0, len = obj.length; i < len; i++) {\n            copy[i] = deepCopyObj(obj[i]);\n        }\n        return copy;\n    }\n    if (obj instanceof Object) {\n        var copy = {};\n        for (var attr in obj) {\n            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]);\n        }\n        return copy;\n    }\n    throw new Error("Unable to copy obj this object.");\n}\n
    \n
  2. \n
  3. ES5+, using JSON.parse and JSON.stringify.

    \n
    var deepCopyObj = JSON.parse(JSON.stringify(obj));\n
    \n
  4. \n
  5. Angular:

    \n
    var deepCopyObj = angular.copy(obj);\n
    \n
  6. \n
  7. jQuery:

    \n
    var deepCopyObj = jQuery.extend(true, {}, obj);\n
    \n
  8. \n
  9. Underscore.js & Lodash:

    \n
    var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy\n
    \n
  10. \n
\n

Hope these help…

\n", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b3", + "creator": "Ihor Pavlyk", + "createdAt": 1479986113000, + "text": "

\r\n
\r\n
class Handler {\r\n  static deepCopy (obj) {\r\n    if (Object.prototype.toString.call(obj) === '[object Array]') {\r\n      const result = [];\r\n      \r\n      for (let i = 0, len = obj.length; i < len; i++) {\r\n        result[i] = Handler.deepCopy(obj[i]);\r\n      }\r\n      return result;\r\n    } else if (Object.prototype.toString.call(obj) === '[object Object]') {\r\n      const result = {};\r\n      for (let prop in obj) {\r\n        result[prop] = Handler.deepCopy(obj[prop]);\r\n      }\r\n      return result;\r\n    }\r\n    return obj;\r\n  }\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b5", + "creator": "Redu", + "createdAt": 1491404799000, + "text": "

Without touching the prototypical inheritance you may deep lone objects and arrays as follows;

\n\n

\r\n
\r\n
function objectClone(o){\r\n  var ot = Array.isArray(o);\r\n  return o !== null && typeof o === \"object\" ? Object.keys(o)\r\n                                                     .reduce((r,k) => o[k] !== null && typeof o[k] === \"object\" ? (r[k] = objectClone(o[k]),r)\r\n                                                                                                                : (r[k] = o[k],r), ot ? [] : {})\r\n                                             : o;\r\n}\r\nvar obj = {a: 1, b: {c: 2, d: {e: 3, f: {g: 4, h: null}}}},\r\n    arr = [1,2,[3,4,[5,6,[7]]]],\r\n    nil = null,\r\n  clobj = objectClone(obj),\r\n  clarr = objectClone(arr),\r\n  clnil = objectClone(nil);\r\nconsole.log(clobj, obj === clobj);\r\nconsole.log(clarr, arr === clarr);\r\nconsole.log(clnil, nil === clnil);\r\nclarr[2][2][2] = \"seven\";\r\nconsole.log(arr, clarr);
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b7", + "creator": "prograhammer", + "createdAt": 1497767677000, + "text": "

I disagree with the answer with the greatest votes here. A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.

\n\n\n\n

And here's the function for quick reference:

\n\n
function cloneDeep (o) {\n  let newO\n  let i\n\n  if (typeof o !== 'object') return o\n\n  if (!o) return o\n\n  if (Object.prototype.toString.apply(o) === '[object Array]') {\n    newO = []\n    for (i = 0; i < o.length; i += 1) {\n      newO[i] = cloneDeep(o[i])\n    }\n    return newO\n  }\n\n  newO = {}\n  for (i in o) {\n    if (o.hasOwnProperty(i)) {\n      newO[i] = cloneDeep(o[i])\n    }\n  }\n  return newO\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3238c082fcc3049e9150a", + "creator": "Harry", + "createdAt": 1521350390000, + "text": "Crashes on circular references.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902b9", + "creator": "shobhit1", + "createdAt": 1504969738000, + "text": "

There are so many ways to achieve this, but if you want to do this without any library, you can use the following:

\n\n
const cloneObject = (oldObject) => {\n  let newObject = oldObject;\n  if (oldObject && typeof oldObject === 'object') {\n    if(Array.isArray(oldObject)) {\n      newObject = [];\n    } else if (Object.prototype.toString.call(oldObject) === '[object Date]' && !isNaN(oldObject)) {\n      newObject = new Date(oldObject.getTime());\n    } else {\n      newObject = {};\n      for (let i in oldObject) {\n        newObject[i] = cloneObject(oldObject[i]);\n      }\n    }\n\n  }\n  return newObject;\n}\n
\n\n

Let me know what you think.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ba", + "creator": "Mayur Agarwal", + "createdAt": 1507737742000, + "text": "

I am late to answer this question, but I have an another way of cloning the object:

\n
function cloneObject(obj) {\n    if (obj === null || typeof(obj) !== 'object')\n        return obj;\n    var temp = obj.constructor(); // changed\n    for (var key in obj) {\n        if (Object.prototype.hasOwnProperty.call(obj, key)) {\n            obj['isActiveClone'] = null;\n            temp[key] = cloneObject(obj[key]);\n            delete obj['isActiveClone'];\n        }\n    }\n    return temp;\n}\n\nvar b = cloneObject({"a":1,"b":2});   // calling\n
\n

which is much better and faster then:

\n
var a = {"a":1,"b":2};\nvar b = JSON.parse(JSON.stringify(a));  \n
\n

and

\n
var a = {"a":1,"b":2};\n\n// Deep copy\nvar newObject = jQuery.extend(true, {}, a);\n
\n

I have bench-marked the code and you can test the results here:

\n

and sharing the results:\n\"enter\nReferences: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e9150e", + "creator": "Antoniossss", + "createdAt": 1524731454000, + "text": "its funny but when I run your tests it actually shoed me that method 1 is the slowest one", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91510", + "creator": "SPG", + "createdAt": 1543972138000, + "text": "same as me, block 1 is the lowest!", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91512", + "creator": "Phoenix", + "createdAt": 1615569729000, + "text": "Only solution that worked for me! Had to deep clone an object that contained other objects with function properties. Perfect.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91514", + "creator": "Aykut Kllic", + "createdAt": 1618559458000, + "text": "Why do you set obj['isActiveClone'] = null and then delete it? And why don't you call obj.hasOwnProperty(key)?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902bb", + "creator": "Julez", + "createdAt": 1508097687000, + "text": "

Here is my way of deep cloning a object with ES2015 default value and spread operator

\n\n
 const makeDeepCopy = (obj, copy = {}) => {\n  for (let item in obj) {\n    if (typeof obj[item] === 'object') {\n      makeDeepCopy(obj[item], copy)\n    }\n    if (obj.hasOwnProperty(item)) {\n      copy = {\n        ...obj\n      }\n    }\n  }\n  return copy\n}\n
\n\n

\r\n
\r\n
const testObj = {\r\n  \"type\": \"object\",\r\n  \"properties\": {\r\n    \"userId\": {\r\n      \"type\": \"string\",\r\n      \"chance\": \"guid\"\r\n    },\r\n    \"emailAddr\": {\r\n      \"type\": \"string\",\r\n      \"chance\": {\r\n        \"email\": {\r\n          \"domain\": \"fake.com\"\r\n        }\r\n      },\r\n      \"pattern\": \".+@fake.com\"\r\n    }\r\n  },\r\n  \"required\": [\r\n    \"userId\",\r\n    \"emailAddr\"\r\n  ]\r\n}\r\n\r\nconst makeDeepCopy = (obj, copy = {}) => {\r\n  for (let item in obj) {\r\n    if (typeof obj[item] === 'object') {\r\n      makeDeepCopy(obj[item], copy)\r\n    }\r\n    if (obj.hasOwnProperty(item)) {\r\n      copy = {\r\n        ...obj\r\n      }\r\n    }\r\n  }\r\n  return copy\r\n}\r\n\r\nconsole.log(makeDeepCopy(testObj))
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902bc", + "creator": "Константин Ван", + "createdAt": 1516479995000, + "text": "

What about asynchronous object cloning done by a Promise?

\n\n
async function clone(thingy /**/)\n{\n    if(thingy instanceof Promise)\n    {\n        throw Error(\"This function cannot clone Promises.\");\n    }\n    return thingy;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e91518", + "creator": "Константин Ван", + "createdAt": 1566922411000, + "text": "Hold on, 5 upvoters, how does it work? I forgot it myself and this looks counterintuitive, now that one and half years has past.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91519", + "creator": "Sebi", + "createdAt": 1598099269000, + "text": "No idea what it's supposed to do, I'm confused :s", + "upvotes": 2570, + "upvoterUsernames": [], + "downvotes": 2570, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e9151a", + "creator": "Константин Ван", + "createdAt": 1598136073000, + "text": "Does Promise.resolve(value) resolve a cloned value? I doubt it, past myself.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902bd", + "creator": "Steve Griffith", + "createdAt": 1519617916000, + "text": "

Looking through this long list of answers nearly all the solutions have been covered except one that I am aware of. This is the list of VANILLA JS ways of deep cloning an object.

\n\n
    \n
  1. JSON.parse(JSON.stringify( obj ) );

  2. \n
  3. Through history.state with pushState or replaceState

  4. \n
  5. Web Notifications API but this has the downside of asking the user for permissions.

  6. \n
  7. Doing your own recursive loop through the object to copy each level.

  8. \n
  9. The answer I didn't see -> Using ServiceWorkers. The messages (objects) passed back and forth between the page and the ServiceWorker script will be deep clones of any object.

  10. \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902be", + "creator": "codeMonkey", + "createdAt": 1522086168000, + "text": "

ES 2017 example:

\n\n
let objectToCopy = someObj;\nlet copyOfObject = {};\nObject.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy));\n// copyOfObject will now be the same as objectToCopy\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e9151c", + "creator": "Nikita Malyschkin", + "createdAt": 1547706342000, + "text": "This is not a deep copy but a shallow copy. @GurebuBokofu", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902c0", + "creator": "Vikram K", + "createdAt": 1529867318000, + "text": "

For a shallow copy there is a great, simple method introduced in ECMAScript2018 standard. It involves the use of Spread Operator :

\n\n
let obj = {a : \"foo\", b:\"bar\" , c:10 , d:true , e:[1,2,3] };\n\nlet objClone = { ...obj };\n
\n\n

I have tested it in Chrome browser, both objects are stored in different locations, so changing immediate child values in either will not change the other. Though (in the example) changing a value in e will effect both copies.

\n\n

This technique is very simple and straight forward. I consider this a true Best Practice for this question once and for all.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e9151f", + "creator": "Taugenichts", + "createdAt": 1530111550000, + "text": "updating e in objClone will still update e in obj. This is still only a shallow copy. The question explicitly asks for a deep clone.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91521", + "creator": "Taugenichts", + "createdAt": 1530120946000, + "text": "yes, I tested it. run this code: objClone.e[4] = 5; console.log(obj.e); You will see obj.e being updated", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902bf", + "creator": "Parabolord", + "createdAt": 1529445973000, + "text": "

In my experience, a recursive version vastly outperforms JSON.parse(JSON.stringify(obj)). Here is a modernized recursive deep object copy function which can fit on a single line:

\n\n
function deepCopy(obj) {\n  return Object.keys(obj).reduce((v, d) => Object.assign(v, {\n    [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d]\n  }), {});\n}\n
\n\n

This is performing around 40 times faster than the JSON.parse... method.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e91523", + "creator": "zenw0lf", + "createdAt": 1566685855000, + "text": "Too bad it doesn't work right when the value is an array. But, shouldn't be too difficult to modify to get it to work for that case.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e91525", + "creator": "medBouzid", + "createdAt": 1603120084000, + "text": "TypeError: Cannot read property 'constructor' of undefined", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902c2", + "creator": "Tính Ngô Quang", + "createdAt": 1533716251000, + "text": "

Deep copying objects in JavaScript (I think the best and the simplest)

\n\n

1. Using JSON.parse(JSON.stringify(object));

\n\n
var obj = { \n  a: 1,\n  b: { \n    c: 2\n  }\n}\nvar newObj = JSON.parse(JSON.stringify(obj));\nobj.b.c = 20;\nconsole.log(obj); // { a: 1, b: { c: 20 } }\nconsole.log(newObj); // { a: 1, b: { c: 2 } } \n
\n\n

2.Using created method

\n\n
function cloneObject(obj) {\n    var clone = {};\n    for(var i in obj) {\n        if(obj[i] != null &&  typeof(obj[i])==\"object\")\n            clone[i] = cloneObject(obj[i]);\n        else\n            clone[i] = obj[i];\n    }\n    return clone;\n}\n\nvar obj = { \n  a: 1,\n  b: { \n    c: 2\n  }\n}\nvar newObj = cloneObject(obj);\nobj.b.c = 20;\n\nconsole.log(obj); // { a: 1, b: { c: 20 } }\nconsole.log(newObj); // { a: 1, b: { c: 2 } } \n
\n\n

3. Using Lo-Dash's _.cloneDeep link lodash

\n\n
var obj = { \n  a: 1,\n  b: { \n    c: 2\n  }\n}\n\nvar newObj = _.cloneDeep(obj);\nobj.b.c = 20;\nconsole.log(obj); // { a: 1, b: { c: 20 } }\nconsole.log(newObj); // { a: 1, b: { c: 2 } } \n
\n\n

4. Using Object.assign() method

\n\n
var obj = { \n  a: 1,\n  b: 2\n}\n\nvar newObj = _.clone(obj);\nobj.b = 20;\nconsole.log(obj); // { a: 1, b: 20 }\nconsole.log(newObj); // { a: 1, b: 2 }  \n
\n\n

BUT WRONG WHEN

\n\n
var obj = { \n  a: 1,\n  b: { \n    c: 2\n  }\n}\n\nvar newObj = Object.assign({}, obj);\nobj.b.c = 20;\nconsole.log(obj); // { a: 1, b: { c: 20 } }\nconsole.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG\n// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.\n
\n\n

5.Using Underscore.js _.clone link Underscore.js

\n\n
var obj = { \n  a: 1,\n  b: 2\n}\n\nvar newObj = _.clone(obj);\nobj.b = 20;\nconsole.log(obj); // { a: 1, b: 20 }\nconsole.log(newObj); // { a: 1, b: 2 }  \n
\n\n

BUT WRONG WHEN

\n\n
var obj = { \n  a: 1,\n  b: { \n    c: 2\n  }\n}\n\nvar newObj = _.cloneDeep(obj);\nobj.b.c = 20;\nconsole.log(obj); // { a: 1, b: { c: 20 } }\nconsole.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG\n// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)\n
\n\n

JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd\n\"Performance

\n", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323c9082fcc3049e91528", + "creator": "kenanyildiz90", + "createdAt": 1575962458000, + "text": "Hey, your last example is wrong. In my opinion, you must use _clone and not _cloneDeep for the wrong example.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323c9082fcc3049e9152a", + "creator": "Toivo Säwén", + "createdAt": 1576677967000, + "text": "This created method (2.) won't work for arrays, will it?", + "upvotes": 189, + "upvoterUsernames": [], + "downvotes": 189, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902c1", + "creator": "Jinu Joseph Daniel", + "createdAt": 1531727884000, + "text": "

Hope this helps.

\n\n
function deepClone(obj) {\n    /*\n     * Duplicates an object \n     */\n\n    var ret = null;\n    if (obj !== Object(obj)) { // primitive types\n        return obj;\n    }\n    if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { // string objecs\n        ret = obj; // for ex: obj = new String(\"Spidergap\")\n    } else if (obj instanceof Date) { // date\n        ret = new obj.constructor();\n    } else\n        ret = Object.create(obj.constructor.prototype);\n\n    var prop = null;\n    var allProps = Object.getOwnPropertyNames(obj); //gets non enumerables also\n\n\n    var props = {};\n    for (var i in allProps) {\n        prop = allProps[i];\n        props[prop] = false;\n    }\n\n    for (i in obj) {\n        props[i] = i;\n    }\n\n    //now props contain both enums and non enums \n    var propDescriptor = null;\n    var newPropVal = null; // value of the property in new object\n    for (i in props) {\n        prop = obj[i];\n        propDescriptor = Object.getOwnPropertyDescriptor(obj, i);\n\n        if (Array.isArray(prop)) { //not backward compatible\n            prop = prop.slice(); // to copy the array\n        } else\n        if (prop instanceof Date == true) {\n            prop = new prop.constructor();\n        } else\n        if (prop instanceof Object == true) {\n            if (prop instanceof Function == true) { // function\n                if (!Function.prototype.clone) {\n                    Function.prototype.clone = function() {\n                        var that = this;\n                        var temp = function tmp() {\n                            return that.apply(this, arguments);\n                        };\n                        for (var ky in this) {\n                            temp[ky] = this[ky];\n                        }\n                        return temp;\n                    }\n                }\n                prop = prop.clone();\n\n            } else // normal object \n            {\n                prop = deepClone(prop);\n            }\n\n        }\n\n        newPropVal = {\n            value: prop\n        };\n        if (propDescriptor) {\n            /*\n             * If property descriptors are there, they must be copied\n             */\n            newPropVal.enumerable = propDescriptor.enumerable;\n            newPropVal.writable = propDescriptor.writable;\n\n        }\n        if (!ret.hasOwnProperty(i)) // when String or other predefined objects\n            Object.defineProperty(ret, i, newPropVal); // non enumerable\n\n    }\n    return ret;\n}\n
\n\n

https://github.com/jinujd/Javascript-Deep-Clone

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c4", + "creator": "shunryu111", + "createdAt": 1539163316000, + "text": "

if you find yourself doing this type of thing regular ( eg- creating undo redo functionality ) it might be worth looking into Immutable.js

\n\n
const map1 = Immutable.fromJS( { a: 1, b: 2, c: { d: 3 } } );\nconst map2 = map1.setIn( [ 'c', 'd' ], 50 );\n\nconsole.log( `${ map1.getIn( [ 'c', 'd' ] ) } vs ${ map2.getIn( [ 'c', 'd' ] ) }` ); // \"3 vs 50\"\n
\n\n

https://codepen.io/anon/pen/OBpqNE?editors=1111

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c3", + "creator": "Prasanth Jaya", + "createdAt": 1535017250000, + "text": "

When your object is nested and it contains data object, other structured object or some property object, etc then using JSON.parse(JSON.stringify(object)) or Object.assign({}, obj) or $.extend(true, {}, obj) will not work. In that case use lodash. It is simple and easy..

\n\n
var obj = {a: 25, b: {a: 1, b: 2}, c: new Date(), d: anotherNestedObject };\nvar A = _.cloneDeep(obj);\n
\n\n

Now A will be your new cloned of obj without any references..

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c6", + "creator": "Mystical", + "createdAt": 1549329116000, + "text": "

How about merging the keys of the object with its values?

\n\n
function deepClone(o) {\n    var keys = Object.keys(o);\n    var values = Object.values(o);\n\n    var clone = {};\n\n    keys.forEach(function(key, i) {\n        clone[key] = typeof values[i] == 'object' ? Object.create(values[i]) : values[i];\n    });\n\n    return clone;\n}\n
\n\n

Note: This method doesn't necessarily make shallow copies, but it only copies with the depth of one inner-object, meaning that when you are given something like {a: {b: {c: null}}}, it will only clone the objects that are directly inside of them, so deepClone(a.b).c is technically a reference to a.b.c, while deepClone(a).b is a clone, not a reference.

\n", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c5", + "creator": "chandan gupta", + "createdAt": 1541410994000, + "text": "
\n

In JavaScript, you can write your deepCopy method like

\n
\n\n
function deepCopy(src) {\n  let target = Array.isArray(src) ? [] : {};\n  for (let prop in src) {\n    let value = src[prop];\n    if(value && typeof value === 'object') {\n      target[prop] = deepCopy(value);\n  } else {\n      target[prop] = value;\n  }\n }\n    return target;\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c7", + "creator": "shakthi nagaraj", + "createdAt": 1549374375000, + "text": "
function clone(obj) {\n    var copy;\n\n    // Handle the 3 simple types, and null or undefined\n    if (null == obj || \"object\" != typeof obj) return obj;\n\n    // Handle Date\n    if (obj instanceof Date) {\n        copy = new Date();\n        copy.setTime(obj.getTime());\n        return copy;\n    }\n\n    // Handle Array\n    if (obj instanceof Array) {\n        copy = [];\n        for (var i = 0, len = obj.length; i < len; i++) {\n            copy[i] = clone(obj[i]);\n        }\n        return copy;\n    }\n\n    // Handle Object\n    if (obj instanceof Object) {\n        copy = {};\n        for (var attr in obj) {\n            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);\n        }\n        return copy;\n    }\n\n    throw new Error(\"Unable to copy obj! Its type isn't supported.\");\n}\n
\n\n

use the following method instead of JSON.parse(JSON.stringify(obj)) because \nit is slower than the following method

\n\n

How do I correctly clone a JavaScript object?

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c8", + "creator": "Shidersz", + "createdAt": 1555392831000, + "text": "

With the proposal of the new method Object.fromEntries() that is supported on newer versions of some browsers (reference). I want to contribute with the next recursive approach:

\n\n

\r\n
\r\n
const obj = {\r\n  key1: {key11: \"key11\", key12: \"key12\", key13: {key131: 22}},\r\n  key2: {key21: \"key21\", key22: \"key22\"},\r\n  key3: \"key3\",\r\n  key4: [1,2,3, {key: \"value\"}]\r\n}\r\n\r\nconst cloneObj = (obj) =>\r\n{\r\n    if (Object(obj) !== obj)\r\n       return obj;\r\n    else if (Array.isArray(obj))\r\n       return obj.map(cloneObj);\r\n\r\n    return Object.fromEntries(Object.entries(obj).map(\r\n        ([k,v]) => ([k, cloneObj(v)])\r\n    ));\r\n}\r\n\r\n// Clone the original object.\r\nlet newObj = cloneObj(obj);\r\n\r\n// Make changes on the original object.\r\nobj.key1.key11 = \"TEST\";\r\nobj.key3 = \"TEST\";\r\nobj.key1.key13.key131 = \"TEST\";\r\nobj.key4[1] = \"TEST\";\r\nobj.key4[3].key = \"TEST\";\r\n\r\n// Display both objects on the console.\r\nconsole.log(\"Original object: \", obj);\r\nconsole.log(\"Cloned object: \", newObj);
\r\n
.as-console {background-color:black !important; color:lime;}\r\n.as-console-wrapper {max-height:100% !important; top:0;}
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902c9", + "creator": "Kamyar", + "createdAt": 1558268559000, + "text": "

My scenario was a bit different. I had an object with nested objects as well as functions. Therefore, Object.assign() and JSON.stringify() were not solutions to my problem. Using third-party libraries was not an option for me neither.

\n\n

Hence, I decided to make a simple function to use built-in methods to copy an object with its literal properties, its nested objects, and functions.

\n\n
let deepCopy = (target, source) => {\n    Object.assign(target, source);\n    // check if there's any nested objects\n    Object.keys(source).forEach((prop) => {\n        /**\n          * assign function copies functions and\n          * literals (int, strings, etc...)\n          * except for objects and arrays, so:\n          */\n        if (typeof(source[prop]) === 'object') {\n            // check if the item is, in fact, an array\n            if (Array.isArray(source[prop])) {\n                // clear the copied referenece of nested array\n                target[prop] = Array();\n                // iterate array's item and copy over\n                source[prop].forEach((item, index) => {\n                    // array's items could be objects too!\n                    if (typeof(item) === 'object') {\n                        // clear the copied referenece of nested objects\n                        target[prop][index] = Object();\n                        // and re do the process for nested objects\n                        deepCopy(target[prop][index], item);\n                    } else {\n                        target[prop].push(item);\n                    }\n                });\n            // otherwise, treat it as an object\n            } else {\n                // clear the copied referenece of nested objects\n                target[prop] = Object();\n                // and re do the process for nested objects\n                deepCopy(target[prop], source[prop]);\n            }\n        }\n    });\n};\n
\n\n

Here's a test code:

\n\n
let a = {\n    name: 'Human', \n    func: () => {\n        console.log('Hi!');\n    }, \n    prop: {\n        age: 21, \n        info: {\n            hasShirt: true, \n            hasHat: false\n        }\n    },\n    mark: [89, 92, { exam: [1, 2, 3] }]\n};\n\nlet b = Object();\n\ndeepCopy(b, a);\n\na.name = 'Alien';\na.func = () => { console.log('Wassup!'); };\na.prop.age = 1024;\na.prop.info.hasShirt = false;\na.mark[0] = 87;\na.mark[1] = 91;\na.mark[2].exam = [4, 5, 6];\n\nconsole.log(a); // updated props\nconsole.log(b);\n
\n\n

For efficiency-related concerns, I believe this is the simplest and most efficient solution to the problem I had. I would appreciate any comments on this algorithm that could make it more efficient.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902cb", + "creator": "Ankur Kedia", + "createdAt": 1564850212000, + "text": "

This is my solution without using any library or native javascript function.

\n\n
function deepClone(obj) {\n  if (typeof obj !== \"object\") {\n    return obj;\n  } else {\n    let newObj =\n      typeof obj === \"object\" && obj.length !== undefined ? [] : {};\n    for (let key in obj) {\n      if (key) {\n        newObj[key] = deepClone(obj[key]);\n      }\n    }\n    return newObj;\n  }\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ca082fcc3049e91535", + "creator": "Ian", + "createdAt": 1566408228000, + "text": "Careful... const o = {}; o.a = o; deepClone(o); -> recursion error.", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ca", + "creator": "KRIPA SHANKAR JHA", + "createdAt": 1559024232000, + "text": "

Object.assign({},sourceObj) only clones the object if their property is not having reference type key.\nex

\n\n
obj={a:\"lol\",b:[\"yes\",\"no\",\"maybe\"]}\nclonedObj = Object.assign({},obj);\n\nclonedObj.b.push(\"skip\")// changes will reflected to the actual obj as well because of its reference type.\nobj.b //will also console => yes,no,maybe,skip\n
\n\n

So for the deep cloning is not possible to achieve in this way.

\n\n

The best solution that works is

\n\n
var obj = Json.stringify(yourSourceObj)\nvar cloned = Json.parse(obj);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ca082fcc3049e91538", + "creator": "vsync", + "createdAt": 1603545325000, + "text": "Far from "best". maybe for simple objects.", + "upvotes": 1140, + "upvoterUsernames": [], + "downvotes": 1140, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902b8", + "creator": "JTeam", + "createdAt": 1502864201000, + "text": "

As this question is having lot of attention and answers with reference to inbuilt features such as Object.assign or custom code to deep clone, i would like to share some libraries to deep clone,

\n\n

1. esclone

\n\n

npm install --savedev esclone https://www.npmjs.com/package/esclone

\n\n

Example use in ES6:

\n\n
import esclone from \"esclone\";\n\nconst rockysGrandFather = {\n  name: \"Rockys grand father\",\n  father: \"Don't know :(\"\n};\nconst rockysFather = {\n  name: \"Rockys Father\",\n  father: rockysGrandFather\n};\n\nconst rocky = {\n  name: \"Rocky\",\n  father: rockysFather\n};\n\nconst rockyClone = esclone(rocky);\n
\n\n

Example use in ES5:

\n\n
var esclone = require(\"esclone\")\nvar foo = new String(\"abcd\")\nvar fooClone = esclone.default(foo)\nconsole.log(fooClone)\nconsole.log(foo === fooClone)\n
\n\n

2. deep copy

\n\n

npm install deep-copy\nhttps://www.npmjs.com/package/deep-copy

\n\n

Example:

\n\n
var dcopy = require('deep-copy')\n\n// deep copy object \nvar copy = dcopy({a: {b: [{c: 5}]}})\n\n// deep copy array \nvar copy = dcopy([1, 2, {a: {b: 5}}])\n
\n\n

3. clone-deep

\n\n

$ npm install --save clone-deep\nhttps://www.npmjs.com/package/clone-deep

\n\n

Example:

\n\n
var cloneDeep = require('clone-deep');\n\nvar obj = {a: 'b'};\nvar arr = [obj];\n\nvar copy = cloneDeep(arr);\nobj.c = 'd';\n\nconsole.log(copy);\n//=> [{a: 'b'}] \n\nconsole.log(arr);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902b6", + "creator": "Daniel Barde", + "createdAt": 1496609393000, + "text": "

Lodash has a function that handles that for you like so.

\n\n
var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}};\n\nvar bar = _.cloneDeep(foo);\n// bar = {a: 'a', b: {c:'d', e: {f: 'g'}}} \n
\n\n

Read the docs here.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bc082fcc3049e92dea", + "creator": "tommyalvarez", + "createdAt": 1499794053000, + "text": "I ended up using this, since the JSON.parse(JSON.stringify(obj)) does not keep the original object prototype", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321be082fcc3049e90242", + "creator": "Navid", + "createdAt": 1562396860000, + "text": "The react community has introduced immutability-helper", + "upvotes": 2847, + "upvoterUsernames": [], + "downvotes": 2847, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2620984, + "uvac": 2621046 + } + }, + { + "_id": "62f321bb082fcc3049e8fec4", + "title": "Which "href" value should I use for JavaScript links, "#" or "javascript:void(0)"?", + "title-lowercase": "which "href" value should i use for javascript links, "#" or "javascript:void(0)"?", + "creator": "2cBGj7vsfp", + "createdAt": 1222365267000, + "status": "open", + "text": "

The following are two methods of building a link that has the sole purpose of running JavaScript code. Which is better, in terms of functionality, page load speed, validation purposes, etc.?

\n\n

\r\n
\r\n
function myJsFunc() {\r\n    alert(\"myJsFunc\");\r\n}
\r\n
<a href=\"#\" onclick=\"myJsFunc();\">Run JavaScript Code</a>
\r\n
\r\n
\r\n

\n\n

or

\n\n

\r\n
\r\n
function myJsFunc() {\r\n    alert(\"myJsFunc\");\r\n}
\r\n
 <a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Run JavaScript Code</a>
\r\n
\r\n
\r\n

\n", + "upvotes": 6749, + "upvoterUsernames": [], + "downvotes": 2412, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2443002, + "answers": 54, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e9040f", + "creator": "Adam Tuttle", + "createdAt": 1222365334000, + "text": "

'#' will take the user back to the top of the page, so I usually go with void(0).

\n\n

javascript:; also behaves like javascript:void(0);

\n", + "upvotes": 532, + "upvoterUsernames": [], + "downvotes": 176, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32426082fcc3049e91765", + "creator": "Guvante", + "createdAt": 1222377774000, + "text": "The way to avoid that is to return false in the onclick event handler.", + "upvotes": 121, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e91767", + "creator": "Quentin", + "createdAt": 1222416602000, + "text": "Returning false in the event handler doesn't avoid that if JavaScript the JS doesn't run successfully.", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e91768", + "creator": "scunliffe", + "createdAt": 1222483592000, + "text": "using "#someNonExistantAnchorName" works well because it has nowhere to jump to.", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e9176a", + "creator": "user692942", + "createdAt": 1374149182000, + "text": "Any use of # or void(0) is bad practice and shouldn't be encouraged.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e9176c", + "creator": "Neel", + "createdAt": 1397694192000, + "text": "The shebang (#!) does the trick but it's definitely bad practice.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e9176e", + "creator": "Ilya Streltsyn", + "createdAt": 1411377482000, + "text": "@Neel, any anchor that doesn't actually exist in the document (e.g. #_) does the same trick, with the same drawbacks.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90411", + "creator": "nathaniel", + "createdAt": 1222365468000, + "text": "

Don't lose sight of the fact that your URL may be necessary -- onclick is fired before the reference is followed, so sometimes you will need to process something clientside before navigating off the page.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90412", + "creator": "Simon Forrest", + "createdAt": 1222365751000, + "text": "

Unless you're writing out the link using JavaScript (so that you know it's enabled in the browser), you should ideally be providing a proper link for people who are browsing with JavaScript disabled and then prevent the default action of the link in your onclick event handler. This way those with JavaScript enabled will run the function and those with JavaScript disabled will jump to an appropriate page (or location within the same page) rather than just clicking on the link and having nothing happen.

\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90410", + "creator": "Zach", + "createdAt": 1222365413000, + "text": "

The first one, ideally with a real link to follow in case the user has JavaScript disabled. Just make sure to return false to prevent the click event from firing if the JavaScript executes.

\n\n
<a href=\"#\" onclick=\"myJsFunc(); return false;\">Link</a>\n
\n\n

If you use Angular2, this way works:

\n\n

<a [routerLink]=\"\" (click)=\"passTheSalt()\">Click me</a>.

\n\n

See here https://stackoverflow.com/a/45465728/2803344

\n", + "upvotes": 291, + "upvoterUsernames": [], + "downvotes": 142, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90413", + "creator": "Aaron Wagner", + "createdAt": 1222366162000, + "text": "

Neither.

\n\n

If you can have an actual URL that makes sense use that as the HREF. The onclick won't fire if someone middle-clicks on your link to open a new tab or if they have JavaScript disabled.

\n\n

If that is not possible, then you should at least inject the anchor tag into the document with JavaScript and the appropriate click event handlers.

\n\n

I realize this isn't always possible, but in my opinion it should be striven for in developing any public website.

\n\n

Check out Unobtrusive JavaScript and Progressive enhancement (both Wikipedia).

\n", + "upvotes": 2039, + "upvoterUsernames": [], + "downvotes": 643, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32426082fcc3049e91774", + "creator": "Sebastian Simon", + "createdAt": 1622249562000, + "text": "“If that is not possible, then” use a button instead. That’s how this answer should’ve ended.", + "upvotes": 69, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e91775", + "creator": "mohamed tebry", + "createdAt": 1644555455000, + "text": "and if we use a button, how would our lovely users will open their href in new tab using their shiny smartphones?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90414", + "creator": "Steve Paulo", + "createdAt": 1222367742000, + "text": "

Ideally you'd do this:

\n\n
<a href=\"javascriptlessDestination.html\" onclick=\"myJSFunc(); return false;\">Link text</a>\n
\n\n

Or, even better, you'd have the default action link in the HTML, and you'd add the onclick event to the element unobtrusively via JavaScript after the DOM renders, thus ensuring that if JavaScript is not present/utilized you don't have useless event handlers riddling your code and potentially obfuscating (or at least distracting from) your actual content.

\n", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90415", + "creator": "mmacaulay", + "createdAt": 1222368014000, + "text": "

It's nice to have your site be accessible by users with JavaScript disabled, in which case the href points to a page that performs the same action as the JavaScript being executed. Otherwise I use \"#\" with a \"return false;\" to prevent the default action (scroll to top of the page) as others have mentioned.

\n\n

Googling for \"javascript:void(0)\" provides a lot of information on this topic. Some of them, like this one mention reasons to NOT use void(0).

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32426082fcc3049e91779", + "creator": "AnthonyWJones", + "createdAt": 1222413627000, + "text": "The blog entry does not cite the reference as to why javascript:void(0) should be avoided.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e9177b", + "creator": "Peter Mortensen", + "createdAt": 1306829183000, + "text": "The link is (effectively) broken now.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32426082fcc3049e9177c", + "creator": "Scott Schupbach", + "createdAt": 1586285989000, + "text": "javascript:void(0); is also not good if you want to use a strict Content Security Policy that disables inline JavaScript.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90416", + "creator": "Peter", + "createdAt": 1222368638000, + "text": "

Ideally you should have a real URL as fallback for non-JavaScript users.

\n\n

If this doesn't make sense, use # as the href attribute. I don't like using the onclick attribute since it embeds JavaScript directly in the HTML. A better idea would be to use an external JS file and then add the event handler to that link. You can then prevent the default event so that the URL doesn't change to append the # after the user clicks it.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90417", + "creator": "fijter", + "createdAt": 1222369187000, + "text": "

Neither if you ask me;

\n\n

If your \"link\" has the sole purpose of running some JavaScript code it doesn't qualify as a link; rather a piece of text with a JavaScript function coupled to it. I would recommend to use a <span> tag with an onclick handler attached to it and some basic CSS to immitate a link. Links are made for navigation, and if your JavaScript code isn't for navigation it should not be an <a> tag.

\n\n

Example:

\n\n

\r\n
\r\n
function callFunction() { console.log(\"function called\"); }
\r\n
.jsAction {\r\n    cursor: pointer;\r\n    color: #00f;\r\n    text-decoration: underline;\r\n}
\r\n
<p>I want to call a JavaScript function <span class=\"jsAction\" onclick=\"callFunction();\">here</span>.</p>
\r\n
\r\n
\r\n

\n", + "upvotes": 193, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32427082fcc3049e9177f", + "creator": "Hosam Aly", + "createdAt": 1235848862000, + "text": "Hardcoding colors in your CSS would prevent the browser from using custom colors the user may define, which can be a problem with accessibility.", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32427082fcc3049e91781", + "creator": "redShadow", + "createdAt": 1323904558000, + "text": "<span>s are not meant to do anything. <A>nchors and <buttons> are used for that!", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32427082fcc3049e91783", + "creator": "apnerve", + "createdAt": 1356683586000, + "text": "Using buttons is a better choice here while using a span is not.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90419", + "creator": "CleverPatrick", + "createdAt": 1222377635000, + "text": "

I believe you are presenting a false dichotomy. These are not the only two options.

\n\n

I agree with Mr. D4V360 who suggested that, even though you are using the anchor tag, you do not truly have an anchor here. All you have is a special section of a document that should behave slightly different. A <span> tag is far more appropriate.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32427082fcc3049e91786", + "creator": "AndFisher", + "createdAt": 1484750553000, + "text": "Also if you were to replace an a with a span, you'll need to remember to make it focusable via keyboard.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90418", + "creator": "Will Read", + "createdAt": 1222376558000, + "text": "

Depending on what you want to accomplish, you could forget the onclick and just use the href:

\n\n
<a href=\"javascript:myJsFunc()\">Link Text</a>\n
\n\n

It gets around the need to return false. I don't like the # option because, as mentioned, it will take the user to the top of the page. If you have somewhere else to send the user if they don't have JavaScript enabled (which is rare where I work, but a very good idea), then Steve's proposed method works great.

\n\n
<a href=\"javascriptlessDestination.html\" onclick=\"myJSFunc(); return false;\">Link text</a>\n
\n\n

Lastly, you can use javascript:void(0) if you do not want anyone to go anywhere and if you don't want to call a JavaScript function. It works great if you have an image you want a mouseover event to happen with, but there's not anything for the user to click on.

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9041b", + "creator": "AnthonyWJones", + "createdAt": 1222416219000, + "text": "

I use javascript:void(0).

\n\n

Three reasons. Encouraging the use of # amongst a team of developers inevitably leads to some using the return value of the function called like this:

\n\n
function doSomething() {\n    //Some code\n    return false;\n}\n
\n\n

But then they forget to use return doSomething() in the onclick and just use doSomething().

\n\n

A second reason for avoiding # is that the final return false; will not execute if the called function throws an error. Hence the developers have to also remember to handle any error appropriately in the called function.

\n\n

A third reason is that there are cases where the onclick event property is assigned dynamically. I prefer to be able to call a function or assign it dynamically without having to code the function specifically for one method of attachment or another. Hence my onclick (or on anything) in HTML markup look like this:

\n\n
onclick=\"someFunc.call(this)\"\n
\n\n

OR

\n\n
onclick=\"someFunc.apply(this, arguments)\"\n
\n\n

Using javascript:void(0) avoids all of the above headaches, and I haven't found any examples of a downside.

\n\n

So if you're a lone developer then you can clearly make your own choice, but if you work as a team you have to either state:

\n\n

Use href=\"#\", make sure onclick always contains return false; at the end, that any called function does not throw an error and if you attach a function dynamically to the onclick property make sure that as well as not throwing an error it returns false.

\n\n

OR

\n\n

Use href=\"javascript:void(0)\"

\n\n

The second is clearly much easier to communicate.

\n", + "upvotes": 3574, + "upvoterUsernames": [], + "downvotes": 1305, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32427082fcc3049e9178a", + "creator": "Nathan", + "createdAt": 1622584877000, + "text": "@s3c The enter keypress already triggers the click event, so is that last listener necessary?", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [] + }, + { + "_id": "62f32427082fcc3049e9178c", + "creator": "RobG", + "createdAt": 1655020192000, + "text": "@SebastianSimon—I was going to say that… but you did it for me. :-)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90421", + "creator": "Matt Goddard", + "createdAt": 1224771753000, + "text": "

Just to pick up the point some of the other have mentioned.

\n\n

It's much better to bind the event 'onload'a or $('document').ready{}; then to put JavaScript directly into the click event.

\n\n

In the case that JavaScript isn't available, I would use a href to the current URL, and perhaps an anchor to the position of the link. The page is still be usable for the people without JavaScript those who have won't notice any difference.

\n\n

As I have it to hand, here is some jQuery which might help:

\n\n
var [functionName] = function() {\n   // do something\n};\n\njQuery(\"[link id or other selector]\").bind(\"click\", [functionName]);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32464082fcc3049e9178d", + "creator": "Matt Kantor", + "createdAt": 1248270203000, + "text": "LowPro is really nice for unobtrusive JS if you have a lot of complex behaviors.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90420", + "creator": "naveen", + "createdAt": 1224755757000, + "text": "

It would be better to use jQuery,

\n\n
$(document).ready(function() {\n    $(\"a\").css(\"cursor\", \"pointer\");\n});\n
\n\n

and omit both href=\"#\" and href=\"javascript:void(0)\".

\n\n

The anchor tag markup will be like

\n\n
<a onclick=\"hello()\">Hello</a>\n
\n\n

Simple enough!

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32464082fcc3049e91790", + "creator": "mtyaka", + "createdAt": 1259057960000, + "text": "May I say this is the option that SO decided to go with. Check the "flag" links, for instance.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32464082fcc3049e91792", + "creator": "AndFisher", + "createdAt": 1484750730000, + "text": "Better to add a js class to the body, and Let CSS handle the CSS. body.js a{cursor: pointer;}", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90422", + "creator": "Lukom", + "createdAt": 1235840791000, + "text": "

You can also write a hint in an anchor like this:

\n\n
<a href=\"javascript:void('open popup image')\" onclick=\"return f()\">...</a>\n
\n\n

so the user will know what this link does.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90423", + "creator": "se_pavel", + "createdAt": 1239950974000, + "text": "

I use the following

\n\n
<a href=\"javascript:;\" onclick=\"myJsFunc();\">Link</a>\n
\n\n

instead

\n\n
<a href=\"javascript:void(0);\" onclick=\"myJsFunc();\">Link</a>\n
\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90424", + "creator": "Free Consulting", + "createdAt": 1257560808000, + "text": "

Definitely hash (#) is better because in JavaScript it is a pseudoscheme:

\n\n
    \n
  1. pollutes history
  2. \n
  3. instantiates new copy of engine
  4. \n
  5. runs in global scope and doesn't respect event system.
  6. \n
\n\n

Of course \"#\" with an onclick handler which prevents default action is [much] better. Moreover, a link that has the sole purpose to run JavaScript is not really \"a link\" unless you are sending user to some sensible anchor on the page (just # will send to top) when something goes wrong. You can simply simulate look and feel of link with stylesheet and forget about href at all.

\n\n

In addition, regarding cowgod's suggestion, particularly this: ...href=\"javascript_required.html\" onclick=\"... This is good approach, but it doesn't distinguish between \"JavaScript disabled\" and \"onclick fails\" scenarios.

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90425", + "creator": "Justin Johnson", + "createdAt": 1259008701000, + "text": "

# is better than javascript:anything, but the following is even better:

\n\n

HTML:

\n\n
<a href=\"/gracefully/degrading/url/with/same/functionality.ext\" class=\"some-selector\">For great justice</a>\n
\n\n

JavaScript:

\n\n
$(function() {\n    $(\".some-selector\").click(myJsFunc);\n});\n
\n\n

You should always strive for graceful degradation (in the event that the user doesn't have JavaScript enabled...and when it is with specs. and budget). Also, it is considered bad form to use JavaScript attributes and protocol directly in HTML.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32464082fcc3049e91797", + "creator": "Ry-", + "createdAt": 1397173636000, + "text": "@Muhd: Return should activate click on links…", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90427", + "creator": "rcof", + "createdAt": 1293463022000, + "text": "

There is one more important thing to remember here. Section 508 compliance.\nBecause of it, I feel it's necessary to point out that you need the anchor tag for screen readers such as JAWS to be able to focus it through tabbing. So the solution \"just use JavaScript and forget the anchor to begin with\" is not an option for some of this. Firing the JavaScript inside the href is only necessary if you can't afford for the screen to jump back up to the top. You can use a settimeout for 0 seconds and have JavaScript fire to where you need focus but even the apage will jump to the top and then back.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90426", + "creator": "user394888", + "createdAt": 1279406802000, + "text": "

In total agreement with the overall sentiment, use void(0) when you need it, and use a valid URL when you need it.

\n\n

Using URL rewriting you can make URLs that not only do what you want to do with JavaScript disabled, but also tell you exactly what its going to do.

\n\n
<a href=\"./Readable/Text/URL/Pointing/To/Server-Side/Script\" id=\"theLinkId\">WhyClickHere</a>\n
\n\n

On the server side, you just have to parse the URL and query string and do what you want. If you are clever, you can allow the server side script to respond to both Ajax and standard requests differently. Allowing you to have concise centralized code that handles all the links on your page.

\n\n

URL rewriting tutorials

\n\n

Pros

\n\n\n\n

Cons

\n\n\n\n

I am sure there are tons more cons out there. Feel free to discuss them.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90428", + "creator": "user564706", + "createdAt": 1311680144000, + "text": "

I choose use javascript:void(0), because use this could prevent right click to open the content menu. But javascript:; is shorter and does the same thing.

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90429", + "creator": "Spammer Joe", + "createdAt": 1316829306000, + "text": "

Using just # makes some funny movements, so I would recommend to use #self if you would like to save on typing efforts of JavaScript bla, bla,.

\n", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32464082fcc3049e9179c", + "creator": "syahid246", + "createdAt": 1652998847000, + "text": "has the same effect, as cHao said. You just need to return false on element a when clicked (onclick)", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9042a", + "creator": "Jesse Atkinson", + "createdAt": 1323896796000, + "text": "

I strongly prefer to keep my JavaScript out of my HTML markup as much as possible. If I'm using <a> as click event handlers then I'd recommend using <a class=\"trigger\" href=\"#\">Click me!</a>.

\n\n
$('.trigger').click(function (e) {\n    e.preventDefault();\n    // Do stuff...\n});\n
\n\n

It's very important to note that many developers out there believe that using anchor tags for click-event handlers isn't good. They'd prefer you to use a <span> or <div> with some CSS that adds cursor: pointer; to it. This is a matter if much debate.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9042b", + "creator": "Josh Simerman", + "createdAt": 1323900599000, + "text": "

I'm basically paraphrasing from this practical article using progressive enhancement. The short answer is that you never use javascript:void(0); or # unless your user interface has already inferred that JavaScript is enabled, in which case you should use javascript:void(0);. Also, do not use span as links, since that is semantically false to begin with.

\n\n

Using SEO friendly URL routes in your application, such as /Home/Action/Parameters is a good practice as well. If you have a link to a page that works without JavaScript first, you can enhance the experience afterward. Use a real link to a working page, then add an onlick event to enhance the presentation.

\n\n

Here is a sample. Home/ChangePicture is a working link to a form on a page complete with user interface and standard HTML submit buttons, but it looks nicer injected into a modal dialog with jQueryUI buttons. Either way works, depending on the browser, which satisfies mobile first development.

\n\n
<p><a href=\"Home/ChangePicture\" onclick=\"return ChangePicture_onClick();\" title=\"Change Picture\">Change Picture</a></p>\n\n<script type=\"text/javascript\">\n    function ChangePicture_onClick() {\n        $.get('Home/ChangePicture',\n              function (htmlResult) {\n                  $(\"#ModalViewDiv\").remove(); //Prevent duplicate dialogs\n                  $(\"#modalContainer\").append(htmlResult);\n                  $(\"#ModalViewDiv\").dialog({\n                      width: 400,\n                      modal: true,\n                      buttons: {\n                          \"Upload\": function () {\n                              if(!ValidateUpload()) return false;\n                              $(\"#ModalViewDiv\").find(\"form\").submit();\n                          },\n                          Cancel: function () { $(this).dialog(\"close\"); }\n                      },\n                      close: function () { }\n                  });\n              }\n        );\n        return false;\n    }\n</script>\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32465082fcc3049e917a0", + "creator": "eQ19", + "createdAt": 1429654707000, + "text": "In term of SEO I would prefer this way. Using as many friendly URL shall definitely improve page value factor.", + "upvotes": 337, + "upvoterUsernames": [], + "downvotes": 337, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9042c", + "creator": "Berker Yüceer", + "createdAt": 1323940895000, + "text": "

What I understand from your words is that you want to create a link just to run JavaScript code.

\n\n

Then you should consider that there are people who blocks JavaScript out there in their browsers.

\n\n

So if you are really going to use that link only for running a JavaScript function then you should add it dynamically so it won't be even seen if the users didn't enable their JavaScript in the browser and you are using that link just to trigger a JavaScript function which makes no sense to use a link like that when JavaScript is disabled in the browser.

\n\n

For that reason neither of them is good when JavaScript is disabled.

\n\n

Aand if JavaScript is enabled and you only want to use that link to invoke a JavaScript function then

\n\n
<a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Link</a>\n
\n\n

is far better way than using

\n\n
<a href=\"#\" onclick=\"myJsFunc();\">Link</a>\n
\n\n

because href=\"#\" is going to cause the page to do actions that are not needed.

\n\n

Also, another reason why <a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Link</a> is better than <a href=\"#\" onclick=\"myJsFunc();\">Link</a> is that JavaScript is used as the default scripting language for most of the browsers. As an example Internet Explorer, uses an onclick attribute to define the type of scripting language that would be used. Unless another good scripting language pops up, JavaScript will be used by Internet Explorer as the default too, but if another scripting language used javascript:, it would let Internet Explorer to understand which scripting language is being used.

\n\n

Considering this, I would prefer using and exercising on

\n\n
<a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Link</a>\n
\n\n

enough to make it a habit and to be more user friendly please add that kind of links within the JavaScript code:

\n\n
$(document).ready(function(){\n    $(\".blabla\").append('<a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Link</a>')\n});\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9042d", + "creator": "Tracker1", + "createdAt": 1323974517000, + "text": "

I would honestly suggest neither. I would use a stylized <button></button> for that behavior.

\n\n

\r\n
\r\n
button.link {\r\n  display: inline-block;\r\n  position: relative;\r\n  background-color: transparent;\r\n  cursor: pointer;\r\n  border: 0;\r\n  padding: 0;\r\n  color: #00f;\r\n  text-decoration: underline;\r\n  font: inherit;\r\n}
\r\n
<p>A button that looks like a <button type=\"button\" class=\"link\">link</button>.</p>
\r\n
\r\n
\r\n

\n\n

This way you can assign your onclick. I also suggest binding via script, not using the onclick attribute on the element tag. The only gotcha is the psuedo 3d text effect in older IEs that cannot be disabled.

\n\n
\n\n

If you MUST use an A element, use javascript:void(0); for reasons already mentioned.

\n\n\n\n

NOTE: You can replace the 0 with a string such as javascript:void('Delete record 123') which can serve as an extra indicator that will show what the click will actually do.

\n", + "upvotes": 590, + "upvoterUsernames": [], + "downvotes": 259, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32465082fcc3049e917a3", + "creator": "Y. Gherbi", + "createdAt": 1639268385000, + "text": "to be honest, Imo this is the correct answer. following semantics and it indeed doesn't make sense to use a link if it isn't a link", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32465082fcc3049e917a5", + "creator": "E. Williams", + "createdAt": 1658157591000, + "text": "Correct answer. Rest of answers are just hacks to change the natural behaviour of a component.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9042e", + "creator": "Eric Yin", + "createdAt": 1323994180000, + "text": "

I would use:

\n\n
<a href=\"#\" onclick=\"myJsFunc();return false;\">Link</a>\n
\n\n

Reasons:

\n\n
    \n
  1. This makes the href simple, search engines need it. If you use anything else ( such as a string), it may cause a 404 not found error.
  2. \n
  3. When mouse hovers over the link, it doesn't show that it is a script.
  4. \n
  5. By using return false;, the page doesn't jump to the top or break the back button.
  6. \n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9042f", + "creator": "Timo Huovinen", + "createdAt": 1331804617000, + "text": "

Here is one more option for completeness sake, that prevents the link from doing anything even if JavaScript is disabled, and it's short :)

\n\n
<a href=\"#void\" onclick=\"myJsFunc()\">Run JavaScript function</a>\n
\n\n

If the id is not present on the page, then the link will do nothing.

\n\n

Generally, I agree with the Aaron Wagner's answer, the JavaScript link should be injected with JavaScript code into the document.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90431", + "creator": "Stacks on Stacks on Stacks", + "createdAt": 1340652879000, + "text": "

When I've got several faux-links, I prefer to give them a class of 'no-link'.

\n\n

Then in jQuery, I add the following code:

\n\n
$(function(){\n   $('.no-link').click(function(e){\n       e.preventDefault();\n   });\n});\n
\n\n

And for the HTML, the link is simply

\n\n
<a href=\"/\" class=\"no-link\">Faux-Link</a>\n
\n\n

I don't like using Hash-Tags unless they're used for anchors, and I only do the above when I've got more than two faux-links, otherwise I go with javascript:void(0).

\n\n
<a href=\"javascript:void(0)\" class=\"no-link\">Faux-Link</a>\n
\n\n

Typically, I like to just avoid using a link at all and just wrap something around in a span and use that as a way to active some JavaScript code, like a pop-up or a content-reveal.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90430", + "creator": "dazbradbury", + "createdAt": 1331864173000, + "text": "

I see a lot of answers by people who want to keep using # values for href, hence, here is an answer hopefully satisfying both camps:

\n\n

A) I'm happy to have javascript:void(0) as my href value:

\n\n
<a href=\"javascript:void(0)\" onclick=\"someFunc.call(this)\">Link Text</a>\n
\n\n

B) I am using jQuery, and want # as my href value:

\n\n
<a href=\"#\" onclick=\"someFunc.call(this)\">Link Text</a>\n\n<script type=\"text/javascript\">\n    /* Stop page jumping when javascript links are clicked.\n       Only select links where the href value is a #. */\n    $('a[href=\"#\"]').live(\"click\", function(e) {\n         return false; // prevent default click action from happening!\n         e.preventDefault(); // same thing as above\n    });\n</script>\n
\n\n

Note, if you know links won't be created dynamically, use the click function instead:

\n\n

$('a[href=\"#\"]').click(function(e) {

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90433", + "creator": "G. Ghez", + "createdAt": 1360332902000, + "text": "

You should not use inline onclick=\"something();\" in your HTML to not polluate it with meaningless code; all click bindings must be set in Javascript files (*.js).

\n\n

Set binding like this : $('#myAnchor').click(function(){... **return false**;}); or $('#myAnchor').bind('click', function(){... **return false**;});

\n\n

Then you have a clean HTML file easy to load (and seo friendly) without thousands of href=\"javascript:void(0);\" and just href=\"#\"

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32465082fcc3049e917ab", + "creator": "ryabenko-pro", + "createdAt": 1382446545000, + "text": "But what if there is a list of elements and I want add link to remove some element from it by string ID?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90432", + "creator": "vol7ron", + "createdAt": 1345651683000, + "text": "

You could use the href and remove all links that have only hashes:

\n\n

HTML:

\n\n
<a href=\"#\" onclick=\"run_foo()\"> foo </a>\n
\n\n

JS:

\n\n
$(document).ready(function(){         // on DOM ready or some other event\n\n   $('a[href=#]').attr('href','');    // set all reference handles to blank strings\n                                      //  for anchors that have only hashes\n\n});\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90434", + "creator": "whirlwin", + "createdAt": 1374791517000, + "text": "

If you happen to be using AngularJS, you can use the following:

\n\n
<a href=\"\">Do some fancy JavaScript</a>\n
\n\n

Which will not do anything.

\n\n

In addition

\n\n\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32466082fcc3049e917af", + "creator": "Henry Hu", + "createdAt": 1375143393000, + "text": "But this would cause the page to reload, and since we're always using javascript to modify the page, this is unacceptable.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32466082fcc3049e917b1", + "creator": "whirlwin", + "createdAt": 1375181219000, + "text": "@HenryHu I figured out that the reason it did not reload was because of AngularJS. See my updated answer.", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90435", + "creator": "Ashish Kumar", + "createdAt": 1392262440000, + "text": "

So, when you are doing some JavaScript things with an <a /> tag and if you put href=\"#\" as well, you can add return false at the end of the event (in case of inline event binding) like:

\n\n
<a href=\"#\" onclick=\"myJsFunc(); return false;\">Run JavaScript Code</a>\n
\n\n

Or you can change the href attribute with JavaScript like:

\n\n
<a href=\"javascript://\" onclick=\"myJsFunc();\">Run JavaScript Code</a>\n
\n\n

or

\n\n
<a href=\"javascript:void(0)\" onclick=\"myJsFunc();\">Run JavaScript Code</a>\n
\n\n
\n\n

But semantically, all the above ways to achieve this are wrong (it works fine though). If any element is not created to navigate the page and that have some JavaScript things associated with it, then it should not be a <a> tag.

\n\n

You can simply use a <button /> instead to do things or any other element like b, span or whatever fits there as per your need, because you are allowed to add events on all the elements.

\n\n
\n\n

So, there is one benefit to use <a href=\"#\">. You get the cursor pointer by default on that element when you do a href=\"#\". For that, I think you can use CSS for this like cursor:pointer; which solves this problem also.

\n\n

And at the end, if you are binding the event from the JavaScript code itself, there you can do event.preventDefault() to achieve this if you are using <a> tag, but if you are not using a <a> tag for this, there you get an advantage, you don't need to do this.

\n\n

So, if you see, it's better not to use a tag for this kind of stuff.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90436", + "creator": "Vinny Fonseca", + "createdAt": 1394202484000, + "text": "

I use href=\"#\" for links that I want a dummy behaviour for. Then I use this code:

\n\n
$(document).ready(function() {\n    $(\"a[href='#']\").click(function(event) {\n        event.preventDefault();\n    });\n});\n
\n\n

Meaning if the href equals to a hash (*=\"#\") it prevents the default link behaviour, thus still allowing you to write functionality for it, and it doesn't affect anchor clicks.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90437", + "creator": "Anders M.", + "createdAt": 1395312957000, + "text": "

I'd say the best way is to make an href anchor to an ID you'd never use, like #Do1Not2Use3This4Id5 or a similar ID, that you are 100% sure no one will use and won't offend people.

\n\n
    \n
  1. Javascript:void(0) is a bad idea and violates Content Security Policy on CSP-enabled HTTPS pages https://developer.mozilla.org/en/docs/Security/CSP (thanks to @jakub.g)
  2. \n
  3. Using just # will have the user jump back to the top when pressed
  4. \n
  5. Won't ruin the page if JavaScript isn't enabled (unless you have JavaScript detecting code
  6. \n
  7. If JavaScript is enabled you can disable the default event
  8. \n
  9. You have to use href unless you know how to prevent your browser from selecting some text, (don't know if using 4 will remove the thing that stops the browser from selecting text)
  10. \n
\n\n

Basically no one mentioned 5 in this article which I think is important as your site comes off as unprofessional if it suddenly starts selecting things around the link.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90439", + "creator": "AlxGol", + "createdAt": 1416325365000, + "text": "

Why not using this? This doesn't scroll page up.

\n\n
<span role=\"button\" onclick=\"myJsFunc();\">Run JavaScript Code</span>\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32466082fcc3049e917b6", + "creator": "Martin", + "createdAt": 1489501139000, + "text": "As others have said, this cannot be activated using the keyboard.", + "upvotes": 1749, + "upvoterUsernames": [], + "downvotes": 1749, + "downvoterUsernames": [] + }, + { + "_id": "62f32466082fcc3049e917b8", + "creator": "AlxGol", + "createdAt": 1574840637000, + "text": "5 years after the answer I also consider the a11y features:)", + "upvotes": 731, + "upvoterUsernames": [], + "downvotes": 731, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90438", + "creator": "Milan and Friends", + "createdAt": 1398537450000, + "text": "

I personally use them in combination. For example:

\n\n

HTML

\n\n
<a href=\"#\">Link</a>\n
\n\n
\n\n

with little bit of jQuery

\n\n
$('a[href=\"#\"]').attr('href','javascript:void(0);');\n
\n\n

or

\n\n
$('a[href=\"#\"]').click(function(e) {\n   e.preventDefault();\n});\n
\n\n

But I'm using that just for preventing the page jumping to the top when the user clicks on an empty anchor. I'm rarely using onClick and other on events directly in HTML.

\n\n

My suggestion would be to use <span> element with the class attribute instead of\nan anchor. For example:

\n\n
<span class=\"link\">Link</span>\n
\n\n
\n\n

Then assign the function to .link with a script wrapped in the body and just before the </body> tag or in an external JavaScript document.

\n\n
<script>\n    (function($) {\n        $('.link').click(function() {\n            // do something\n        });\n    })(jQuery);\n</script>\n
\n\n

*Note: For dynamically created elements, use:

\n\n
$('.link').on('click', function() {\n    // do something\n});\n
\n\n

And for dynamically created elements which are created with dynamically created elements, use:

\n\n
$(document).on('click','.link', function() {\n    // do something\n});\n
\n\n
\n\n

Then you can style the span element to look like an anchor with a little CSS:

\n\n
.link {\n    color: #0000ee;\n    text-decoration: underline;\n    cursor: pointer;\n}\n.link:active {\n    color: red;\n}\n
\n\n

Here's a jsFiddle example of above aforementioned.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9043b", + "creator": "Garrett", + "createdAt": 1453838058000, + "text": "

Don't use links for the sole purpose of running JavaScript.

\n\n

The use of href=\"#\" scrolls the page to the top; the use of void(0) creates navigational problems within the browser.

\n\n

Instead, use an element other than a link:

\n\n
<span onclick=\"myJsFunc()\" class=\"funcActuator\">myJsFunc</span>\n
\n\n

And style it with CSS:

\n\n
.funcActuator { \n  cursor: default;\n}\n\n.funcActuator:hover { \n  color: #900;\n}\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32466082fcc3049e917bb", + "creator": "Quentin", + "createdAt": 1465992768000, + "text": "Use a button, not a span. Buttons naturally fall in the focus order so can be accessed without a mouse / trackpad / etc.", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9043a", + "creator": "dnetix", + "createdAt": 1439427664000, + "text": "

I usually go for

\n\n
<a href=\"javascript:;\" onclick=\"yourFunction()\">Link description</a>\n
\n\n

It's shorter than javascript:void(0) and does the same.

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9043d", + "creator": "TomDK", + "createdAt": 1490570855000, + "text": "

On a modern website the use of href should be avoided if the element is only doing JavaScript functionality (not a real link).

\n\n

Why?\nThe presence of this element tells the browser that this is a link with a destination. \nWith that, the browser will show the Open In New Tab / Window function (also triggered when you use shift+click). \nDoing so will result in opening the same page without the desired function triggered (resulting in user frustration).

\n\n

In regards to IE:\nAs of IE8, element styling (including hover) works if the doctype is set. Other versions of IE are not really to worry about anymore.

\n\n

Only Drawback:\nRemoving HREF removes the tabindex.\nTo overcome this, you can use a button that's styled as a link or add a tabindex attribute using JS.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9043c", + "creator": "user6460587", + "createdAt": 1477768216000, + "text": "

I tried both in google chrome with the developer tools, and the id=\"#\" took 0.32 seconds. While the javascript:void(0) method took only 0.18 seconds. So in google chrome, javascript:void(0) works better and faster.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32466082fcc3049e917be", + "creator": "Jochen Schultz", + "createdAt": 1505991506000, + "text": "Actually they don't do the same. # makes you jump to top of the page.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9043e", + "creator": "Charlie", + "createdAt": 1507146445000, + "text": "

Bootstrap modals from before 4.0 have a basically undocumented behavior that they will load hrefs from a elements using AJAX unless they are exactly #. If you are using Bootstrap 3, javascript:void(0); hrefs will cause javascript errors:

\n\n

AJAX Error: error GET javascript:void(0);

\n\n

In these cases you would need to upgrade to bootstrap 4 or change the href.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9043f", + "creator": "L Y E S - C H I O U K H", + "createdAt": 1546443925000, + "text": "

Edited on 2019 January

\n\n

In HTML5, using an a element without an href attribute is valid. It is considered to be a \"placeholder hyperlink\"

\n\n
\n

If the a element has no href attribute, then the element represents a placeholder for where a link might otherwise have been placed, if it had been relevant, consisting of just the element's contents.

\n
\n\n

Example:

\n\n
<a>previous</a>\n
\n\n

If after that you want to do otherwise :

\n\n

1 - If your link doesn't go anywhere, don't use an <a> element. Use a <span> or something else appropriate and add CSS :hover to style it as you wish.

\n\n

2 - Use the javascript:void(0) OR javascript:undefined OR javascript:; if you want to be raw, precise and fast.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90440", + "creator": "SchoolforDesign", + "createdAt": 1557841251000, + "text": "

Javascript: void(0); is void to null value [Not assigned], which that mean your browser is going to NULL click to DOM, and window return to false.
\n• The '#' is not follow the DOM or Window in javascript. which that mean the '#' sign inside anchor href is a LINK. Link to the same current direction.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90441", + "creator": "Javed Khan", + "createdAt": 1559105395000, + "text": "

You can use javascript:void(0) here instead of using # to stop anchor tag redirect to header section.

\n\n
function helloFunction() {\n    alert(\"hello world\");\n}\n<a href=\"javascript:void(0)\" onclick=\"helloFunction();\">Call Hello Function</a>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90442", + "creator": "Dev pokhariya", + "createdAt": 1560512251000, + "text": "

The most simple and used by everyone mostly is javascript:void(0) You can use it instead of using # to stop tag redirect to header section.

\n\n
<a href=\"javascript:void(0)\" onclick=\"testFunction();\">Click To check Function</a>\n\nfunction testFunction() {\n    alert(\"hello world\");\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90443", + "creator": "Stokely", + "createdAt": 1614624539000, + "text": "

There are actually four options here.

\n

Using return false; allows you to keep the anchor version in cases where you want a safe "fallback" in browsers that have JavaScript disabled or it is not supported in the user agent (1-5% of user's now). You can use the anchor "#" sign, an empty string, or a special URL for the href should your script fail. Note that you must use an href so screen readers know it is a hyperlink. (Note: I am not going to get into arguments about removing the href attribute as that point is moot here. Without an href on an anchor means the anchor is no longer a hyperlink and just an html tag with a click event on it that is captured.)

\n
<a href="" onclick="alert('hello world!');return false;">My Link</a>\n<a href="#" onclick="alert('hello world!');return false;">My Link</a>\n<a href="MyFallbackURL.html" onclick="alert('hello world!');return false;">My Link</a>\n
\n

Below is the more popular design today using javascript:void(0) inside the href attribute. If a browser doesn't support scripting it should post again back to its page again, as an empty string is returned for the href hyperlink path. Use this if you don't care who supports JavaScript.

\n
<a href="javascript:void(0);" onclick="alert('hello world!');">My Link</a>\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90444", + "creator": "Nedim AKAR", + "createdAt": 1624789060000, + "text": "

javascript:void(0) will deprecate in future, therefore you should use #.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9041c", + "creator": "Pablo Cabrera", + "createdAt": 1222429480000, + "text": "

If you are using an <a> element, just use this:

\n\n
<a href=\"javascript:myJSFunc();\" />myLink</a>\n
\n\n

Personally I'd attach an event handler with JavaScript later on instead (using attachEvent or addEventListener or maybe <put your favorite JavaScript framework here > also).

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bd082fcc3049e92e0b", + "creator": "Timo Huovinen", + "createdAt": 1337196975000, + "text": "Can someone explain the reason why this answer has so many downvotes?", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9041d", + "creator": "Fczbkk", + "createdAt": 1222513096000, + "text": "

I agree with suggestions elsewhere stating that you should use regular URL in href attribute, then call some JavaScript function in onclick. The flaw is, that they automaticaly add return false after the call.

\n\n

The problem with this approach is, that if the function will not work or if there will be any problem, the link will become unclickable. Onclick event will always return false, so the normal URL will not be called.

\n\n

There's very simple solution. Let function return true if it works correctly. Then use the returned value to determine if the click should be cancelled or not:

\n\n

JavaScript

\n\n
function doSomething() {\n    alert( 'you clicked on the link' );\n    return true;\n}\n
\n\n

HTML

\n\n
<a href=\"path/to/some/url\" onclick=\"return !doSomething();\">link text</a>\n
\n\n
\n\n

Note, that I negate the result of the doSomething() function. If it works, it will return true, so it will be negated (false) and the path/to/some/URL will not be called. If the function will return false (for example, the browser doesn't support something used within the function or anything else goes wrong), it is negated to true and the path/to/some/URL is called.

\n", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9041f", + "creator": "Andrew Moore", + "createdAt": 1224180025000, + "text": "

Usually, you should always have a fall back link to make sure that clients with JavaScript disabled still has some functionality. This concept is called unobtrusive JavaScript.

\n\n

Example... Let's say you have the following search link:

\n\n
<a href=\"search.php\" id=\"searchLink\">Search</a>\n
\n\n

You can always do the following:

\n\n
var link = document.getElementById('searchLink');\n\nlink.onclick = function() {\n    try {\n        // Do Stuff Here        \n    } finally {\n        return false;\n    }\n};\n
\n\n

That way, people with JavaScript disabled are directed to search.php while your viewers with JavaScript view your enhanced functionality.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9041e", + "creator": "treat your mods well", + "createdAt": 1224179211000, + "text": "

I recommend using a <button> element instead, especially if the control is supposed to produce a change in the data. (Something like a POST.)

\n\n

It's even better if you inject the elements unobtrusively, a type of progressive enhancement. (See this comment.)

\n", + "upvotes": 111, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bd082fcc3049e92e0f", + "creator": "user8331407", + "createdAt": 1625474581000, + "text": "this depend if you need button on your bem ( block element model) of html.", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9041a", + "creator": "Shadow2531", + "createdAt": 1222378697000, + "text": "

If you use a link as a way to just execute some JavaScript code (instead of using a span like D4V360 greatly suggested), just do:

\n\n
<a href=\"javascript:(function()%7Balert(%22test%22)%3B%7D)()%3B\">test</a>\n
\n\n

If you're using a link with onclick for navigation, don't use href=\"#\" as the fallback when JavaScript is off. It's usually very annoying when the user clicks on the link. Instead, provide the same link the onclick handler would provide if possible. If you can't do that, skip the onclick and just use a JavaScript URI in the href.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bd082fcc3049e92e11", + "creator": "mplungjan", + "createdAt": 1326569058000, + "text": "This would give an error if JS was off instead of seemingly doing nothing or just go to the top of the page", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f329bd082fcc3049e92e12", + "creator": "mplungjan", + "createdAt": 1326623318000, + "text": "So have href="jsdisabled.html" instead", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f329bd082fcc3049e92e13", + "creator": "Shadow2531", + "createdAt": 1326693156000, + "text": "@mplungjan You could. But, that navigates away from the page.", + "upvotes": 165, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e9040e", + "creator": "RobG", + "createdAt": 1654952220000, + "text": "Why use a link when you want a button? Then there is no issue with pseudo–protocols.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2449752, + "uvac": 2449806 + } + }, + { + "_id": "62f321bb082fcc3049e8ff04", + "title": "How to format numbers as currency strings", + "title-lowercase": "how to format numbers as currency strings", + "creator": "Daniel Magliola", + "createdAt": 1222700428000, + "status": "open", + "text": "

I would like to format a price in JavaScript. I'd like a function which takes a float as an argument and returns a string formatted like this:

\n
"$ 2,500.00"\n
\n

What's the best way to do this?

\n", + "upvotes": 2588, + "upvoterUsernames": [], + "downvotes": 280, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2517607, + "answers": 59, + "answerItems": [ + { + "_id": "62f321cd082fcc3049e90f18", + "creator": "roenving", + "createdAt": 1222700809000, + "text": "

The main part is inserting the thousand-separators, and that could be done like this:

\n
<script type="text/javascript">\n  function ins1000Sep(val) {\n    val = val.split(".");\n    val[0] = val[0].split("").reverse().join("");\n    val[0] = val[0].replace(/(\\d{3})/g, "$1,");\n    val[0] = val[0].split("").reverse().join("");\n    val[0] = val[0].indexOf(",") == 0 ? val[0].substring(1) : val[0];\n    return val.join(".");\n  }\n\n  function rem1000Sep(val) {\n    return val.replace(/,/g, "");\n  }\n\n  function formatNum(val) {\n    val = Math.round(val*100)/100;\n    val = ("" + val).indexOf(".") > -1 ? val + "00" : val + ".00";\n    var dec = val.indexOf(".");\n    return dec == val.length-3 || dec == 0 ? val : val.substring(0, dec+3);\n  }\n</script>\n\n<button onclick="alert(ins1000Sep(formatNum(12313231)));">\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b3c", + "creator": "Peter", + "createdAt": 1439278372000, + "text": "I get wrong number output while entering negative values to ins1000Sep().", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f1a", + "creator": "Bill the Lizard", + "createdAt": 1222701417000, + "text": "
function CurrencyFormatted(amount)\n{\n    var i = parseFloat(amount);\n    if(isNaN(i)) { i = 0.00; }\n    var minus = '';\n    if(i < 0) { minus = '-'; }\n    i = Math.abs(i);\n    i = parseInt((i + .005) * 100);\n    i = i / 100;\n    s = new String(i);\n    if(s.indexOf('.') < 0) { s += '.00'; }\n    if(s.indexOf('.') == (s.length - 2)) { s += '0'; }\n    s = minus + s;\n    return s;\n}\n
\n\n

From WillMaster.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b3d", + "creator": "Connor Simpson", + "createdAt": 1469711954000, + "text": "Small and simple. Thank you.", + "upvotes": 2529, + "upvoterUsernames": [], + "downvotes": 2529, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b3f", + "creator": "aron", + "createdAt": 1521480240000, + "text": "simple, but no comma for 1,000", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f1b", + "creator": "albertein", + "createdAt": 1222701469000, + "text": "

The YUI codebase uses the following formatting:

\n
format: function(nData, oConfig) {\n    oConfig = oConfig || {};\n\n    if(!YAHOO.lang.isNumber(nData)) {\n        nData *= 1;\n    }\n\n    if(YAHOO.lang.isNumber(nData)) {\n        var sOutput = nData + "";\n        var sDecimalSeparator = (oConfig.decimalSeparator) ? oConfig.decimalSeparator : ".";\n        var nDotIndex;\n\n        // Manage decimals\n        if(YAHOO.lang.isNumber(oConfig.decimalPlaces)) {\n            // Round to the correct decimal place\n            var nDecimalPlaces = oConfig.decimalPlaces;\n            var nDecimal = Math.pow(10, nDecimalPlaces);\n            sOutput = Math.round(nData*nDecimal)/nDecimal + "";\n            nDotIndex = sOutput.lastIndexOf(".");\n\n            if(nDecimalPlaces > 0) {\n                // Add the decimal separator\n                if(nDotIndex < 0) {\n                    sOutput += sDecimalSeparator;\n                    nDotIndex = sOutput.length-1;\n                }\n                // Replace the "."\n                else if(sDecimalSeparator !== "."){\n                    sOutput = sOutput.replace(".",sDecimalSeparator);\n                }\n                // Add missing zeros\n                while((sOutput.length - 1 - nDotIndex) < nDecimalPlaces) {\n                    sOutput += "0";\n                }\n            }\n        }\n\n        // Add the thousands separator\n        if(oConfig.thousandsSeparator) {\n            var sThousandsSeparator = oConfig.thousandsSeparator;\n            nDotIndex = sOutput.lastIndexOf(sDecimalSeparator);\n            nDotIndex = (nDotIndex > -1) ? nDotIndex : sOutput.length;\n            var sNewOutput = sOutput.substring(nDotIndex);\n            var nCount = -1;\n            for (var i=nDotIndex; i>0; i--) {\n                nCount++;\n                if ((nCount%3 === 0) && (i !== nDotIndex)) {\n                    sNewOutput = sThousandsSeparator + sNewOutput;\n                }\n                sNewOutput = sOutput.charAt(i-1) + sNewOutput;\n            }\n            sOutput = sNewOutput;\n        }\n\n        // Prepend prefix\n        sOutput = (oConfig.prefix) ? oConfig.prefix + sOutput : sOutput;\n\n        // Append suffix\n        sOutput = (oConfig.suffix) ? sOutput + oConfig.suffix : sOutput;\n\n        return sOutput;\n    }\n    // Still not a number. Just return it unaltered\n    else {\n        return nData;\n    }\n}\n
\n

It would need editing as the YUI library is configurable, like replacing oConfig.decimalSeparator with ".".

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b42", + "creator": "Daniel Magliola", + "createdAt": 1222701924000, + "text": "Too long, and i'd have to include YUI", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b44", + "creator": "Marco Demaio", + "createdAt": 1328788046000, + "text": "At YUI they must be sick, can't believe they wrote such piece of code.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f1c", + "creator": "Daniel Magliola", + "createdAt": 1222701753000, + "text": "

Ok, based on what you said, I'm using this:

\n
var DecimalSeparator = Number("1.2").toLocaleString().substr(1,1);\n\nvar AmountWithCommas = Amount.toLocaleString();\nvar arParts = String(AmountWithCommas).split(DecimalSeparator);\nvar intPart = arParts[0];\nvar decPart = (arParts.length > 1 ? arParts[1] : '');\ndecPart = (decPart + '00').substr(0,2);\n\nreturn '£ ' + intPart + DecimalSeparator + decPart;\n
\n

I'm open to improvement suggestions (I'd prefer not to include YUI just to do this :-) )

\n

I already know I should be detecting the "." instead of just using it as the decimal separator...

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f1d", + "creator": "Ates Goral", + "createdAt": 1222704164000, + "text": "

A minimalistic approach that just meets the original requirements:

\n
function formatMoney(n) {\n    return "$ " + (Math.round(n * 100) / 100).toLocaleString();\n}\n
\n

@Daniel Magliola: You're right. The above was a hasty, incomplete implementation. Here's the corrected implementation:

\n
function formatMoney(n) {\n    return "$ " + n.toLocaleString().split(".")[0] + "."\n        + n.toFixed(2).split(".")[1];\n}\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b46", + "creator": "Ates Goral", + "createdAt": 1223045362000, + "text": "This was a "minimalistic" approach to meet the original vague requirements that just gave "$ 2,500.00" as an example.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f19", + "creator": "17 of 26", + "createdAt": 1222701287000, + "text": "

Take a look at the JavaScript Number object and see if it can help you.

\n\n

To use these at the same time the value must have its type changed back to a number because they both output a string.

\n

Example:

\n
Number((someNumber).toFixed(1)).toLocaleString()\n
\n

EDIT

\n

One can just use toLocaleString directly and its not necessary to recast to a number:

\n
someNumber.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});\n
\n

Multiple numbers

\n

If you need to frequently format numbers similarly you can create a specific object for reuse. Like for German (Switzerland):

\n
const money = new Intl.NumberFormat('de-CH',\n  { style:'currency', currency: 'CHF' });\nconst percent = new Intl.NumberFormat('de-CH',\n  { style:'percent', maximumFractionDigits: 1, signDisplay: "always"});\n
\n

which than can be used as:

\n
money.format(1234.50); // output CHF 1'234.50\npercent.format(0.083);  // output +8.3%\n
\n

Pretty nifty.

\n", + "upvotes": 480, + "upvoterUsernames": [], + "downvotes": 136, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b47", + "creator": "Daniel Magliola", + "createdAt": 1222701955000, + "text": "Thanks! Based on this idea I was able to make one that is short and simple enough! (and localized) Excellent.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b48", + "creator": "Zaptree", + "createdAt": 1384745619000, + "text": "Actually You can. i.e. for dollars: '$'+(value + 0.001).toLocaleString().slice(0,-1)", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b4a", + "creator": "acorncom", + "createdAt": 1386290334000, + "text": "Looks like it'd be great, but there is little browser support at the moment", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b4c", + "creator": "Neil Monroe", + "createdAt": 1403799484000, + "text": "Safari definitely implements toLocale functions differently. The locale date formats also produce different output than every other major browser.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b4e", + "creator": "Alexander", + "createdAt": 1529491398000, + "text": "Yes, you are correct. The one I now use is the prototype Number.prototype.formatMoney which is identified below.", + "upvotes": 460, + "upvoterUsernames": [], + "downvotes": 460, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b4f", + "creator": "oldboy", + "createdAt": 1565600815000, + "text": "the best answer by far", + "upvotes": 3127, + "upvoterUsernames": [], + "downvotes": 3127, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b51", + "creator": "acido", + "createdAt": 1573237362000, + "text": "The Number() is mandatory if the var is a string or any other type. This helped me a lot! thanks for sharing it.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b53", + "creator": "Cegone", + "createdAt": 1592316211000, + "text": "I agree with @Burak, why this is best answer so far.. anyone tried? it is not giving expected output", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f1f", + "creator": "Richard Parnaby-King", + "createdAt": 1274953109000, + "text": "
function getMoney(A){\n    var a = new Number(A);\n    var b = a.toFixed(2); // Get 12345678.90\n    a = parseInt(a); // Get 12345678\n    b = (b-a).toPrecision(2); // Get 0.90\n    b = parseFloat(b).toFixed(2); // In case we get 0.0, we pad it out to 0.00\n    a = a.toLocaleString(); // Put in commas - Internet Explorer also puts in .00, so we'll get 12,345,678.00\n    // If Internet Explorer (our number ends in .00)\n    if(a < 1 && a.lastIndexOf('.00') == (a.length - 3))\n    {\n        a = a.substr(0, a.length-3); // Delete the .00\n    }\n    return a + b.substr(1); // Remove the 0 from b, then return a + b = 12,345,678.90\n}\nalert(getMoney(12345678.9));\n
\n

This works in Firefox and Internet Explorer.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f1e", + "creator": "Marco Demaio", + "createdAt": 1274280534000, + "text": "

Below is the Patrick Desjardins (alias Daok) code with a bit of comments added and some minor changes:

\n
/*\ndecimal_sep: character used as decimal separator, it defaults to '.' when omitted\nthousands_sep: char used as thousands separator, it defaults to ',' when omitted\n*/\nNumber.prototype.toMoney = function(decimals, decimal_sep, thousands_sep)\n{\n   var n = this,\n   c = isNaN(decimals) ? 2 : Math.abs(decimals), // If decimal is zero we must take it. It means the user does not want to show any decimal\n   d = decimal_sep || '.', // If no decimal separator is passed, we use the dot as default decimal separator (we MUST use a decimal separator)\n\n   /*\n   According to [https://stackoverflow.com/questions/411352/how-best-to-determine-if-an-argument-is-not-sent-to-the-javascript-function]\n   the fastest way to check for not defined parameter is to use typeof value === 'undefined'\n   rather than doing value === undefined.\n   */\n   t = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, // If you don't want to use a thousands separator you can pass empty string as thousands_sep value\n\n   sign = (n < 0) ? '-' : '',\n\n   // Extracting the absolute value of the integer part of the number and converting to string\n   i = parseInt(n = Math.abs(n).toFixed(c)) + '',\n\n   j = ((j = i.length) > 3) ? j % 3 : 0;\n   return sign + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\\d{3})(?=\\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');\n}\n
\n

And here some tests:

\n
// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)\nalert(123456789.67392.toMoney() + '\\n' + 123456789.67392.toMoney(3) + '\\n' + 123456789.67392.toMoney(0) + '\\n' + (123456).toMoney() + '\\n' + (123456).toMoney(0) + '\\n' + 89.67392.toMoney() + '\\n' + (89).toMoney());\n\n// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)\nalert((-123456789.67392).toMoney() + '\\n' + (-123456789.67392).toMoney(-3));\n
\n

The minor changes are:

\n
    \n
  1. moved a bit the Math.abs(decimals) to be done only when is not NaN.

    \n
  2. \n
  3. decimal_sep can not be empty string any more (a some sort of decimal separator is a must)

    \n
  4. \n
  5. we use typeof thousands_sep === 'undefined' as suggested in How best to determine if an argument is not sent to the JavaScript function

    \n
  6. \n
  7. (+n || 0) is not needed because this is a Number object

    \n
  8. \n
\n

JSFiddle

\n", + "upvotes": 290, + "upvoterUsernames": [], + "downvotes": 119, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b56", + "creator": "sohtimsso1970", + "createdAt": 1321372875000, + "text": "You may want to use '10' as the radix in parseInt. Otherwise, any number that starts with '0' will use octal numbering.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b58", + "creator": "Tracker1", + "createdAt": 1332201731000, + "text": "try, for example: parseInt("016") ... returns 14, as parseInt assumes it's octal encoded, when the string begins with a zero.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b59", + "creator": "Anton P Robul", + "createdAt": 1458329201000, + "text": "This function are incorrect: > (2030).toMoney(0, '.', ' '); < "2 03 0"", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f20", + "creator": "Miller Medeiros", + "createdAt": 1279567567000, + "text": "

As usually, there are multiple ways of doing the same thing, but I would avoid using Number.prototype.toLocaleString since it can return different values based on the user settings.

\n

I also don't recommend extending the Number.prototype - extending native objects prototypes is a bad practice since it can cause conflicts with other people code (e.g. libraries/frameworks/plugins) and may not be compatible with future JavaScript implementations/versions.

\n

I believe that regular expressions are the best approach for the problem, here is my implementation:

\n
/**\n * Converts number into currency format\n * @param {number} number    Number that should be converted.\n * @param {string} [decimalSeparator]    Decimal separator, defaults to '.'.\n * @param {string} [thousandsSeparator]    Thousands separator, defaults to ','.\n * @param {int} [nDecimalDigits]    Number of decimal digits, defaults to `2`.\n * @return {string} Formatted string (e.g. numberToCurrency(12345.67) returns '12,345.67')\n */\nfunction numberToCurrency(number, decimalSeparator, thousandsSeparator, nDecimalDigits){\n    //default values\n    decimalSeparator = decimalSeparator || '.';\n    thousandsSeparator = thousandsSeparator || ',';\n    nDecimalDigits = nDecimalDigits == null? 2 : nDecimalDigits;\n\n    var fixed = number.toFixed(nDecimalDigits), //limit/add decimal digits\n        parts = new RegExp('^(-?\\\\d{1,3})((?:\\\\d{3})+)(\\\\.(\\\\d{'+ nDecimalDigits +'}))?$').exec( fixed ); //separate begin [$1], middle [$2] and decimal digits [$4]\n\n    if(parts){ //number >= 1000 || number <= -1000\n        return parts[1] + parts[2].replace(/\\d{3}/g, thousandsSeparator + '$&') + (parts[4] ? decimalSeparator + parts[4] : '');\n    }else{\n        return fixed.replace('.', decimalSeparator);\n    }\n}\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b5b", + "creator": "Joseph Lennox", + "createdAt": 1376534690000, + "text": "The point of toLocaleString is that it does adjust with the user's settings.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f21", + "creator": "Wayne", + "createdAt": 1300379299000, + "text": "

Here's another attempt, just for fun:

\n
function formatDollar(num) {\n    var p = num.toFixed(2).split(".");\n    return "$" + p[0].split("").reverse().reduce(function(acc, num, i, orig) {\n        return num + (num != "-" && i && !(i % 3) ? "," : "") + acc;\n    }, "") + "." + p[1];\n}\n
\n

And some tests:

\n
formatDollar(45664544.23423) // "$45,664,544.23"\nformatDollar(45) // "$45.00"\nformatDollar(123) // "$123.00"\nformatDollar(7824) // "$7,824.00"\nformatDollar(1) // "$1.00"\nformatDollar(-1345) // "$-1,345.00\nformatDollar(-3) // "$-3.00"\n
\n", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b5e", + "creator": "Blaise", + "createdAt": 1336651665000, + "text": "A not about compatability: The reduce method was introduced in Ecmascript 1.8, and is not supported in Internet Explorer 8 and below.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b60", + "creator": "rsbarro", + "createdAt": 1375386362000, + "text": "Like @Blaise said, this method will not work in IE 8 or below.", + "upvotes": 530, + "upvoterUsernames": [], + "downvotes": 530, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b62", + "creator": "Wayne", + "createdAt": 1379363037000, + "text": "Yes, that's of course correct. As noted in the answer itself, this is just for fun. Also, it should handle negative numbers just fine.", + "upvotes": 4400, + "upvoterUsernames": [], + "downvotes": 4400, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b63", + "creator": "Betty Mock", + "createdAt": 1421280607000, + "text": "Negative numbers worked fine for me. What I like about this is that there isn't very much code and it doesn't depend on a regex.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b65", + "creator": "Chase Sandmann", + "createdAt": 1434487020000, + "text": "Why didn't you just do (parseInt(p[0])).toLocaleString()?", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b67", + "creator": "Wayne", + "createdAt": 1620106887000, + "text": "The edit to support negative numbers doesn't actually seem to work", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b68", + "creator": "Md. Rejaul Karim", + "createdAt": 1637742591000, + "text": "Not work with negative number", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f22", + "creator": "jc00ke", + "createdAt": 1302894237000, + "text": "

Patrick Desjardins (ex Daok)'s example worked well for me. I ported it over to CoffeeScript if anyone is interested.

\n
Number.prototype.toMoney = (decimals = 2, decimal_separator = ".", thousands_separator = ",") ->\n    n = this\n    c = if isNaN(decimals) then 2 else Math.abs decimals\n    sign = if n < 0 then "-" else ""\n    i = parseInt(n = Math.abs(n).toFixed(c)) + ''\n    j = if (j = i.length) > 3 then j % 3 else 0\n    x = if j then i.substr(0, j) + thousands_separator else ''\n    y = i.substr(j).replace(/(\\d{3})(?=\\d)/g, "$1" + thousands_separator)\n    z = if c then decimal_separator + Math.abs(n - i).toFixed(c).slice(2) else ''\n    sign + x + y + z\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f23", + "creator": "Daniel Fernandez", + "createdAt": 1305141746000, + "text": "

This might work:

\n\n
function format_currency(v, number_of_decimals, decimal_separator, currency_sign){\n  return (isNaN(v)? v : currency_sign + parseInt(v||0).toLocaleString() + decimal_separator + (v*1).toFixed(number_of_decimals).slice(-number_of_decimals));\n}\n
\n\n

No loops, no regexes, no arrays, no exotic conditionals.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b6b", + "creator": "Peter Mortensen", + "createdAt": 1622064097000, + "text": "Does it work? 10 years should be enough to have figured it out. Or is it a rhetorical question?", + "upvotes": 4762, + "upvoterUsernames": [], + "downvotes": 4762, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f25", + "creator": "daveoncode", + "createdAt": 1311320871000, + "text": "

I use the library Globalize (from Microsoft):

\n\n

It's a great project to localize numbers, currencies and dates and to have them automatically formatted the right way according to the user locale! ...and despite it should be a jQuery extension, it's currently a 100% independent library. I suggest you all to try it out! :)

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b6d", + "creator": "Neil Monroe", + "createdAt": 1403799824000, + "text": "It is still considered alpha stage, so use cautiously, but great find.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b6e", + "creator": "Guy Schalnat", + "createdAt": 1440181590000, + "text": "No longer in alpha (or beta). This seems to be very useful while we wait for Safari to meet the new standard and for IE < 11 to die.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f24", + "creator": "Goodeq", + "createdAt": 1310792958000, + "text": "

javascript-number-formatter (formerly at Google Code)

\n\n

(excerpt from its README)

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f27", + "creator": "Julien de Prabère", + "createdAt": 1322579696000, + "text": "

A quicker way with regexp:

\n
Number.prototype.toMonetaryString = function() {\n  var n = this.toFixed(2), m;\n  //var = this.toFixed(2).replace(/\\./, ','); For comma separator\n  // with a space for thousands separator\n  while ((m = n.replace(/(\\d)(\\d\\d\\d)\\b/g, '$1 $2')) != n)\n      n = m;\n  return m;\n}\n\nString.prototype.fromMonetaryToNumber = function(s) {\n  return this.replace(/[^\\d-]+/g, '')/100;\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f26", + "creator": "GasheK", + "createdAt": 1314855970000, + "text": "

accounting.js is a tiny JavaScript library for number, money and currency formatting.

\n", + "upvotes": 208, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32929082fcc3049e92b73", + "creator": "vector", + "createdAt": 1318020716000, + "text": "... just remember to pass a currency symbol otherwise it errors out in IE7 and IE8, IE9 is fine either way", + "upvotes": 450, + "upvoterUsernames": [], + "downvotes": 450, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b75", + "creator": "Mat Schaffer", + "createdAt": 1326829313000, + "text": "Looks like the IE7/IE8 bug is fixed.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b77", + "creator": "Neil Monroe", + "createdAt": 1403799918000, + "text": "I like the fact that you can do the reverse--pass a formatted currency string and get the numeric value.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32929082fcc3049e92b78", + "creator": "Ted", + "createdAt": 1425546198000, + "text": "Keep voting this one up as it's the best solution (if not the best answer).", + "upvotes": 3879, + "upvoterUsernames": [], + "downvotes": 3879, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f28", + "creator": "troy", + "createdAt": 1322868310000, + "text": "

A simple option for proper comma placement by reversing the string first and basic regexp.

\n\n
String.prototype.reverse = function() {\n    return this.split('').reverse().join('');\n};\n\nNumber.prototype.toCurrency = function( round_decimal /*boolean*/ ) {       \n     // format decimal or round to nearest integer\n     var n = this.toFixed( round_decimal ? 0 : 2 );\n\n     // convert to a string, add commas every 3 digits from left to right \n     // by reversing string\n     return (n + '').reverse().replace( /(\\d{3})(?=\\d)/g, '$1,' ).reverse();\n};\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f29", + "creator": "Julien de Prabère", + "createdAt": 1325677629000, + "text": "

A shorter method (for inserting space, comma or point) with a regular expression:

\n
    Number.prototype.toCurrencyString = function(){\n        return this.toFixed(2).replace(/(\\d)(?=(\\d{3})+\\b)/g, '$1 ');\n    }\n\n    n = 12345678.9;\n    alert(n.toCurrencyString());\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3292a082fcc3049e92b7b", + "creator": "Harrison", + "createdAt": 1614724514000, + "text": "This is amazing! Should be the best answers,!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f2a", + "creator": "Diodeus - James MacFarlane", + "createdAt": 1329424859000, + "text": "

There is no equivalent of \"formatNumber\" in JavaScript. You can write it yourself or find a library that already does this.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f2b", + "creator": "crush", + "createdAt": 1329424929000, + "text": "

I think you want:

\n
f.nettotal.value = "$" + showValue.toFixed(2);\n
\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3292a082fcc3049e92b7e", + "creator": "Rocco The Taco", + "createdAt": 1329425366000, + "text": "@ crush this works but it no longer carries the calculations onto the tax field?", + "upvotes": 218, + "upvoterUsernames": [], + "downvotes": 218, + "downvoterUsernames": [] + }, + { + "_id": "62f3292a082fcc3049e92b80", + "creator": "crush", + "createdAt": 1329425953000, + "text": "Once you append a $ sign to it, it is no longer a number, but a string.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f3292a082fcc3049e92b82", + "creator": "Simon East", + "createdAt": 1570607102000, + "text": "This option doesn't put a comma between the thousands. :-(", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f2d", + "creator": "Gate", + "createdAt": 1329480586000, + "text": "

There is a built-in function, toFixed, in JavaScript:

\n
var num = new Number(349);\ndocument.write("$" + num.toFixed(2));\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3292a082fcc3049e92b83", + "creator": "Ian Dunn", + "createdAt": 1348594082000, + "text": "This answer looks redundant. Crush's answer already mentinoed toFixed()", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f2c", + "creator": "Jonathan M", + "createdAt": 1329424930000, + "text": "

Here's the best JavaScript money formatter I've seen:

\n
Number.prototype.formatMoney = function(decPlaces, thouSeparator, decSeparator) {\n    var n = this,\n        decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,\n        decSeparator = decSeparator == undefined ? "." : decSeparator,\n        thouSeparator = thouSeparator == undefined ? "," : thouSeparator,\n        sign = n < 0 ? "-" : "",\n        i = parseInt(n = Math.abs(+n || 0).toFixed(decPlaces)) + "",\n        j = (j = i.length) > 3 ? j % 3 : 0;\n    return sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\\d{3})(?=\\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(n - i).toFixed(decPlaces).slice(2) : "");\n};\n
\n

It was reformatted and borrowed from here: How to format numbers as currency strings

\n

You'll have to supply your own currency designator (you used $ above).

\n

Call it like this (although note that the arguments default to 2, comma, and period, so you don't need to supply any arguments if that's your preference):

\n
var myMoney = 3543.75873;\nvar formattedMoney = '$' + myMoney.formatMoney(2, ',', '.'); // "$3,543.76"\n
\n", + "upvotes": 183, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3292a082fcc3049e92b86", + "creator": "hacklikecrack", + "createdAt": 1384968800000, + "text": "watch out for globals sign, i, j", + "upvotes": 868, + "upvoterUsernames": [], + "downvotes": 868, + "downvoterUsernames": [] + }, + { + "_id": "62f3292a082fcc3049e92b87", + "creator": "Jonathan M", + "createdAt": 1384970317000, + "text": "@hacklikecrack, all variables are local; they're in the var statement.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f3292a082fcc3049e92b89", + "creator": "hacklikecrack", + "createdAt": 1393345081000, + "text": "sorry, yes, though you're redeclaring arguments. Indentation! ;)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3292a082fcc3049e92b8a", + "creator": "Serj Sagan", + "createdAt": 1523299837000, + "text": "Horrible use of variable names!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f2e", + "creator": "Tim Saylor", + "createdAt": 1334548855000, + "text": "

Patrick Desjardins' answer looks good, but I prefer my JavaScript code simple. Here's a function I just wrote to take a number in and return it in currency format (minus the dollar sign):

\n
// Format numbers to two decimals with commas\nfunction formatDollar(num) {\n    var p = num.toFixed(2).split(".");\n    var chars = p[0].split("").reverse();\n    var newstr = '';\n    var count = 0;\n    for (x in chars) {\n        count++;\n        if(count%3 == 1 && count != 1) {\n            newstr = chars[x] + ',' + newstr;\n        } else {\n            newstr = chars[x] + newstr;\n        }\n    }\n    return newstr + "." + p[1];\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3292a082fcc3049e92b8c", + "creator": "n8jadams", + "createdAt": 1565714880000, + "text": "I needed something to work in both the browser and in an old version of Node. This worked perfectly. Thanks", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f32", + "creator": "adamwdraper", + "createdAt": 1349212050000, + "text": "

Numeral.js - a JavaScript library for easy number formatting by @adamwdraper

\n
numeral(23456.789).format('$0,0.00'); // = "$23,456.79"\n
\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32967082fcc3049e92b8f", + "creator": "adamwdraper", + "createdAt": 1480713777000, + "text": "Numeral.js is active again.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f33", + "creator": "juanOS", + "createdAt": 1349212527000, + "text": "

I suggest the NumberFormat class from Google Visualization API.

\n

You can do something like this:

\n
var formatter = new google.visualization.NumberFormat({\n    prefix: '$',\n    pattern: '#,###,###.##'\n});\n\nformatter.formatValue(1000000); // $ 1,000,000\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f36", + "creator": "Kirk Bentley", + "createdAt": 1359436916000, + "text": "

Here is a mootools 1.2 implementation from the code provided by XMLilley...

\n\n
Number.implement('format', function(decPlaces, thouSeparator, decSeparator){\ndecPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces;\ndecSeparator = decSeparator === undefined ? '.' : decSeparator;\nthouSeparator = thouSeparator === undefined ? ',' : thouSeparator;\n\nvar num = this,\n    sign = num < 0 ? '-' : '',\n    i = parseInt(num = Math.abs(+num || 0).toFixed(decPlaces)) + '',\n    j = (j = i.length) > 3 ? j % 3 : 0;\n\nreturn sign + (j ? i.substr(0, j) + thouSeparator : '') + i.substr(j).replace(/(\\d{3})(?=\\d)/g, '$1' + thouSeparator) + (decPlaces ? decSeparator + Math.abs(num - i).toFixed(decPlaces).slice(2) : '');\n});\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f34", + "creator": "mendezcode", + "createdAt": 1350618221000, + "text": "

Here's mine...

\n\n
function thousandCommas(num) {\n  num = num.toString().split('.');\n  var ints = num[0].split('').reverse();\n  for (var out=[],len=ints.length,i=0; i < len; i++) {\n    if (i > 0 && (i % 3) === 0) out.push(',');\n    out.push(ints[i]);\n  }\n  out = out.reverse() && out.join('');\n  if (num.length === 2) out += '.' + num[1];\n  return out;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32967082fcc3049e92b93", + "creator": "Peter Mortensen", + "createdAt": 1622076778000, + "text": "An explanation would be in order.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f37", + "creator": "Jay Dansand", + "createdAt": 1360172534000, + "text": "

This might be a little late, but here's a method I just worked up for a coworker to add a locale-aware .toCurrencyString() function to all numbers. The internalization is for number grouping only, not the currency sign - if you're outputting dollars, use "$" as supplied, because $123 4567 in Japan or China is the same number of USD as $1,234,567 is in the US. If you're outputting euro, etc., then change the currency sign from "$".

\n

Declare this anywhere in your HTML <head> section or wherever necessary, just before you need to use it:

\n
  Number.prototype.toCurrencyString = function(prefix, suffix) {\n    if (typeof prefix === 'undefined') { prefix = '$'; }\n    if (typeof suffix === 'undefined') { suffix = ''; }\n    var _localeBug = new RegExp((1).toLocaleString().replace(/^1/, '').replace(/\\./, '\\\\.') + "$");\n    return prefix + (~~this).toLocaleString().replace(_localeBug, '') + (this % 1).toFixed(2).toLocaleString().replace(/^[+-]?0+/,'') + suffix;\n  }\n
\n

Then you're done! Use (number).toCurrencyString() anywhere you need to output the number as currency.

\n
var MyNumber = 123456789.125;\nalert(MyNumber.toCurrencyString()); // alerts "$123,456,789.13"\nMyNumber = -123.567;\nalert(MyNumber.toCurrencyString()); // alerts "$-123.57"\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f38", + "creator": "kalisjoshua", + "createdAt": 1363835481000, + "text": "

I based this heavily on the answer from VisioN:

\n
function format (val) {\n  val = (+val).toLocaleString();\n  val = (+val).toFixed(2);\n  val += "";\n  return val.replace(/(\\d)(?=(\\d{3})+(?:\\.\\d+)?$)/g, "$1" + format.thousands);\n}\n\n(function (isUS) {\n  format.decimal =   isUS ? "." : ",";\n  format.thousands = isUS ? "," : ".";\n}(("" + (+(0.00).toLocaleString()).toFixed(2)).indexOf(".") > 0));\n
\n

I tested with inputs:

\n
[   ""\n  , "1"\n  , "12"\n  , "123"\n  , "1234"\n  , "12345"\n  , "123456"\n  , "1234567"\n  , "12345678"\n  , "123456789"\n  , "1234567890"\n  , ".12"\n  , "1.12"\n  , "12.12"\n  , "123.12"\n  , "1234.12"\n  , "12345.12"\n  , "123456.12"\n  , "1234567.12"\n  , "12345678.12"\n  , "123456789.12"\n  , "1234567890.12"\n  , "1234567890.123"\n  , "1234567890.125"\n].forEach(function (item) {\n  console.log(format(item));\n});\n
\n

And got these results:

\n
0.00\n1.00\n12.00\n123.00\n1,234.00\n12,345.00\n123,456.00\n1,234,567.00\n12,345,678.00\n123,456,789.00\n1,234,567,890.00\n0.12\n1.12\n12.12\n123.12\n1,234.12\n12,345.12\n123,456.12\n1,234,567.12\n12,345,678.12\n123,456,789.12\n1,234,567,890.12\n1,234,567,890.12\n1,234,567,890.13\n
\n

Just for fun.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f35", + "creator": "VisioN", + "createdAt": 1358709937000, + "text": "

Short and fast solution (works everywhere!)

\n\n
(12345.67).toFixed(2).replace(/\\d(?=(\\d{3})+\\.)/g, '$&,');  // 12,345.67\n
\n\n

The idea behind this solution is replacing matched sections with first match and comma, i.e. '$&,'. The matching is done using lookahead approach. You may read the expression as \"match a number if it is followed by a sequence of three number sets (one or more) and a dot\".

\n\n

TESTS:

\n\n
1        --> \"1.00\"\n12       --> \"12.00\"\n123      --> \"123.00\"\n1234     --> \"1,234.00\"\n12345    --> \"12,345.00\"\n123456   --> \"123,456.00\"\n1234567  --> \"1,234,567.00\"\n12345.67 --> \"12,345.67\"\n
\n\n

DEMO: http://jsfiddle.net/hAfMM/9571/

\n\n
\n\n

Extended short solution

\n\n

You can also extend the prototype of Number object to add additional support of any number of decimals [0 .. n] and the size of number groups [0 .. x]:

\n\n
/**\n * Number.prototype.format(n, x)\n * \n * @param integer n: length of decimal\n * @param integer x: length of sections\n */\nNumber.prototype.format = function(n, x) {\n    var re = '\\\\d(?=(\\\\d{' + (x || 3) + '})+' + (n > 0 ? '\\\\.' : '$') + ')';\n    return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');\n};\n\n1234..format();           // \"1,234\"\n12345..format(2);         // \"12,345.00\"\n123456.7.format(3, 2);    // \"12,34,56.700\"\n123456.789.format(2, 4);  // \"12,3456.79\"\n
\n\n

DEMO / TESTS: http://jsfiddle.net/hAfMM/435/

\n\n
\n\n

Super extended short solution

\n\n

In this super extended version you may set different delimiter types:

\n\n
/**\n * Number.prototype.format(n, x, s, c)\n * \n * @param integer n: length of decimal\n * @param integer x: length of whole part\n * @param mixed   s: sections delimiter\n * @param mixed   c: decimal delimiter\n */\nNumber.prototype.format = function(n, x, s, c) {\n    var re = '\\\\d(?=(\\\\d{' + (x || 3) + '})+' + (n > 0 ? '\\\\D' : '$') + ')',\n        num = this.toFixed(Math.max(0, ~~n));\n\n    return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ','));\n};\n\n12345678.9.format(2, 3, '.', ',');  // \"12.345.678,90\"\n123456.789.format(4, 4, ' ', ':');  // \"12 3456:7890\"\n12345678.9.format(0, 3, '-');       // \"12-345-679\"\n
\n\n

DEMO / TESTS: http://jsfiddle.net/hAfMM/612/

\n", + "upvotes": 1583, + "upvoterUsernames": [], + "downvotes": 117, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32967082fcc3049e92b97", + "creator": "kalisjoshua", + "createdAt": 1363834212000, + "text": "I actually went a step further: .replace(/(\\d)(?=(\\d{3})+(?:\\.\\d+)?$)/g, "$1,").", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b98", + "creator": "VisioN", + "createdAt": 1395744819000, + "text": "@JuliendePrabère Please give an example of a long number which doesn't work with this approach.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b99", + "creator": "VisioN", + "createdAt": 1434442522000, + "text": "@Gyrocode.com Why $& approach didn't work for you?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b9a", + "creator": "Clain Dsilva", + "createdAt": 1442058882000, + "text": "Why this is not chosen as the best answer? Its has covered all possible options", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b9b", + "creator": "Andres Felipe", + "createdAt": 1449860858000, + "text": "ok awesome code, unfortunately i can't understand many things but it was very useful tks", + "upvotes": 694, + "upvoterUsernames": [], + "downvotes": 694, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b9d", + "creator": "Mohammed Farooq", + "createdAt": 1474357494000, + "text": "Awesome code. Can it be possible to do reverse like un-format (12.345.678,90 to 12345678.9) similarly any currency format to normal number @VisioN", + "upvotes": 96, + "upvoterUsernames": [], + "downvotes": 96, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92b9f", + "creator": "VisioN", + "createdAt": 1474448624000, + "text": "@MohammedFarooq Sure, as easy as this: jsfiddle.net/hAfMM/3934.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92ba0", + "creator": "Matrix", + "createdAt": 1474497956000, + "text": "How can I do to format "12514652" number? I can't call 12514652.format() like ruby... so?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92ba2", + "creator": "Besat", + "createdAt": 1476975289000, + "text": "Wonderful! Thanks!", + "upvotes": 897, + "upvoterUsernames": [], + "downvotes": 897, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92ba4", + "creator": "Faisal", + "createdAt": 1478233456000, + "text": "how to make arr[i].price example, formatted to this? this code work great! @VisioN", + "upvotes": 1151, + "upvoterUsernames": [], + "downvotes": 1151, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92ba6", + "creator": "wilmol", + "createdAt": 1611099520000, + "text": "These inbuilt format functions are inconsistent (require locale data...) so this regex is a lifesaver.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f39", + "creator": "Joseph Lennox", + "createdAt": 1376536971000, + "text": "

This answer meets the following criteria:

\n\n

This code is built on concepts from other answers. Its execution speed should be among the better posted here if that's a concern.

\n
var decimalCharacter = Number("1.1").toLocaleString().substr(1,1);\nvar defaultCurrencyMarker = "$";\nfunction formatCurrency(number, currencyMarker) {\n    if (typeof number != "number")\n        number = parseFloat(number, 10);\n\n    // if NaN is passed in or comes from the parseFloat, set it to 0.\n    if (isNaN(number))\n        number = 0;\n\n    var sign = number < 0 ? "-" : "";\n    number = Math.abs(number);    // so our signage goes before the $ symbol.\n\n    var integral = Math.floor(number);\n    var formattedIntegral = integral.toLocaleString();\n\n    // IE returns "##.00" while others return "##"\n    formattedIntegral = formattedIntegral.split(decimalCharacter)[0];\n\n    var decimal = Math.round((number - integral) * 100);\n    return sign + (currencyMarker || defaultCurrencyMarker) +\n        formattedIntegral  +\n        decimalCharacter +\n        decimal.toString() + (decimal < 10 ? "0" : "");\n}\n
\n

These tests only work on a US locale machine. This decision was made for simplicity and because this could cause of crappy input (bad auto-localization) allowing for crappy output issues.

\n
var tests = [\n    // [ input, expected result ]\n    [123123, "$123,123.00"],    // no decimal\n    [123123.123, "$123,123.12"],    // decimal rounded down\n    [123123.126, "$123,123.13"],    // decimal rounded up\n    [123123.4, "$123,123.40"],    // single decimal\n    ["123123", "$123,123.00"],    // repeat subset of the above using string input.\n    ["123123.123", "$123,123.12"],\n    ["123123.126", "$123,123.13"],\n    [-123, "-$123.00"]    // negatives\n];\n\nfor (var testIndex=0; testIndex < tests.length; testIndex++) {\n    var test = tests[testIndex];\n    var formatted = formatCurrency(test[0]);\n    if (formatted == test[1]) {\n        console.log("Test passed, \\"" + test[0] + "\\" resulted in \\"" + formatted + "\\"");\n    } else {\n        console.error("Test failed. Expected \\"" + test[1] + "\\", got \\"" + formatted + "\\"");\n    }\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f3a", + "creator": "Nick Grealy", + "createdAt": 1380073365000, + "text": "

Works for all current browsers

\n

Use toLocaleString to format a currency in its language-sensitive representation (using ISO 4217 currency codes).

\n
(2500).toLocaleString("en-GB", {style: "currency", currency: "GBP", minimumFractionDigits: 2})\n
\n

Example South African Rand code snippets for avenmore:

\n

\r\n
\r\n
console.log((2500).toLocaleString(\"en-ZA\", {style: \"currency\", currency: \"ZAR\", minimumFractionDigits: 2}))\n// -> R 2 500,00\nconsole.log((2500).toLocaleString(\"en-GB\", {style: \"currency\", currency: \"ZAR\", minimumFractionDigits: 2}))\n// -> ZAR 2,500.00
\r\n
\r\n
\r\n

\n", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32967082fcc3049e92ba8", + "creator": "MSC", + "createdAt": 1467527740000, + "text": "I like this and am happy that it works with Indian digit grouping.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92baa", + "creator": "Evgeny", + "createdAt": 1492184013000, + "text": "This is fully supported as of 2017 and should be the only correct answer", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92bac", + "creator": "Nick Grealy", + "createdAt": 1566262602000, + "text": "@avenmore works for latest versions of Chrome/Firefox/Safari. I hate to ask... but what browser & version are you testing on?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92bae", + "creator": "avenmore", + "createdAt": 1566287965000, + "text": "Latest and greatest :) FF69, Chrome76, etc. "R 2 500,00" is not what we use here, it should be "R 2,500.00", same as en-GB.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32967082fcc3049e92bb0", + "creator": "Tim", + "createdAt": 1571930036000, + "text": "minimumFractionDigits: 2 just what I was looking for!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f3b", + "creator": "Anunay", + "createdAt": 1390299487000, + "text": "

Here is the short and best one to convert numbers into a currency format:

\n
function toCurrency(amount){\n    return amount.replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, "$1,");\n}\n\n// usage: toCurrency(3939920.3030);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32967082fcc3049e92bb1", + "creator": "Peter Mortensen", + "createdAt": 1622107154000, + "text": "How can it be the best one if it produces wrong output?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f3d", + "creator": "Tom", + "createdAt": 1403778415000, + "text": "

The code from Jonathan M looked too complicated for me, so I rewrote it and got about 30% on Firefox v30 and 60% on Chrome v35 speed boost (http://jsperf.com/number-formating2):

\n
Number.prototype.formatNumber = function(decPlaces, thouSeparator, decSeparator) {\n    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces;\n    decSeparator = decSeparator == undefined ? "." : decSeparator;\n    thouSeparator = thouSeparator == undefined ? "," : thouSeparator;\n\n    var n = this.toFixed(decPlaces);\n    if (decPlaces) {\n        var i = n.substr(0, n.length - (decPlaces + 1));\n        var j = decSeparator + n.substr(-decPlaces);\n    } else {\n        i = n;\n        j = '';\n    }\n\n    function reverse(str) {\n        var sr = '';\n        for (var l = str.length - 1; l >= 0; l--) {\n            sr += str.charAt(l);\n        }\n        return sr;\n    }\n\n    if (parseInt(i)) {\n        i = reverse(reverse(i).replace(/(\\d{3})(?=\\d)/g, "$1" + thouSeparator));\n    }\n    return i + j;\n};\n
\n

Usage:

\n
var sum = 123456789.5698;\nvar formatted = '$' + sum.formatNumber(2, ',', '.'); // "$123,456,789.57"\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f3c", + "creator": "Chad Kuehn", + "createdAt": 1400537314000, + "text": "

A function to handle currency output, including negatives.\n

Sample Output:
\n$5.23
\n-$5.23

\n\n
function formatCurrency(total) {\n    var neg = false;\n    if(total < 0) {\n        neg = true;\n        total = Math.abs(total);\n    }\n    return (neg ? \"-$\" : '$') + parseFloat(total, 10).toFixed(2).replace(/(\\d)(?=(\\d{3})+\\.)/g, \"$1,\").toString();\n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f3e", + "creator": "Tomas Kubes", + "createdAt": 1410298085000, + "text": "

I like it simple:

\n\n
function formatPriceUSD(price) {\n    var strPrice = price.toFixed(2).toString();\n    var a = strPrice.split('');\n\n    if (price > 1000000000)\n        a.splice(a.length - 12, 0, ',');\n\n    if (price > 1000000)\n        a.splice(a.length - 9, 0, ',');\n\n    if (price > 1000)\n        a.splice(a.length - 6, 0, ',');\n\n    return '$' + a.join(\"\");\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f3f", + "creator": "iBet7o", + "createdAt": 1410311996000, + "text": "

Intl.NumberFormat

\n
var number = 3500;\nalert(new Intl.NumberFormat().format(number));\n// → "3,500" if in US English locale\n
\n

Or PHP's number_format in JavaScript.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32968082fcc3049e92bb5", + "creator": "Bhargav Nanekalva", + "createdAt": 1435346177000, + "text": "Asked for a JS solution!", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bb7", + "creator": "Peter Mortensen", + "createdAt": 1622108527000, + "text": "@Bhargav Nanekalva: It is actually in JavaScript.", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bb8", + "creator": "Peter Mortensen", + "createdAt": 1622108584000, + "text": "The first link is (effectively) broken: "Page not found"", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f40", + "creator": "Mohamed.Abdo", + "createdAt": 1413976868000, + "text": "

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat\nExample: Using locales

\n\n

This example shows some of the variations in localized number formats. In order to get the format of the language used in the user interface of your application, make sure to specify that language (and possibly some fallback languages) using the locales argument:

\n\n
\n

var number = 123456.789;

\n \n

// German uses comma as decimal separator and period for thousands\n console.log(new Intl.NumberFormat('de-DE').format(number)); // →\n 123.456,789

\n \n

// Arabic in most Arabic speaking countries uses real Arabic digits\n console.log(new Intl.NumberFormat('ar-EG').format(number)); // →\n ١٢٣٤٥٦٫٧٨٩

\n \n

// India uses thousands/lakh/crore separators console.log(new\n Intl.NumberFormat('en-IN').format(number));

\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f41", + "creator": "Daniel Barbalace", + "createdAt": 1415135378000, + "text": "

If amount is a number, say -123, then

\n
amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' });\n
\n

will produce the string "-$123.00".

\n

Here's a complete working example.

\n", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 106, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32968082fcc3049e92bbb", + "creator": "Daniel Barbalace", + "createdAt": 1427391362000, + "text": "The above code does rounding to the number of digits you want. See the example and type in 1.237 in the input box.", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 89, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bbd", + "creator": "Lance Anderson", + "createdAt": 1431055353000, + "text": "Doesn't seem to work in Safari. It just returns the number as a String without any formatting.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bbf", + "creator": "Ethan", + "createdAt": 1500076607000, + "text": "Wow, this is a really great answer. Should be top.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bc1", + "creator": "Horacio", + "createdAt": 1527612622000, + "text": "If for some reason you don't want cents, you can change decimal precision with: minimumFractionDigits: 0", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bc3", + "creator": "Nick Grealy", + "createdAt": 1558190362000, + "text": "@Horacio / Nico - See earlier answer: stackoverflow.com/a/18994850/782034", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f42", + "creator": "mp31415", + "createdAt": 1422536856000, + "text": "

I like the shortest answer by VisionN except when I need to modify it for a number without a decimal point ($123 instead of $123.00). It does not work, so instead of quick copy/paste I need to decipher the arcane syntax of the JavaScript regular expression.

\n

Here is the original solution

\n
n.toFixed(2).replace(/\\d(?=(\\d{3})+\\.)/g, '$&,');\n
\n

I'll make it a bit longer:

\n
var re = /\\d(?=(\\d{3})+\\.)/g;\nvar subst = '$&,';\nn.toFixed(2).replace(re, subst);\n
\n

The re part here (search part in string replace) means

\n
    \n
  1. Find all digits (\\d)
  2. \n
  3. Followed by (?= ...) (lookahead)
  4. \n
  5. One or more groups (...)+
  6. \n
  7. Of exactly three digits (\\d{3})
  8. \n
  9. Ending with a dot (\\.)
  10. \n
  11. Do it for all occurrences (g)
  12. \n
\n

The subst part here means:

\n
    \n
  1. Every time there is a match, replace it with itself ($&), followed by a comma.
  2. \n
\n

As we use string.replace, all other text in the string remains the same and only found digits (those that are followed by 3, 6, 9, etc. other digits) get an additional comma.

\n

So in a number, 1234567.89, digits 1 and 4 meet the condition (1234567.89) and are replaced with "1," and "4," resulting in 1,234,567.89.

\n

If we don't need the decimal point in dollar amount at all (i.e., $123 instead of $123.00), we may change the regular expression like this:

\n
var re2 = /\\d(?=(\\d{3})+$)/g;\n
\n

It relies on the end of line ($) instead of a dot (\\.) and the final expression will be (notice also toFixed(0)):

\n
n.toFixed(0).replace(/\\d(?=(\\d{3})+$)/g, '$&,');\n
\n

This expression will give

\n
1234567.89 -> 1,234,567\n
\n

Also instead of end of line ($) in the regular expression above, you may opt for a word boundary as well (\\b).

\n

My apology in advance if I misinterpreted any part of the regular expression handling.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f43", + "creator": "rab", + "createdAt": 1428325426000, + "text": "

There are already good answers. Here's a simple attempt for fun:

\n
function currencyFormat(no) {\n  var ar = (+no).toFixed(2).split('.');\n  return [\n      numberFormat(ar[0] | 0),\n      '.',\n      ar[1]\n  ].join('');\n}\n\n\nfunction numberFormat(no) {\n  var str = no + '';\n  var ar = [];\n  var i  = str.length -1;\n\n  while(i >= 0) {\n    ar.push((str[i-2] || '') + (str[i-1] || '') + (str[i] || ''));\n    i = i-3;\n  }\n  return ar.reverse().join(',');\n}\n
\n

Then run some examples:

\n
console.log(\n  currencyFormat(1),\n  currencyFormat(1200),\n  currencyFormat(123),\n  currencyFormat(9870000),\n  currencyFormat(12345),\n  currencyFormat(123456.232)\n)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32968082fcc3049e92bc5", + "creator": "Peter Mortensen", + "createdAt": 1622112562000, + "text": "An explanation would be in order. E.g., what is the purpose of ar[0] | 0?", + "upvotes": 134, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f44", + "creator": "Ken Palmer", + "createdAt": 1430752169000, + "text": "

tggagne is correct. My solution below is not good due to float rounding. And the toLocaleString function lacks some browser support. I'll leave the below comments for archival purposes of what not to do. :)

\n

Date.prototype.toLocaleString()

\n

(Old Solution) Use Patrick Desjardins' solution instead.

\n

This is a terse solution that uses toLocaleString(), which has been supported since JavaScript version 1.0. This example designates the currency to U.S. Dollars, but could be switched to pounds by using 'GBP' instead of 'USD'.

\n
var formatMoney = function (value) {\n    // Convert the value to a floating point number in case it arrives as a string.\n    var numeric = parseFloat(value);\n    // Specify the local currency.\n    return numeric.toLocaleString('USD', { style: 'currency', currency: "USD", minimumFractionDigits: 2, maximumFractionDigits: 2 });\n}\n
\n

See Internationalization and localization, Currencies for additional details.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32968082fcc3049e92bc6", + "creator": "stackoverfloweth", + "createdAt": 1433817206000, + "text": "Not consistent across all browsers :/", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bc8", + "creator": "Ken Palmer", + "createdAt": 1433856305000, + "text": "Thanks for the heads-up @godmode. What browsers did you see problems with?", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bca", + "creator": "stackoverfloweth", + "createdAt": 1433858020000, + "text": "Safari 8.0.6 it wasn't trimming decimals, inserting "$", or adding commas", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bcb", + "creator": "tggagne", + "createdAt": 1439991133000, + "text": "Uptick the idea for using toLocaleString(), but downtick for the float. As was commented earlier, currency should never be stored in a float.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32968082fcc3049e92bcc", + "creator": "Ken Palmer", + "createdAt": 1439998986000, + "text": "Thanks @tggagne, you are correct. Bad oversight on my part.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f45", + "creator": "jacob", + "createdAt": 1445533738000, + "text": "

Another way:

\n
function centsToDollaString(x){\n  var cents = x + ""\n  while(cents.length < 4){\n    cents = "0" + cents;\n  }\n  var dollars = cents.substr(0,cents.length - 2)\n  var decimal = cents.substr(cents.length - 2, 2)\n  while(dollars.length % 3 != 0){\n    dollars = "0" + dollars;\n  }\n  str = dollars.replace(/(\\d{3})(?=\\d)/g, "$1" + ",").replace(/^0*(?=.)/, "");\n  return "$" + str + "." + decimal;\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32968082fcc3049e92bcd", + "creator": "Peter Mortensen", + "createdAt": 1619891503000, + "text": "An explanation would be in order. E.g., what is the idea/gist? How is it different from previous answers?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f47", + "creator": "Faysal Haque", + "createdAt": 1465020550000, + "text": "

I found this from: accounting.js. It's very easy and perfectly fits my need.

\n

\r\n
\r\n
// Default usage:\naccounting.formatMoney(12345678); // $12,345,678.00\n\n// European formatting (custom symbol and separators), can also use options object as second parameter:\naccounting.formatMoney(4999.99, \"€\", 2, \".\", \",\"); // €4.999,99\n\n// Negative values can be formatted nicely:\naccounting.formatMoney(-500000, \"£ \", 0); // £ -500,000\n\n// Simple `format` string allows control of symbol position (%v = value, %s = symbol):\naccounting.formatMoney(5318008, { symbol: \"GBP\",  format: \"%v %s\" }); // 5,318,008.00 GBP\n\n// Euro currency symbol to the right\naccounting.formatMoney(5318008, {symbol: \"€\", precision: 2, thousand: \".\", decimal : \",\", format: \"%v%s\"}); // 1.008,00€ 
\r\n
\r\n
\r\n

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f46", + "creator": "Diego Fernando Villarroel Diaz", + "createdAt": 1452290007000, + "text": "

I want to contribute with this:

\n
function toMoney(amount) {\n    neg = amount.charAt(0);\n    amount = amount.replace(/\\D/g, '');\n    amount = amount.replace(/\\./g, '');\n    amount = amount.replace(/\\-/g, '');\n\n    var numAmount = new Number(amount);\n    amount = numAmount.toFixed(0).replace(/./g, function(c, i, a) {\n        return i > 0 && c !== "," && (a.length - i) % 3 === 0 ? "." + c : c;\n    });\n\n    if(neg == '-')\n        return neg + amount;\n    else\n        return amount;\n}\n
\n

This allows you to convert numbers in a text box where you are only supposed to put numbers (consider this scenario).

\n

This is going to clean a textbox where there are only supposed to be numbers, even if you paste a string with numbers and letters or any character

\n
<html>\n<head>\n    <script language=="Javascript">\n        function isNumber(evt) {\n            var theEvent = evt || window.event;\n            var key = theEvent.keyCode || theEvent.which;\n            key = String.fromCharCode(key);\n            if (key.length == 0)\n                return;\n            var regex = /^[0-9\\-\\b]+$/;\n            if (!regex.test(key)) {\n                theEvent.returnValue = false;\n                if (theEvent.preventDefault)\n                    theEvent.preventDefault();\n            }\n        }\n\n        function toMoney(amount) {\n            neg = amount.charAt(0);\n            amount = amount.replace(/\\D/g, '');\n            amount = amount.replace(/\\./g, '');\n            amount = amount.replace(/\\-/g, '');\n\n            var numAmount = new Number(amount);\n            amount = numAmount.toFixed(0).replace(/./g, function(c, i, a) {\n                return i > 0 && c !== "," && (a.length - i) % 3 === 0 ? "." + c : c;\n            });\n\n            if(neg == '-')\n                return neg + amount;\n            else\n                return amount;\n        }\n\n        function clearText(inTxt, newTxt, outTxt) {\n            inTxt = inTxt.trim();\n            newTxt = newTxt.trim();\n            if(inTxt == '' || inTxt == newTxt)\n                return outTxt;\n\n            return inTxt;\n        }\n\n        function fillText(inTxt, outTxt) {\n            inTxt = inTxt.trim();\n            if(inTxt != '')\n                outTxt = inTxt;\n\n            return outTxt;\n        }\n    </script>\n</head>\n\n<body>\n    $ <input name=reca2 id=reca2 type=text value="0" onFocus="this.value = clearText(this.value, '0', '');" onblur="this.value = fillText(this.value, '0'); this.value = toMoney(this.value);" onKeyPress="isNumber(event);" style="width:80px;" />\n</body>\n\n</html>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32969082fcc3049e92bd0", + "creator": "Peter Mortensen", + "createdAt": 1622113870000, + "text": "Is <script language=="Javascript"> valid syntax (not a rhetorical question)?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32969082fcc3049e92bd1", + "creator": "Peter Mortensen", + "createdAt": 1622114696000, + "text": "What is the difference between theEvent.preventDefault and theEvent.preventDefault()? (Used in the same construct)", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f48", + "creator": "James Eames", + "createdAt": 1475202885000, + "text": "

toLocaleString is good, but it doesn't work in all browsers. I usually use currencyFormatter.js (https://osrec.github.io/currencyFormatter.js/). It's pretty lightweight and contains all the currency and locale definitions right out of the box. It's also good at formatting unusually formatted currencies, such as the INR (which groups numbers in lakhs, crores, etc.). Also, there aren't any dependencies!

\n

OSREC.CurrencyFormatter.format(2534234, { currency: 'INR' }); // Returns ₹ 25,34,234.00

\n

OSREC.CurrencyFormatter.format(2534234, { currency: 'EUR' }); // Returns 2.534.234,00 €

\n

OSREC.CurrencyFormatter.format(2534234, { currency: 'EUR', locale: 'fr' }); // Returns 2 534 234,00 €

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f49", + "creator": "synthet1c", + "createdAt": 1476681918000, + "text": "

The following is concise, easy to understand, and doesn't rely on any overly complicated regular expressions.

\n

\r\n
\r\n
function moneyFormat(price, sign = '$') {\n  const pieces = parseFloat(price).toFixed(2).split('')\n  let ii = pieces.length - 3\n  while ((ii-=3) > 0) {\n    pieces.splice(ii, 0, ',')\n  }\n  return sign + pieces.join('')\n}\n\nconsole.log(\n  moneyFormat(100),\n  moneyFormat(1000),\n  moneyFormat(10000.00),\n  moneyFormat(1000000000000000000)\n)
\r\n
\r\n
\r\n

\n

Here is a version with more options in the final output to allow formatting different currencies in different locality formats.

\n

\r\n
\r\n
// higher order function that takes options then a price and will return the formatted price\nconst makeMoneyFormatter = ({\n  sign = '$',\n  delimiter = ',',\n  decimal = '.',\n  append = false,\n  precision = 2,\n  round = true,\n  custom\n} = {}) => value => {\n\n  const e = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]\n\n  value = round\n    ? (Math.round(value * e[precision]) / e[precision])\n    : parseFloat(value)\n\n  const pieces = value\n    .toFixed(precision)\n    .replace('.', decimal)\n    .split('')\n\n  let ii = pieces.length - (precision ? precision + 1 : 0)\n\n  while ((ii-=3) > 0) {\n    pieces.splice(ii, 0, delimiter)\n  }\n\n  if (typeof custom === 'function') {\n    return custom({\n      sign,\n      float: value,\n      value: pieces.join('')\n    })\n  }\n\n  return append\n    ? pieces.join('') + sign\n    : sign + pieces.join('')\n}\n\n// create currency converters with the correct formatting options\nconst formatDollar = makeMoneyFormatter()\nconst formatPound = makeMoneyFormatter({\n  sign: '£',\n  precision: 0\n})\nconst formatEuro = makeMoneyFormatter({\n  sign: '€',\n  delimiter: '.',\n  decimal: ',',\n  append: true\n})\n\nconst customFormat = makeMoneyFormatter({\n  round: false,\n  custom: ({ value, float, sign }) => `SALE:$${value}USD`\n})\n\nconsole.log(\n  formatPound(1000),\n  formatDollar(10000.0066),\n  formatEuro(100000.001),\n  customFormat(999999.555)\n)
\r\n
\r\n
\r\n

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32969082fcc3049e92bd4", + "creator": "synthet1c", + "createdAt": 1479893757000, + "text": "No worries @CharlieDalsass. I would recommend using babel to compile it down to ES5 for production code.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32969082fcc3049e92bd5", + "creator": "user285594", + "createdAt": 1496763704000, + "text": "But how to do Euro currency? 1.000,00 Euro?", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32969082fcc3049e92bd6", + "creator": "synthet1c", + "createdAt": 1496811213000, + "text": "@YumYumYum I added a full example with more formatting options to allow for more flexibility.", + "upvotes": 735, + "upvoterUsernames": [], + "downvotes": 735, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f4a", + "creator": "Daniel Campos", + "createdAt": 1478802754000, + "text": "

I had a hard time finding a simple library to work with date and currency, so I created my own: https://github.com/dericeira/slimFormatter.js

\n

Simple as that:

\n
var number = slimFormatter.currency(2000.54);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f4b", + "creator": "Choylton B. Higginbottom", + "createdAt": 1479854758000, + "text": "

Here's a straightforward formatter in vanilla JavaScript:

\n
function numberFormatter (num) {\n    console.log(num)\n    var wholeAndDecimal = String(num.toFixed(2)).split(".");\n    console.log(wholeAndDecimal)\n    var reversedWholeNumber = Array.from(wholeAndDecimal[0]).reverse();\n    var formattedOutput = [];\n\n    reversedWholeNumber.forEach( (digit, index) => {\n        formattedOutput.push(digit);\n        if ((index + 1) % 3 === 0 && index < reversedWholeNumber.length - 1) {\n            formattedOutput.push(",");\n        }\n    })\n\n    formattedOutput = formattedOutput.reverse().join('') + "." + wholeAndDecimal[1];\n\n    return formattedOutput;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f4d", + "creator": "Nicolas Giszpenc", + "createdAt": 1532964624000, + "text": "

I wanted a vanilla JavaScript solution that automatically returned the decimal portion.

\n
function formatDollar(amount) {\n    var dollar = Number(amount).toLocaleString("us", "currency");\n    // Decimals\n    var arrAmount = dollar.split(".");\n    if (arrAmount.length==2) {\n        var decimal = arrAmount[1];\n        if (decimal.length==1) {\n            arrAmount[1] += "0";\n        }\n    }\n    if (arrAmount.length==1) {\n        arrAmount.push("00");\n    }\n\n    return "$" + arrAmount.join(".");\n}\n\n\nconsole.log(formatDollar("1812.2");\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f4c", + "creator": "Nick", + "createdAt": 1523003830000, + "text": "

Because every problem deserves a one-line solution:

\n
Number.prototype.formatCurrency = function() { return this.toFixed(2).toString().split(/[-.]/).reverse().reduceRight(function (t, c, i) { return (i == 2) ? '-' + t : (i == 1) ? t + c.replace(/(\\d)(?=(\\d{3})+$)/g, '$1,') : t + '.' + c; }, '$'); }\n
\n

This is easy enough to change for different locales. Just change the '$1,' to '$1.' and '.' to ',' to swap , and . in numbers. The currency symbol can be changed by changing the '$' at the end.

\n

Or, if you have ES6, you can just declare the function with default values:

\n
Number.prototype.formatCurrency = function(thou = ',', dec = '.', sym = '$') { return this.toFixed(2).toString().split(/[-.]/).reverse().reduceRight(function (t, c, i) { return (i == 2) ? '-' + t : (i == 1) ? t + c.replace(/(\\d)(?=(\\d{3})+$)/g, '$1' + thou) : t + dec + c; }, sym); }\n\nconsole.log((4215.57).formatCurrency())\n$4,215.57\nconsole.log((4216635.57).formatCurrency('.', ','))\n$4.216.635,57\nconsole.log((4216635.57).formatCurrency('.', ',', "\\u20AC"))\n€4.216.635,57\n
\n

Oh and it works for negative numbers too:

\n
console.log((-6635.574).formatCurrency('.', ',', "\\u20AC"))\n-€6.635,57\nconsole.log((-1066.507).formatCurrency())\n-$1,066.51\n
\n

And of course you don't have to have a currency symbol:

\n
console.log((1234.586).formatCurrency(',','.',''))\n1,234.59\nconsole.log((-7890123.456).formatCurrency(',','.',''))\n-7,890,123.46\nconsole.log((1237890.456).formatCurrency('.',',',''))\n1.237.890,46\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f4e", + "creator": "Adam Pery", + "createdAt": 1547974781000, + "text": "

\r\n
\r\n
Number(value)\r\n        .toFixed(2)\r\n        .replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, \"$1,\")
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f4f", + "creator": "Murtaza Hussain", + "createdAt": 1568885723000, + "text": "

We can also use numeraljs

\n\n
\n

Numbers can be formatted to look like currency, percentages, times, or even plain old numbers with decimal places, thousands, and abbreviations. And you can always create a custom format.

\n
\n\n
var string = numeral(1000).format('0,0');\n// '1,000'\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f50", + "creator": "Vinod Kumar", + "createdAt": 1585265247000, + "text": "

Please try the below code

\n
"250000".replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, '$1,');\n
\n

Ans: 250,000

\n

\"Enter

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f51", + "creator": "pgee70", + "createdAt": 1601854854000, + "text": "

Taking a few of the best rated answers, I combined and made an ECMAScript 2015 (ES6) function that passes ESLint.

\n
export const formatMoney = (\n  amount,\n  decimalCount = 2,\n  decimal = '.',\n  thousands = ',',\n  currencySymbol = '$',\n) => {\n  if (typeof Intl === 'object') {\n    return new Intl.NumberFormat('en-AU', {\n      style: 'currency',\n      currency: 'AUD',\n    }).format(amount);\n  }\n  // Fallback if Intl is not present.\n  try {\n    const negativeSign = amount < 0 ? '-' : '';\n    const amountNumber = Math.abs(Number(amount) || 0).toFixed(decimalCount);\n    const i = parseInt(amountNumber, 10).toString();\n    const j = i.length > 3 ? i.length % 3 : 0;\n    return (\n      currencySymbol +\n      negativeSign +\n      (j ? i.substr(0, j) + thousands : '') +\n      i.substr(j).replace(/(\\d{3})(?=\\d)/g, `$1${thousands}`) +\n      (decimalCount\n        ? decimal +\n          Math.abs(amountNumber - i)\n            .toFixed(decimalCount)\n            .slice(2)\n        : '')\n    );\n  } catch (e) {\n    // eslint-disable-next-line no-console\n    console.error(e);\n  }\n  return amount;\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f52", + "creator": "gadielkalleb", + "createdAt": 1627335342000, + "text": "

just use the native javascript Intl

\n

you just use the options to format its value

\n

\r\n
\r\n
const number = 1233445.5678\nconsole.log(new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(number));
\r\n
\r\n
\r\n

\n

mozilla documentation link

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296a082fcc3049e92bdd", + "creator": "aross", + "createdAt": 1627374950000, + "text": "Do you have anything different from this answer? stackoverflow.com/a/16233919/1000608", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f2f", + "creator": "Ebubekir Dirican", + "createdAt": 1340983056000, + "text": "
String.prototype.toPrice = function () {\n    var v;\n    if (/^\\d+(,\\d+)$/.test(this))\n        v = this.replace(/,/, '.');\n    else if (/^\\d+((,\\d{3})*(\\.\\d+)?)?$/.test(this))\n        v = this.replace(/,/g, \"\");\n    else if (/^\\d+((.\\d{3})*(,\\d+)?)?$/.test(this))\n        v = this.replace(/\\./g, \"\").replace(/,/, \".\");\n    var x = parseFloat(v).toFixed(2).toString().split(\".\"),\n    x1 = x[0],\n    x2 = ((x.length == 2) ? \".\" + x[1] : \".00\"),\n    exp = /^([0-9]+)(\\d{3})/;\n    while (exp.test(x1))\n        x1 = x1.replace(exp, \"$1\" + \",\" + \"$2\");\n    return x1 + x2;\n}\n\nalert(\"123123\".toPrice()); //123,123.00\nalert(\"123123,316\".toPrice()); //123,123.32\nalert(\"12,312,313.33213\".toPrice()); //12,312,313.33\nalert(\"123.312.321,32132\".toPrice()); //123,312,321.32\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a09082fcc3049e92f78", + "creator": "Peter Mortensen", + "createdAt": 1622076267000, + "text": "An explanation would be in order.", + "upvotes": 967, + "upvoterUsernames": [], + "downvotes": 967, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f31", + "creator": "DanielEli", + "createdAt": 1341433555000, + "text": "

CoffeeScript for Patrick's popular answer:

\n
Number::formatMoney = (decimalPlaces, decimalChar, thousandsChar) ->\n  n = this\n  c = decimalPlaces\n  d = decimalChar\n  t = thousandsChar\n  c = (if isNaN(c = Math.abs(c)) then 2 else c)\n  d = (if d is undefined then "." else d)\n  t = (if t is undefined then "," else t)\n  s = (if n < 0 then "-" else "")\n  i = parseInt(n = Math.abs(+n or 0).toFixed(c)) + ""\n  j = (if (j = i.length) > 3 then j % 3 else 0)\n  s + (if j then i.substr(0, j) + t else "") + i.substr(j).replace(/(\\d{3})(?=\\d)/g, "$1" + t) + (if c then d + Math.abs(n - i).toFixed(c).slice(2) else "")\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f30", + "creator": "XML", + "createdAt": 1341020748000, + "text": "

+1 to Jonathan M for providing the original method. Since this is explicitly a currency formatter, I went ahead and added the currency symbol (defaults to '$') to the output, and added a default comma as the thousands separator. If you don't actually want a currency symbol (or thousands separator), just use "" (empty string) as your argument for it.

\n
Number.prototype.formatMoney = function(decPlaces, thouSeparator, decSeparator, currencySymbol) {\n    // check the args and supply defaults:\n    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces;\n    decSeparator = decSeparator == undefined ? "." : decSeparator;\n    thouSeparator = thouSeparator == undefined ? "," : thouSeparator;\n    currencySymbol = currencySymbol == undefined ? "$" : currencySymbol;\n\n    var n = this,\n        sign = n < 0 ? "-" : "",\n        i = parseInt(n = Math.abs(+n || 0).toFixed(decPlaces)) + "",\n        j = (j = i.length) > 3 ? j % 3 : 0;\n\n    return sign + currencySymbol + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\\d{3})(?=\\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(n - i).toFixed(decPlaces).slice(2) : "");\n};\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a09082fcc3049e92f7c", + "creator": "Rich Bradshaw", + "createdAt": 1363968907000, + "text": "The first var is kinda weird, as those variables are already declared in the function declaration. Other than that, thanks!", + "upvotes": 1247, + "upvoterUsernames": [], + "downvotes": 1247, + "downvoterUsernames": [] + }, + { + "_id": "62f32a09082fcc3049e92f7e", + "creator": "Rich Bradshaw", + "createdAt": 1382125126000, + "text": "It's not too bad – the +n || 0 is the only thing that seems a little odd (to me anyway).", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f32a09082fcc3049e92f7f", + "creator": "Rich Bradshaw", + "createdAt": 1382129124000, + "text": "True – didn't catch that one.", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 7, + "commentItems": [ + { + "_id": "62f32924082fcc3049e92ad4", + "creator": "Kyle", + "createdAt": 1337631831000, + "text": "@PhilipWhitehouse what would cause loss of data of less than 2 decimal places?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ad5", + "creator": "einstein", + "createdAt": 1414287945000, + "text": "Don't forget the English language wrap negative currency amounts in parentthesis ($#,##0.00).", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ad6", + "creator": "Testo Testini", + "createdAt": 1423170875000, + "text": "@NickG I was enthusiast of your solution but does not work in IPad, Android and IE7, works only in Mozilla and Opera of what I checked", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ad7", + "creator": "cssyphus", + "createdAt": 1423282461000, + "text": "@TestoTestini (a)NickG See this jsFiddle: jsfiddle.net/v7tws309", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ad8", + "creator": "Bergi", + "createdAt": 1431263899000, + "text": "There's nothing wrong with being generic, is it?", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ad9", + "creator": "aross", + "createdAt": 1499241797000, + "text": "@Amir, that's already in my answer", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f32924082fcc3049e92ada", + "creator": "Mike Aron", + "createdAt": 1606602331000, + "text": "@NickGrealy Holy shittt. Dude i did not know this function. I wrote always my custom functions. Cooooool. THX", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2520202, + "uvac": 2520261 + } + }, + { + "_id": "62f321bb082fcc3049e8fee8", + "title": "How do I detect a click outside an element?", + "title-lowercase": "how do i detect a click outside an element?", + "creator": "Sergio del Amo", + "createdAt": 1222780632000, + "status": "open", + "text": "

I have some HTML menus, which I show completely when a user clicks on the head of these menus. I would like to hide these elements when the user clicks outside the menus' area.

\n\n

Is something like this possible with jQuery?

\n\n
$(\"#menuscontainer\").clickOutsideThisElement(function() {\n    // Hide the menus\n});\n
\n", + "upvotes": 4863, + "upvoterUsernames": [], + "downvotes": 2050, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1567565, + "answers": 86, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90af5", + "creator": "Chris MacDonald", + "createdAt": 1222780830000, + "text": "

Check the window click event target (it should propagate to the window, as long as it's not captured anywhere else), and ensure that it's not any of the menu elements. If it's not, then you're outside your menu.

\n\n

Or check the position of the click, and see if it's contained within the menu area.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90af8", + "creator": "Dennis", + "createdAt": 1246921807000, + "text": "

The other solutions here didn't work for me so I had to use:

\n
if(!$(event.target).is('#foo'))\n{\n    // hide menu\n}\n
\n

Edit: Plain Javascript variant (2021-03-31)

\n

I used this method to handle closing a drop down menu when clicking outside of it.

\n

First, I created a custom class name for all the elements of the component. This class name will be added to all elements that make up the menu widget.

\n
const className = `dropdown-${Date.now()}-${Math.random() * 100}`;\n
\n

I create a function to check for clicks and the class name of the clicked element. If clicked element does not contain the custom class name I generated above, it should set the show flag to false and the menu will close.

\n
const onClickOutside = (e) => {\n  if (!e.target.className.includes(className)) {\n    show = false;\n  }\n};\n
\n

Then I attached the click handler to the window object.

\n
// add when widget loads\nwindow.addEventListener("click", onClickOutside);\n
\n

... and finally some housekeeping

\n
// remove listener when destroying the widget\nwindow.removeEventListener("click", onClickOutside);\n
\n", + "upvotes": 214, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90af7", + "creator": "Joe Lencioni", + "createdAt": 1222798419000, + "text": "

I have an application that works similarly to Eran's example, except I attach the click event to the body when I open the menu... Kinda like this:

\n\n
$('#menucontainer').click(function(event) {\n  $('html').one('click',function() {\n    // Hide the menus\n  });\n\n  event.stopPropagation();\n});\n
\n\n

More information on jQuery's one() function

\n", + "upvotes": 134, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275d082fcc3049e92423", + "creator": "vsync", + "createdAt": 1250527597000, + "text": "but then if you click on the menu itself, then outside, it won't work :)", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90af9", + "creator": "Erik", + "createdAt": 1250258901000, + "text": "

If you are scripting for IE and FF 3.* and you just want to know if the click occured within a certain box area, you could also use something like:

\n

\r\n
\r\n
this.outsideElementClick = function(objEvent, objElement) {\n  var objCurrentElement = objEvent.target || objEvent.srcElement;\n  var blnInsideX = false;\n  var blnInsideY = false;\n\n  if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)\n    blnInsideX = true;\n\n  if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)\n    blnInsideY = true;\n\n  if (blnInsideX && blnInsideY)\n    return false;\n  else\n    return true;\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90afb", + "creator": "Wolfram", + "createdAt": 1270462056000, + "text": "

Now there is a plugin for that: outside events (blog post)

\n\n

The following happens when a clickoutside handler (WLOG) is bound to an element:

\n\n\n\n

So no events are stopped from propagation and additional click handlers may be used \"above\" the element with the outside-handler.

\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90afa", + "creator": "user212621", + "createdAt": 1258438424000, + "text": "
$(\"#menuscontainer\").click(function() {\n    $(this).focus();\n});\n$(\"#menuscontainer\").blur(function(){\n    $(this).hide();\n});\n
\n\n

Works for me just fine.

\n", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275d082fcc3049e92427", + "creator": "OzzyTheGiant", + "createdAt": 1637032681000, + "text": "If trying to use this with a custom built select and options menu, blur will trigger before click so nothing will get selected", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90afc", + "creator": "govind", + "createdAt": 1275506133000, + "text": "

This worked perfectly fine in time for me:

\n\n
$('body').click(function() {\n    // Hide the menus if visible.\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90af6", + "creator": "Eran Galperin", + "createdAt": 1222781891000, + "text": "
\n

Note: Using stopPropagation is something that should be avoided as it breaks normal event flow in the DOM. See this CSS Tricks article for more information. Consider using this method instead.

\n
\n

Attach a click event to the document body which closes the window. Attach a separate click event to the container which stops propagation to the document body.

\n
$(window).click(function() {\n  //Hide the menus if visible\n});\n\n$('#menucontainer').click(function(event){\n  event.stopPropagation();\n});\n
\n", + "upvotes": 2892, + "upvoterUsernames": [], + "downvotes": 923, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3275d082fcc3049e9242b", + "creator": "vsync", + "createdAt": 1250527659000, + "text": "I prefer to bind the document to the click event, then unbind the event when needed. its more efficient.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f3275d082fcc3049e9242c", + "creator": "Art", + "createdAt": 1276329610000, + "text": "This breaks standard behaviour of many things, including buttons and links, contained within #menucontainer. I am surprised this answer is so popular.", + "upvotes": 1530, + "upvoterUsernames": [], + "downvotes": 735, + "downvoterUsernames": [] + }, + { + "_id": "62f3275d082fcc3049e9242d", + "creator": "Eran Galperin", + "createdAt": 1276718120000, + "text": "This doesn't break behavior of anything inside #menucontainer, since it is at the bottom of the propagation chain for anything inside of it.", + "upvotes": 131, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f3275d082fcc3049e9242f", + "creator": "Niamatullah Bakhshi", + "createdAt": 1643002192000, + "text": "@Art is right here. It breaks the behaviour of many things. And I am too surprised how this could be marked as the correct answer.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f3275d082fcc3049e92431", + "creator": "Jai Kumaresh", + "createdAt": 1647607767000, + "text": "@Tom #menucontainer children elements click also listening. I want only outside click listeners events.", + "upvotes": 573, + "upvoterUsernames": [], + "downvotes": 573, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90afe", + "creator": "Chu Yeow", + "createdAt": 1291283592000, + "text": "

I've had success with something like this:

\n\n
var $menuscontainer = ...;\n\n$('#trigger').click(function() {\n  $menuscontainer.show();\n\n  $('body').click(function(event) {\n    var $target = $(event.target);\n\n    if ($target.parents('#menuscontainer').length == 0) {\n      $menuscontainer.hide();\n    }\n  });\n});\n
\n\n

The logic is: when #menuscontainer is shown, bind a click handler to the body that hides #menuscontainer only if the target (of the click) isn't a child of it.

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90afd", + "creator": "Neco", + "createdAt": 1276786685000, + "text": "

Function:

\n\n
$(function() {\n    $.fn.click_inout = function(clickin_handler, clickout_handler) {\n        var item = this;\n        var is_me = false;\n        item.click(function(event) {\n            clickin_handler(event);\n            is_me = true;\n        });\n        $(document).click(function(event) {\n            if (is_me) {\n                is_me = false;\n            } else {\n                clickout_handler(event);\n            }\n        });\n        return this;\n    }\n});\n
\n\n

Usage:

\n\n
this.input = $('<input>')\n    .click_inout(\n        function(event) { me.ShowTree(event); },\n        function() { me.Hide(); }\n    )\n    .appendTo(this.node);\n
\n\n

And functions are very simple:

\n\n
ShowTree: function(event) {\n    this.data_span.show();\n}\nHide: function() {\n    this.data_span.hide();\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275e082fcc3049e92434", + "creator": "Jānis Elmeris", + "createdAt": 1345472939000, + "text": "Wouldn't this trigger the clickout event also in the case when a child element of the container is clicked?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90aff", + "creator": "webenformasyon", + "createdAt": 1294616874000, + "text": "

Use:

\n\n
var go = false;\n$(document).click(function(){\n    if(go){\n        $('#divID').hide();\n        go = false;\n    }\n})\n\n$(\"#divID\").mouseover(function(){\n    go = false;\n});\n\n$(\"#divID\").mouseout(function (){\n    go = true;\n});\n\n$(\"btnID\").click( function(){\n    if($(\"#divID:visible\").length==1)\n        $(\"#divID\").hide(); // Toggle\n    $(\"#divID\").show();\n});\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b02", + "creator": "Rowan", + "createdAt": 1316544258000, + "text": "
$(document).click(function() {\n    $(\".overlay-window\").hide();\n});\n$(\".overlay-window\").click(function() {\n    return false;\n});\n
\n\n

If you click on the document, hide a given element, unless you click on that same element.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b00", + "creator": "34m0", + "createdAt": 1305753319000, + "text": "

I don't think what you really need is to close the menu when the user clicks outside; what you need is for the menu to close when the user clicks anywhere at all on the page. If you click on the menu, or off the menu it should close right?

\n\n

Finding no satisfactory answers above prompted me to write this blog post the other day. For the more pedantic, there are a number of gotchas to take note of:

\n\n
    \n
  1. If you attach a click event handler to the body element at click time be sure to wait for the 2nd click before closing the menu, and unbinding the event. Otherwise the click event that opened the menu will bubble up to the listener that has to close the menu.
  2. \n
  3. If you use event.stopPropogation() on a click event, no other elements in your page can have a click-anywhere-to-close feature.
  4. \n
  5. Attaching a click event handler to the body element indefinitely is not a performant solution
  6. \n
  7. Comparing the target of the event, and its parents to the handler's creator assumes that what you want is to close the menu when you click off it, when what you really want is to close it when you click anywhere on the page.
  8. \n
  9. Listening for events on the body element will make your code more brittle. Styling as innocent as this would break it: body { margin-left:auto; margin-right: auto; width:960px;}
  10. \n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b01", + "creator": "nazar kuliyev", + "createdAt": 1311861997000, + "text": "

I found this method in some jQuery calendar plugin.

\n\n
function ClickOutsideCheck(e)\n{\n  var el = e.target;\n  var popup = $('.popup:visible')[0];\n  if (popup==undefined)\n    return true;\n\n  while (true){\n    if (el == popup ) {\n      return true;\n    } else if (el == document) {\n      $(\".popup\").hide();\n      return false;\n    } else {\n      el = $(el).parent()[0];\n    }\n  }\n};\n\n$(document).bind('mousedown.popup', ClickOutsideCheck);\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b04", + "creator": "benb", + "createdAt": 1324555142000, + "text": "

As another poster said there are a lot of gotchas, especially if the element you are displaying (in this case a menu) has interactive elements.\nI've found the following method to be fairly robust:

\n\n
$('#menuscontainer').click(function(event) {\n    //your code that shows the menus fully\n\n    //now set up an event listener so that clicking anywhere outside will close the menu\n    $('html').click(function(event) {\n        //check up the tree of the click target to check whether user has clicked outside of menu\n        if ($(event.target).parents('#menuscontainer').length==0) {\n            // your code to hide menu\n\n            //this event listener has done its job so we can unbind it.\n            $(this).unbind(event);\n        }\n\n    })\n});\n
\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b03", + "creator": "Satya Prakash", + "createdAt": 1323178962000, + "text": "

I did it like this in YUI 3:

\n\n
// Detect the click anywhere other than the overlay element to close it.\nY.one(document).on('click', function (e) {\n    if (e.target.ancestor('#overlay') === null && e.target.get('id') != 'show' && overlay.get('visible') == true) {\n        overlay.hide();\n    }\n});\n
\n\n

I am checking if ancestor is not the widget element container,
\nif target is not which open the widget/element,
\nif widget/element I want to close is already open (not that important).

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b05", + "creator": "netdjw", + "createdAt": 1331039635000, + "text": "

Here is my code:

\n\n
// Listen to every click\n$('html').click(function(event) {\n    if ( $('#mypopupmenu').is(':visible') ) {\n        if (event.target.id != 'click_this_to_show_mypopupmenu') {\n            $('#mypopupmenu').hide();\n        }\n    }\n});\n\n// Listen to selector's clicks\n$('#click_this_to_show_mypopupmenu').click(function() {\n\n  // If the menu is visible, and you clicked the selector again we need to hide\n  if ( $('#mypopupmenu').is(':visible') {\n      $('#mypopupmenu').hide();\n      return true;\n  }\n\n  // Else we need to show the popup menu\n  $('#mypopupmenu').show();\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b06", + "creator": "Spencer Fry", + "createdAt": 1332347078000, + "text": "

This is my solution to this problem:

\n\n
$(document).ready(function() {\n  $('#user-toggle').click(function(e) {\n    $('#user-nav').toggle();\n    e.stopPropagation();\n  });\n\n  $('body').click(function() {\n    $('#user-nav').hide(); \n  });\n\n  $('#user-nav').click(function(e){\n    e.stopPropagation();\n  });\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b07", + "creator": "Toogy", + "createdAt": 1333980120000, + "text": "
jQuery().ready(function(){\n    $('#nav').click(function (event) {\n        $(this).addClass('activ');\n        event.stopPropagation();\n    });\n\n    $('html').click(function () {\n        if( $('#nav').hasClass('activ') ){\n            $('#nav').removeClass('activ');\n        }\n    });\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b08", + "creator": "Salman A", + "createdAt": 1336036917000, + "text": "

Hook a click event listener on the document. Inside the event listener, you can look at the event object, in particular, the event.target to see what element was clicked:

\n\n
$(document).click(function(e){\n    if ($(e.target).closest(\"#menuscontainer\").length == 0) {\n        // .closest can help you determine if the element \n        // or one of its ancestors is #menuscontainer\n        console.log(\"hide\");\n    }\n});\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b0a", + "creator": "Alexandre T.", + "createdAt": 1353875352000, + "text": "

To be honest, I didn't like any of previous the solutions.

\n\n

The best way to do this, is binding the \"click\" event to the document, and comparing if that click is really outside the element (just like Art said in his suggestion).

\n\n

However, you'll have some problems there: You'll never be able to unbind it, and you cannot have an external button to open/close that element.

\n\n

That's why I wrote this small plugin (click here to link), to simplify these tasks. Could it be simpler?

\n\n
<a id='theButton' href=\"#\">Toggle the menu</a><br/>\n<div id='theMenu'>\n    I should be toggled when the above menu is clicked,\n    and hidden when user clicks outside.\n</div>\n\n<script>\n$('#theButton').click(function(){\n    $('#theMenu').slideDown();\n});\n$(\"#theMenu\").dClickOutside({ ignoreList: $(\"#theButton\") }, function(clickedObj){\n    $(this).slideUp();\n});\n</script>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b09", + "creator": "srinath", + "createdAt": 1338818907000, + "text": "

This worked for me perfectly!!

\n\n
$('html').click(function (e) {\n    if (e.target.id == 'YOUR-DIV-ID') {\n        //do something\n    } else {\n        //do something\n    }\n});\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b0b", + "creator": "mlangenberg", + "createdAt": 1355482514000, + "text": "

Just a warning that using this:

\n\n
$('html').click(function() {\n  // Hide the menus if visible\n});\n\n$('#menucontainer').click(function(event){\n  event.stopPropagation();\n});\n
\n\n

It prevents the Ruby on Rails UJS driver from working properly. For example, link_to 'click', '/url', :method => :delete will not work.

\n\n

This might be a workaround:

\n\n
$('html').click(function() {\n  // Hide the menus if visible\n});\n\n$('#menucontainer').click(function(event){\n  if (!$(event.target).data('method')) {\n    event.stopPropagation();\n  }\n});\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b0c", + "creator": "Bilal Berjawi", + "createdAt": 1357991009000, + "text": "

This should work:

\n\n
$('body').click(function (event) {\n    var obj = $(event.target);\n    obj = obj['context']; // context : clicked element inside body\n    if ($(obj).attr('id') != \"menuscontainer\" && $('#menuscontainer').is(':visible') == true) {\n        //hide menu\n    }\n});\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275f082fcc3049e92442", + "creator": "Mahesh Gaikwad", + "createdAt": 1414583848000, + "text": "$("body > div:not(#dvid)").click(function (e) { //your code });", + "upvotes": 2452, + "upvoterUsernames": [], + "downvotes": 2452, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b0e", + "creator": "teynon", + "createdAt": 1369936279000, + "text": "

One more solution is here:

\n\n

http://jsfiddle.net/zR76D/

\n\n

Usage:

\n\n
<div onClick=\"$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });\">Open / Close Menu</div>\n<div id=\"menu\" style=\"display: none; border: 1px solid #000000; background: #660000;\">I am a menu, whoa is me.</div>\n
\n\n

Plugin:

\n\n
(function($) {\n    var clickOutsideElements = [];\n    var clickListener = false;\n\n    $.fn.clickOutside = function(options, ignoreFirstClick) {\n        var that = this;\n        if (ignoreFirstClick == null) ignoreFirstClick = true;\n\n        if (options != \"disable\") {\n            for (var i in clickOutsideElements) {\n                if (clickOutsideElements[i].element[0] == $(this)[0]) return this;\n            }\n\n            clickOutsideElements.push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != \"function\") ? function() {} : options });\n\n            $(this).on(\"click.clickOutside\", function(event) {\n                for (var i in clickOutsideElements) {\n                    if (clickOutsideElements[i].element[0] == $(this)[0]) {\n                        clickOutsideElements[i].clickDetected = true;\n                    }\n                }\n            });\n\n            if (!clickListener) {\n                if (options != null && typeof(options) == \"function\") {\n                    $('html').click(function() {\n                        for (var i in clickOutsideElements) {\n                            if (!clickOutsideElements[i].clickDetected) {\n                                clickOutsideElements[i].fnc.call(that);\n                            }\n                            if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;\n                        }\n                    });\n                    clickListener = true;\n                }\n            }\n        }\n        else {\n            $(this).off(\"click.clickoutside\");\n            for (var i = 0; i < clickOutsideElements.length; ++i) {\n                if (clickOutsideElements[i].element[0] == $(this)[0]) {\n                    clickOutsideElements.splice(i, 1);\n                }\n            }\n        }\n\n        return this;\n    }\n})(jQuery);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b0d", + "creator": "constrictus", + "createdAt": 1361308310000, + "text": "
 <div class=\"feedbackCont\" onblur=\"hidefeedback();\">\n        <div class=\"feedbackb\" onclick=\"showfeedback();\" ></div>\n        <div class=\"feedbackhide\" tabindex=\"1\"> </div>\n </div>\n\nfunction hidefeedback(){\n    $j(\".feedbackhide\").hide();\n}\n\nfunction showfeedback(){\n    $j(\".feedbackhide\").show();\n    $j(\".feedbackCont\").attr(\"tabindex\",1).focus();\n}\n
\n\n

This is the simplest solution I came up with.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b0f", + "creator": "maday", + "createdAt": 1370016018000, + "text": "

The broadest way to do this is to select everything on the web page except the element where you don't want clicks detected and bind the click event those when the menu is opened.

\n\n

Then when the menu is closed remove the binding.

\n\n

Use .stopPropagation to prevent the event from affecting any part of the menuscontainer.

\n\n
$(\"*\").not($(\"#menuscontainer\")).bind(\"click.OutsideMenus\", function ()\n{\n    // hide the menus\n\n    //then remove all of the handlers\n    $(\"*\").unbind(\".OutsideMenus\");\n});\n\n$(\"#menuscontainer\").bind(\"click.OutsideMenus\", function (event) \n{\n    event.stopPropagation(); \n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b10", + "creator": "kboom", + "createdAt": 1370022571000, + "text": "

The solutions here work fine when only one element is to be managed. If there are multiple elements, however, the problem is much more complicated. Tricks with e.stopPropagation() and all the others will not work.

\n\n

I came up with a solution, and maybe it is not so easy, but it's better than nothing. Have a look:

\n\n
$view.on(\"click\", function(e) {\n\n    if(model.isActivated()) return;\n\n        var watchUnclick = function() {\n            rootView.one(\"mouseleave\", function() {\n                $(document).one(\"click\", function() {\n                    model.deactivate();\n                });\n                rootView.one(\"mouseenter\", function() {\n                    watchUnclick();\n                });\n            });\n        };\n        watchUnclick();\n        model.activate();\n    });\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b12", + "creator": "Roei Bahumi", + "createdAt": 1380537118000, + "text": "

This is a more general solution that allows multiple elements to be watched, and dynamically adding and removing elements from the queue.

\n\n

It holds a global queue (autoCloseQueue) - an object container for elements that should be closed on outside clicks.

\n\n

Each queue object key should be the DOM Element id, and the value should be an object with 2 callback functions:

\n\n
 {onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}\n
\n\n

Put this in your document ready callback:

\n\n
window.autoCloseQueue = {}  \n\n$(document).click(function(event) {\n    for (id in autoCloseQueue){\n        var element = autoCloseQueue[id];\n        if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)\n            console.log('This is a click on an element (or its child element) with  id: ' + id);\n            if (typeof element.onPress == 'function') element.onPress(event, id);\n        } else { //This is a click outside the element\n            console.log('This is a click outside the element with id: ' + id);\n            if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback\n            delete autoCloseQueue[id]; //remove the element from the queue\n        }\n    }\n});\n
\n\n

Then, when the DOM element with id 'menuscontainer' is created, just add this object to the queue:

\n\n
window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b11", + "creator": "mems", + "createdAt": 1370444720000, + "text": "

Instead using flow interruption, blur/focus event or any other tricky technics, simply match event flow with element's kinship:

\n\n
$(document).on(\"click.menu-outside\", function(event){\n    // Test if target and it's parent aren't #menuscontainer\n    // That means the click event occur on other branch of document tree\n    if(!$(event.target).parents().andSelf().is(\"#menuscontainer\")){\n        // Click outisde #menuscontainer\n        // Hide the menus (but test if menus aren't already hidden)\n    }\n});\n
\n\n

To remove click outside event listener, simply:

\n\n
$(document).off(\"click.menu-outside\");\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b13", + "creator": "Webmaster G", + "createdAt": 1384825252000, + "text": "

I ended up doing something like this:

\n\n
$(document).on('click', 'body, #msg_count_results .close',function() {\n    $(document).find('#msg_count_results').remove();\n});\n$(document).on('click','#msg_count_results',function(e) {\n    e.preventDefault();\n    return false;\n});\n
\n\n

I have a close button within the new container for end users friendly UI purposes. I had to use return false in order to not go through. Of course, having an A HREF on there to take you somewhere would be nice, or you could call some ajax stuff instead. Either way, it works ok for me. Just what I wanted.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b14", + "creator": "lan.ta", + "createdAt": 1392633128000, + "text": "

Try this code:

\n\n
if ($(event.target).parents().index($('#searchFormEdit')) == -1 &&\n    $(event.target).parents().index($('.DynarchCalendar-topCont')) == -1 &&\n    (_x < os.left || _x > (os.left + 570) || _y < os.top || _y > (os.top + 155)) &&\n    isShowEditForm) {\n\n    setVisibleEditForm(false);\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b16", + "creator": "Awena", + "createdAt": 1398892312000, + "text": "

This will toggle the Nav menu when you click on/off the element.

\n

\r\n
\r\n
$(document).on('click', function(e) {\n  var elem = $(e.target).closest('#menu'),\n    box = $(e.target).closest('#nav');\n  if (elem.length) {\n    e.preventDefault();\n    $('#nav').toggle();\n  } else if (!box.length) {\n    $('#nav').hide();\n  }\n});
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n<li id=\"menu\">\n  <a></a>\n</li>\n<ul id=\"nav\">\n  <!--Nav will toggle when you Click on Menu(it can be an icon in this example)-->\n  <li class=\"page\"><a>Page1</a></li>\n  <li class=\"page\"><a>Page2</a></li>\n  <li class=\"page\"><a>Page3</a></li>\n  <li class=\"page\"><a>Page4</a></li>\n</ul>
\r\n
\r\n
\r\n

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b15", + "creator": "Yacine Zalouani", + "createdAt": 1394733997000, + "text": "

You can set a tabindex to the DOM element. This will trigger a blur event when the user click outside the DOM element.

\n\n

Demo

\n\n
<div tabindex=\"1\">\n    Focus me\n</div>\n\ndocument.querySelector(\"div\").onblur = function(){\n   console.log('clicked outside')\n}\ndocument.querySelector(\"div\").onfocus = function(){\n   console.log('clicked inside')\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b17", + "creator": "Grim", + "createdAt": 1402404977000, + "text": "

Standard HTML:

\n\n

Surround the menus by a <label> and fetch focus state changes.

\n\n

http://jsfiddle.net/bK3gL/

\n\n

Plus: you can unfold the menu by Tab.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32760082fcc3049e9244f", + "creator": "Grim", + "createdAt": 1402406518000, + "text": "plus: you can unfold the menu by <kbd>tab</kbd>", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32760082fcc3049e92450", + "creator": "Sebastian Simon", + "createdAt": 1617697127000, + "text": "This is invalid HTML.", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b18", + "creator": "KyleMit", + "createdAt": 1404742312000, + "text": "

As a wrapper to this great answer from Art, and to use the syntax originally requested by OP, here's a jQuery extension that can record wether a click occured outside of a set element.

\n\n
$.fn.clickOutsideThisElement = function (callback) {\n    return this.each(function () {\n        var self = this;\n        $(document).click(function (e) {\n            if (!$(e.target).closest(self).length) {\n                callback.call(self, e)\n            }\n        })\n    });\n};\n
\n\n

Then you can call like this:

\n\n
$(\"#menuscontainer\").clickOutsideThisElement(function() {\n    // handle menu toggle\n});\n
\n\n

Here's a demo in fiddle

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b19", + "creator": "Bohdan Lyzanets", + "createdAt": 1406194913000, + "text": "

As a variant:

\n\n
var $menu = $('#menucontainer');\n$(document).on('click', function (e) {\n\n    // If element is opened and click target is outside it, hide it\n    if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {\n        $menu.hide();\n    }\n});\n
\n\n

It has no problem with stopping event propagation and better supports multiple menus on the same page where clicking on a second menu while a first is open will leave the first open in the stopPropagation solution.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b1a", + "creator": "Manish Shrivastava", + "createdAt": 1412775006000, + "text": "

For touch devices like iPad and iPhone we can use this code:

\n\n
$(document).on('touchstart', function (event) {\n    var container = $(\"YOUR CONTAINER SELECTOR\");\n\n    if (!container.is(e.target) &&            // If the target of the click isn't the container...\n        container.has(e.target).length === 0) // ... nor a descendant of the container\n    {\n        container.hide();\n    }\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b1b", + "creator": "shiv", + "createdAt": 1412850346000, + "text": "

Using not():

\n\n
$(\"#id\").not().click(function() {\n    alert('Clicked other that #id');\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b1c", + "creator": "Mahesh Gaikwad", + "createdAt": 1414583873000, + "text": "
$(\"body > div:not(#dvid)\").click(function (e) {\n    //your code\n}); \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e92455", + "creator": "bguiz", + "createdAt": 1414586905000, + "text": "Add a click handler to every other element? You will kill performance. There are much better ways to do this.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b1e", + "creator": "Iman Sedighi", + "createdAt": 1422465860000, + "text": "

Solution1

\n\n

Instead of using event.stopPropagation() which can have some side affects, just define a simple flag variable and add one if condition. I tested this and worked properly without any side affects of stopPropagation:

\n\n
var flag = \"1\";\n$('#menucontainer').click(function(event){\n    flag = \"0\"; // flag 0 means click happened in the area where we should not do any action\n});\n\n$('html').click(function() {\n    if(flag != \"0\"){\n        // Hide the menus if visible\n    }\n    else {\n        flag = \"1\";\n    }\n});\n
\n\n

Solution2

\n\n

With just a simple if condition:

\n\n
$(document).on('click', function(event){\n    var container = $(\"#menucontainer\");\n    if (!container.is(event.target) &&            // If the target of the click isn't the container...\n        container.has(event.target).length === 0) // ... nor a descendant of the container\n    {\n        // Do whatever you want to do when click is outside the element\n    }\n});\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e92457", + "creator": "Alice", + "createdAt": 1582035801000, + "text": "Solution 1 works better, because it handles cases when click target is removed from DOM by the time event propagates to the document.", + "upvotes": 228, + "upvoterUsernames": [], + "downvotes": 228, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b1d", + "creator": "aroykos", + "createdAt": 1418865147000, + "text": "
$(\"html\").click(function(){\n    if($('#info').css(\"opacity\")>0.9) {\n        $('#info').fadeOut('fast');\n    }\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e9245a", + "creator": "Sebastian Simon", + "createdAt": 1617697245000, + "text": "This seems unrelated to the question.", + "upvotes": 115, + "upvoterUsernames": [], + "downvotes": 115, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b20", + "creator": "bbe", + "createdAt": 1437860018000, + "text": "

Upvote for the most popular answer, but add

\n\n
&& (e.target != $('html').get(0)) // ignore the scrollbar\n
\n\n

so, a click on a scroll bar does not [hide or whatever] your target element.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b1f", + "creator": "Scott Richardson", + "createdAt": 1436317546000, + "text": "

We implemented a solution, partly based off a comment from a user above, which works perfectly for us. We use it to hide a search box / results when clicking outside those elements, excluding the element that originally.

\n\n
// HIDE SEARCH BOX IF CLICKING OUTSIDE\n$(document).click(function(event){ \n    // IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON \n    if ($(\"#search-holder\").is(\":visible\") && !$(event.target).is(\"#search-holder *, #search\")) {\n        $(\"#search-holder\").fadeOut('fast');\n        $(\"#search\").removeClass('active');\n    }\n});\n
\n\n

It checks if the search box is already visible first also, and in our case, it's also removing an active class on the hide/show search button.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b21", + "creator": "martinedwards", + "createdAt": 1438702666000, + "text": "

This is a classic case of where a tweak to the HTML would be a better solution. Why not set the click on the elements which don't contain the menu item? Then you don't need to add the propagation.

\n\n
$('.header, .footer, .main-content').click(function() {\n//Hide the menus if visible\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b22", + "creator": "Daniel Tonon", + "createdAt": 1438914281000, + "text": "

Outside click plugin!

\n\n

Usage:

\n\n
$('.target-element').outsideClick(function(event){\n    //code that fires when user clicks outside the element\n    //event = the click event\n    //$(this) = the '.target-element' that is firing this function \n}, '.excluded-element')\n
\n\n

The code for it:

\n\n
(function($) {\n\n//when the user hits the escape key, it will trigger all outsideClick functions\n$(document).on(\"keyup\", function (e) {\n    if (e.which == 27) $('body').click(); //escape key\n});\n\n//The actual plugin\n$.fn.outsideClick = function(callback, exclusions) {\n    var subject = this;\n\n    //test if exclusions have been set\n    var hasExclusions = typeof exclusions !== 'undefined';\n\n    //switches click event with touch event if on a touch device\n    var ClickOrTouchEvent = \"ontouchend\" in document ? \"touchend\" : \"click\";\n\n    $('body').on(ClickOrTouchEvent, function(event) {\n        //click target does not contain subject as a parent\n        var clickedOutside = !$(event.target).closest(subject).length;\n\n        //click target was on one of the excluded elements\n        var clickedExclusion = $(event.target).closest(exclusions).length;\n\n        var testSuccessful;\n\n        if (hasExclusions) {\n            testSuccessful = clickedOutside && !clickedExclusion;\n        } else {\n            testSuccessful = clickedOutside;\n        }\n\n        if(testSuccessful) {\n            callback.call(subject, event);\n        }\n    });\n\n    return this;\n};\n\n}(jQuery));\n
\n\n

Adapted from this answer https://stackoverflow.com/a/3028037/1611058

\n", + "upvotes": 2091, + "upvoterUsernames": [], + "downvotes": 2091, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e9245f", + "creator": "Sebastian Simon", + "createdAt": 1617713597000, + "text": "Please don’t do this ClickOrTouchEvent thing. There are devices that have both click and touch, but you’re only binding one.", + "upvotes": 294, + "upvoterUsernames": [], + "downvotes": 294, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b23", + "creator": "Rameez Rami", + "createdAt": 1446453214000, + "text": "

After research I have found three working solutions (I forgot the page links for reference)

\n\n

First solution

\n\n
<script>\n    //The good thing about this solution is it doesn't stop event propagation.\n\n    var clickFlag = 0;\n    $('body').on('click', function () {\n        if(clickFlag == 0) {\n            console.log('hide element here');\n            /* Hide element here */\n        }\n        else {\n            clickFlag=0;\n        }\n    });\n    $('body').on('click','#testDiv', function (event) {\n        clickFlag = 1;\n        console.log('showed the element');\n        /* Show the element */\n    });\n</script>\n
\n\n

Second solution

\n\n
<script>\n    $('body').on('click', function(e) {\n        if($(e.target).closest('#testDiv').length == 0) {\n           /* Hide dropdown here */\n        }\n    });\n</script>\n
\n\n

Third solution

\n\n
<script>\n    var specifiedElement = document.getElementById('testDiv');\n    document.addEventListener('click', function(event) {\n        var isClickInside = specifiedElement.contains(event.target);\n        if (isClickInside) {\n          console.log('You clicked inside')\n        }\n        else {\n          console.log('You clicked outside')\n        }\n    });\n</script>\n
\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e92461", + "creator": "dbarth", + "createdAt": 1469630968000, + "text": "The third solution is by far the most elegant way of checking. It also doesn't involve any overhead of jQuery. Very nice. It helped a lot. Thanks.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b24", + "creator": "Nitekurs", + "createdAt": 1449819767000, + "text": "
    $('#menucontainer').click(function(e){\n        e.stopPropagation();\n     });\n\n    $(document).on('click',  function(e){\n        // code\n    });\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32761082fcc3049e92464", + "creator": "Sebastian Simon", + "createdAt": 1617713443000, + "text": "Doesn’t add much beyond what the accepted answer already provides.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b25", + "creator": "Jitendra Damor", + "createdAt": 1450151458000, + "text": "

A simple solution for the situation is:

\n\n
$(document).mouseup(function (e)\n{\n    var container = $(\"YOUR SELECTOR\"); // Give you class or ID\n\n    if (!container.is(e.target) &&            // If the target of the click is not the desired div or section\n        container.has(e.target).length === 0) // ... nor a descendant-child of the container\n    {\n        container.hide();\n    }\n});\n
\n\n

The above script will hide the div if outside of the div click event is triggered.

\n\n

You can see the following blog for more information : http://www.codecanal.com/detect-click-outside-div-using-javascript/

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32762082fcc3049e92466", + "creator": "SoliMoli", + "createdAt": 1644843292000, + "text": "The best answer here, GJ :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b27", + "creator": "Yishu Fang", + "createdAt": 1457361425000, + "text": "

Have a try of this:

\n\n
$('html').click(function(e) {\n  if($(e.target).parents('#menuscontainer').length == 0) {\n    $('#menuscontainer').hide();\n  }\n});\n
\n\n

https://jsfiddle.net/4cj4jxy0/

\n\n

But note that this cannot work if the click event cannot reach the html tag. (Maybe other elements have stopPropagation()).

\n", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 127, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b26", + "creator": "wsc", + "createdAt": 1453965869000, + "text": "

\r\n
\r\n
$('html').click(function() {\r\n//Hide the menus if visible\r\n});\r\n\r\n$('#menucontainer').click(function(event){\r\n    event.stopPropagation();\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<html>\r\n <button id='#menucontainer'>Ok</button> \r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 424, + "upvoterUsernames": [], + "downvotes": 424, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b29", + "creator": "Matthew Goodwin", + "createdAt": 1461269089000, + "text": "

For easier use, and more expressive code, I created a jQuery plugin for this:

\n\n
$('div.my-element').clickOut(function(target) { \n    //do something here... \n});\n
\n\n

Note: target is the element the user actually clicked. But callback is still executed in the context of the original element, so you can utilize this as you'd expect in a jQuery callback.

\n\n

Plugin:

\n\n
$.fn.clickOut = function (parent, fn) {\n    var context = this;\n    fn = (typeof parent === 'function') ? parent : fn;\n    parent = (parent instanceof jQuery) ? parent : $(document);\n\n    context.each(function () {\n        var that = this;\n        parent.on('click', function (e) {\n            var clicked = $(e.target);\n            if (!clicked.is(that) && !clicked.parents().is(that)) {\n                if (typeof fn === 'function') {\n                    fn.call(that, clicked);\n                }\n            }\n        });\n\n    });\n    return context;\n};\n
\n\n

By default, the click event listener is placed on the document. However, if you want to limit the event listener scope, you can pass in a jQuery object representing a parent level element that will be the top parent at which clicks will be listened to. This prevents unnecessary document level event listeners. Obviously, it won't work unless the parent element supplied is a parent of your initial element.

\n\n

Use like so:

\n\n
$('div.my-element').clickOut($('div.my-parent'), function(target) { \n    //do something here...\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b28", + "creator": "Qwertiy", + "createdAt": 1460380869000, + "text": "

Subscribe capturing phase of click to handle click on elements which call preventDefault.
\nRetrigger it on document element using the other name click-anywhere.

\n\n
document.addEventListener('click', function (event) {\n  event = $.event.fix(event);\n  event.type = 'click-anywhere';\n  $document.trigger(event);\n}, true);\n
\n\n

Then where you need click outside functionality subscribe on click-anywhere event on document and check if the click was outside of the element you are interested in:

\n\n
$(document).on('click-anywhere', function (event) {\n  if (!$(event.target).closest('#smth').length) {\n    // Do anything you need here\n  }\n});\n
\n\n

Some notes:

\n\n\n", + "upvotes": 550, + "upvoterUsernames": [], + "downvotes": 550, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2a", + "creator": "FDisk", + "createdAt": 1463001220000, + "text": "\n\n
$(document).on('click.menu.hide', function(e){\n  if ( !$(e.target).closest('#my_menu').length ) {\n    $('#my_menu').find('ul').toggleClass('active', false);\n  }\n});\n\n$(document).on('click.menu.show', '#my_menu li', function(e){\n  $(this).find('ul').toggleClass('active');\n});\n
\n\n
div {\n  float: left;\n}\n\nul {\n  padding: 0;\n  position: relative;\n}\nul li {\n  padding: 5px 25px 5px 10px;\n  border: 1px solid silver;\n  cursor: pointer;\n  list-style: none;\n  margin-top: -1px;\n  white-space: nowrap;\n}\nul li ul:before {\n  margin-right: -20px;\n  position: absolute;\n  top: -17px;\n  right: 0;\n  content: \"\\25BC\";\n}\nul li ul li {\n  visibility: hidden;\n  height: 0;\n  padding-top: 0;\n  padding-bottom: 0;\n  border-width: 0 0 1px 0;\n}\nul li ul li:last-child {\n  border: none;\n}\nul li ul.active:before {\n  content: \"\\25B2\";\n}\nul li ul.active li {\n  display: list-item;\n  visibility: visible;\n  height: inherit;\n  padding: 5px 25px 5px 10px;\n}\n
\n\n
<script src=\"https://code.jquery.com/jquery-2.1.4.js\"></script>\n<div>\n  <ul id=\"my_menu\">\n    <li>Menu 1\n      <ul>\n        <li>subMenu 1</li>\n        <li>subMenu 2</li>\n        <li>subMenu 3</li>\n        <li>subMenu 4</li>\n      </ul>\n    </li>\n    <li>Menu 2\n      <ul>\n        <li>subMenu 1</li>\n        <li>subMenu 2</li>\n        <li>subMenu 3</li>\n        <li>subMenu 4</li>\n      </ul>\n    </li>\n    <li>Menu 3</li>\n    <li>Menu 4</li>\n    <li>Menu 5</li>\n    <li>Menu 6</li>\n  </ul>\n</div>\n
\n\n\n\n

Here is jsbin version http://jsbin.com/xopacadeni/edit?html,css,js,output

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2b", + "creator": "Thamaraiselvam", + "createdAt": 1471438821000, + "text": "

To hide fileTreeClass if clicked outside of it

\n\n
 jQuery(document).mouseup(function (e) {\n            var container = $(\".fileTreeClass\");\n            if (!container.is(e.target) // if the target of the click isn't the container...\n                && container.has(e.target).length === 0) // ... nor a descendant of the container\n            {\n                container.hide();\n            }\n        });\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2d", + "creator": "Waheed", + "createdAt": 1477994150000, + "text": "

This might be a better fix for some people.

\n\n
$(\".menu_link\").click(function(){\n    // show menu code\n});\n\n$(\".menu_link\").mouseleave(function(){\n    //hide menu code, you may add a timer for 3 seconds before code to be run\n});\n
\n\n

I know mouseleave does not only mean a click outside, it also means leaving that element's area.

\n\n

Once the menu itself is inside the menu_link element then the menu itself should not be a problem to click on or move on.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2c", + "creator": "froilanq", + "createdAt": 1473449518000, + "text": "

Simple plugin:

\n\n
$.fn.clickOff = function(callback, selfDestroy) {\n    var clicked = false;\n    var parent = this;\n    var destroy = selfDestroy || true;\n\n    parent.click(function() {\n        clicked = true;\n    });\n\n    $(document).click(function(event) {\n        if (!clicked && parent.is(':visible')) {\n            if(callback) callback.call(parent, event)\n        }\n        if (destroy) {\n            //parent.clickOff = function() {};\n            //parent.off(\"click\");\n            //$(document).off(\"click\");\n            parent.off(\"clickOff\");\n        }\n        clicked = false;\n    });\n};\n
\n\n

Use:

\n\n
$(\"#myDiv\").clickOff(function() {\n   alert('clickOff');\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2e", + "creator": "Waltur Buerk", + "createdAt": 1478959064000, + "text": "

I believe the best way of doing it is something like this.

\n\n
$(document).on(\"click\", function(event) {\n  clickedtarget = $(event.target).closest('#menuscontainer');\n  $(\"#menuscontainer\").not(clickedtarget).hide();\n});\n
\n\n

This type of solution could easily be made to work for multiple menus and also menus that are dynamically added through javascript. Basically it just allows you to click anywhere in your document, and checks which element you clicked in, and selects it's closest \"#menuscontainer\". Then it hides all menuscontainers but excludes the one you clicked in.

\n\n

Not sure about exactly how your menus are built, but feel free to copy my code in the JSFiddle. It's a very simple but thoroughly functional menu/modal system. All you need to do is build the html-menus and the code will do the work for you.

\n\n

https://jsfiddle.net/zs6anrn7/

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b31", + "creator": "Dan Philip", + "createdAt": 1492144078000, + "text": "

The event has a property called event.path of the element which is a \"static ordered list of all its ancestors in tree order\". To check if an event originated from a specific DOM element or one of its children, just check the path for that specific DOM element. It can also be used to check multiple elements by logically ORing the element check in the some function.

\n\n

\r\n
\r\n
$(\"body\").click(function() {\r\n  target = document.getElementById(\"main\");\r\n  flag = event.path.some(function(el, i, arr) {\r\n    return (el == target)\r\n  })\r\n  if (flag) {\r\n    console.log(\"Inside\")\r\n  } else {\r\n    console.log(\"Outside\")\r\n  }\r\n});
\r\n
#main {\r\n  display: inline-block;\r\n  background:yellow;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div id=\"main\">\r\n  <ul>\r\n    <li>Test-Main</li>\r\n    <li>Test-Main</li>\r\n    <li>Test-Main</li>\r\n    <li>Test-Main</li>\r\n    <li>Test-Main</li>\r\n  </ul>\r\n</div>\r\n<div id=\"main2\">\r\n  Outside Main\r\n</div>
\r\n
\r\n
\r\n

\n\n

So for your case It should be

\n\n
$(\"body\").click(function() {\n  target = $(\"#menuscontainer\")[0];\n  flag = event.path.some(function(el, i, arr) {\n    return (el == target)\n  });\n  if (!flag) {\n    // Hide the menus\n  }\n});\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b2f", + "creator": "Lucas", + "createdAt": 1486601031000, + "text": "

I know there are a million answers to this question, but I've always been a fan of using HTML and CSS to do most of the work. In this case, z-index and positioning. The simplest way that I have found to do this is as follows:

\n\n

\r\n
\r\n
$(\"#show-trigger\").click(function(){\r\n  $(\"#element\").animate({width: 'toggle'});\r\n  $(\"#outside-element\").show();\r\n});\r\n$(\"#outside-element\").click(function(){\r\n  $(\"#element\").hide();\r\n  $(\"#outside-element\").hide();\r\n});
\r\n
#outside-element {\r\n  position:fixed;\r\n  width:100%;\r\n  height:100%;\r\n  z-index:1;\r\n  display:none;\r\n}\r\n#element {\r\n  display:none;\r\n  padding:20px;\r\n  background-color:#ccc;\r\n  width:300px;\r\n  z-index:2;\r\n  position:relative;\r\n}\r\n#show-trigger {\r\n  padding:20px;\r\n  background-color:#ccc;\r\n  margin:20px auto;\r\n  z-index:2;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div id=\"outside-element\"></div>\r\n<div id=\"element\">\r\n  <div class=\"menu-item\"><a href=\"#1\">Menu Item 1</a></div>\r\n  <div class=\"menu-item\"><a href=\"#2\">Menu Item 1</a></div>\r\n  <div class=\"menu-item\"><a href=\"#3\">Menu Item 1</a></div>\r\n  <div class=\"menu-item\"><a href=\"#4\">Menu Item 1</a></div>\r\n</div>\r\n<div id=\"show-trigger\">Show Menu</div>
\r\n
\r\n
\r\n

\n\n

This creates a safe environment, since nothing is going to get triggered unless the menu is actually open and the z-index protects any of the content within the element from creating any misfires upon being clicked.

\n\n

Additionally, you're not requiring jQuery to cover all of your bases with propagation calls and having to purge all of the inner elements from misfires.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b30", + "creator": "Karthikeyan Ganesan", + "createdAt": 1487971920000, + "text": "
$(document).on(\"click\",function (event)   \n {   \n     console.log(event);\n   if ($(event.target).closest('.element').length == 0)\n     {\n    //your code here\n      if ($(\".element\").hasClass(\"active\"))\n      {\n        $(\".element\").removeClass(\"active\");\n      }\n     }\n });\n
\n\n

Try this coding for getting the solution.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b32", + "creator": "Walt", + "createdAt": 1500338617000, + "text": "

If someone curious here is javascript solution(es6):

\n\n
window.addEventListener('mouseup', e => {\n        if (e.target != yourDiv && e.target.parentNode != yourDiv) {\n            yourDiv.classList.remove('show-menu');\n            //or yourDiv.style.display = 'none';\n        }\n    })\n
\n\n

and es5, just in case:

\n\n
window.addEventListener('mouseup', function (e) {\nif (e.target != yourDiv && e.target.parentNode != yourDiv) {\n    yourDiv.classList.remove('show-menu'); \n    //or yourDiv.style.display = 'none';\n}\n
\n\n

});

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b33", + "creator": "Fabian", + "createdAt": 1501164008000, + "text": "

Here is what I do to solve to problem.

\n\n
$(window).click(function (event) {\n    //To improve performance add a checklike \n    //if(myElement.isClosed) return;\n    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');\n\n    if (isClickedElementChildOfMyBox)\n        return;\n\n    //your code to hide the element \n});\n\nvar isChildOfElement = function (event, selector) {\n    if (event.originalEvent.path) {\n        return event.originalEvent.path[0].closest(selector) !== null;\n    }\n\n    return event.originalEvent.originalTarget.closest(selector) !== null;\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b34", + "creator": "hienbt88", + "createdAt": 1505268710000, + "text": "

This works for me

\n\n
$(\"body\").mouseup(function(e) {\n    var subject = $(\".main-menu\");\n    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {\n        $('.sub-menu').hide();\n    }\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b35", + "creator": "Duannx", + "createdAt": 1509695437000, + "text": "

Here is a simple solution by pure javascript. It is up-to-date with ES6:

\n\n
var isMenuClick = false;\nvar menu = document.getElementById('menuscontainer');\ndocument.addEventListener('click',()=>{\n    if(!isMenuClick){\n       //Hide the menu here\n    }\n    //Reset isMenuClick \n    isMenuClick = false;\n})\nmenu.addEventListener('click',()=>{\n    isMenuClick = true;\n})\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32763082fcc3049e92475", + "creator": "Duannx", + "createdAt": 1510972801000, + "text": "@MortenMoulder: Ya. It's just for attention even though it is actually ES6. But just look at the solution. I think it is good.", + "upvotes": 1485, + "upvoterUsernames": [], + "downvotes": 1485, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b36", + "creator": "Aominé", + "createdAt": 1512826974000, + "text": "

if you just want to display a window when you click on a button and undisp this window when you click outside.( or on the button again ) this bellow work good

\n\n
document.body.onclick = function() { undisp_menu(); };\nvar menu_on = 0;\n\nfunction menu_trigger(event){\n\n    if (menu_on == 0)\n    {\n        // otherwise u will call the undisp on body when \n        // click on the button\n        event.stopPropagation(); \n\n        disp_menu();\n    }\n\n    else{\n        undisp_menu();\n    }\n\n}\n\n\nfunction disp_menu(){\n\n    menu_on = 1;\n    var e = document.getElementsByClassName(\"menu\")[0];\n    e.className = \"menu on\";\n\n}\n\nfunction undisp_menu(){\n\n    menu_on = 0;\n    var e = document.getElementsByClassName(\"menu\")[0];\n    e.className = \"menu\";\n\n}\n
\n\n

don't forget this for the button

\n\n
<div class=\"button\" onclick=\"menu_trigger(event)\">\n\n<div class=\"menu\">\n
\n\n

and the css:

\n\n
.menu{\n    display: none;\n}\n\n.on {\n    display: inline-block;\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b37", + "creator": "Muhammet Can TONBUL", + "createdAt": 1513005333000, + "text": "

If you are using tools like \"Pop-up\", you can use the \"onFocusOut\" event.

\n\n

\r\n
\r\n
window.onload=function(){\r\ndocument.getElementById(\"inside-div\").focus();\r\n}\r\nfunction loseFocus(){\r\nalert(\"Clicked outside\");\r\n}
\r\n
#container{\r\nbackground-color:lightblue;\r\nwidth:200px;\r\nheight:200px;\r\n}\r\n\r\n#inside-div{\r\nbackground-color:lightgray;\r\nwidth:100px;\r\nheight:100px;\r\n\r\n}
\r\n
<div id=\"container\">\r\n<input type=\"text\" id=\"inside-div\" onfocusout=\"loseFocus()\">\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b38", + "creator": "chea sotheara", + "createdAt": 1515469367000, + "text": "
$('#propertyType').on(\"click\",function(e){\n          self.propertyTypeDialog = !self.propertyTypeDialog;\n          b = true;\n          e.stopPropagation();\n          console.log(\"input clicked\");\n      });\n\n      $(document).on('click','body:not(#propertyType)',function (e) {\n          e.stopPropagation();\n          if(b == true)  {\n              if ($(e.target).closest(\"#configuration\").length == 0) {\n                  b = false;\n                  self.propertyTypeDialog = false;\n                  console.log(\"outside clicked\");\n              }\n          }\n        // console.log($(e.target).closest(\"#configuration\").length);\n      });\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b39", + "creator": "Rinto George", + "createdAt": 1515918577000, + "text": "

I have used below script and done with jQuery.

\n\n
jQuery(document).click(function(e) {\n    var target = e.target; //target div recorded\n    if (!jQuery(target).is('#tobehide') ) {\n        jQuery(this).fadeOut(); //if the click element is not the above id will hide\n    }\n})\n
\n\n

Below find the HTML code

\n\n
<div class=\"main-container\">\n<div> Hello I am the title</div>\n<div class=\"tobehide\">I will hide when you click outside of me</div>\n</div>\n
\n\n

You can read the tutorial here

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b3a", + "creator": "Jovanni G", + "createdAt": 1518431961000, + "text": "

I am surprised nobody actually acknowledged focusout event:

\n

\r\n
\r\n
var button = document.getElementById('button');\nbutton.addEventListener('click', function(e){\n  e.target.style.backgroundColor = 'green';\n});\nbutton.addEventListener('focusout', function(e){\n  e.target.style.backgroundColor = '';\n});
\r\n
<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <button id=\"button\">Click</button>\n</body>\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b3b", + "creator": "DaniG2k", + "createdAt": 1520412570000, + "text": "

I just want to make @Pistos answer more apparent since it's hidden in the comments.

\n\n

This solution worked perfectly for me. Plain JS:

\n\n
var elementToToggle = $('.some-element');\n$(document).click( function(event) {\n  if( $(event.target).closest(elementToToggle).length === 0 ) {\n    elementToToggle.hide();\n  }\n});\n
\n\n

in CoffeeScript:

\n\n
elementToToggle = $('.some-element')\n$(document).click (event) ->\n  if $(event.target).closest(elementToToggle).length == 0\n    elementToToggle.hide()\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b3c", + "creator": "Yair Cohen", + "createdAt": 1549896420000, + "text": "

Let's say the div you want to detect if the user clicked outside or inside has an id, for example: \"my-special-widget\".

\n\n

Listen to body click events:

\n\n
document.body.addEventListener('click', (e) => {\n    if (isInsideMySpecialWidget(e.target, \"my-special-widget\")) {\n        console.log(\"user clicked INSIDE the widget\");\n    }\n    console.log(\"user clicked OUTSIDE the widget\");\n});\n\nfunction isInsideMySpecialWidget(elem, mySpecialWidgetId){\n    while (elem.parentElement) {\n        if (elem.id === mySpecialWidgetId) {\n            return true;\n        }\n        elem = elem.parentElement;\n    }\n    return false;\n}\n
\n\n

In this case, you won't break the normal flow of click on some element in your page, since you are not using the \"stopPropagation\" method.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b3e", + "creator": "Marcelo Ribeiro", + "createdAt": 1577471113000, + "text": "

\r\n
\r\n
const button = document.querySelector('button')\r\nconst box = document.querySelector('.box');\r\n\r\nconst toggle = event => {\r\n  event.stopPropagation();\r\n  \r\n  if (!event.target.closest('.box')) {\r\n    console.log('Click outside');\r\n\r\n    box.classList.toggle('active');\r\n\r\n    box.classList.contains('active')\r\n      ? document.addEventListener('click', toggle)\r\n      : document.removeEventListener('click', toggle);\r\n  } else {\r\n    console.log('Click inside');\r\n  }\r\n}\r\n\r\nbutton.addEventListener('click', toggle);
\r\n
.box {\r\n  position: absolute;\r\n  display: none;\r\n  margin-top: 8px;\r\n  padding: 20px;\r\n  background: lightgray;\r\n}\r\n\r\n.box.active {\r\n  display: block;\r\n}
\r\n
<button>Toggle box</button>\r\n\r\n<div class=\"box\">\r\n  <form action=\"\">\r\n    <input type=\"text\">\r\n    <button type=\"button\">Search</button>\r\n  </form>\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b3d", + "creator": "Rivenfall", + "createdAt": 1561476205000, + "text": "

First you have to track wether the mouse is inside or outside your element1, using the mouseenter and mouseleave events.\nThen you can create an element2 which covers the whole screen to detect any clicks, and react accordingly depending on wether you are inside or outside element1.

\n\n

I strongly recommend to handle both initialization and cleanup, and that the element2 is made as temporary as possible, for obvious reasons.

\n\n

In the example below, the overlay is an element positionned somewhere, which can be selected by clicking inside, and unselected by clicking outside.\nThe _init and _release methods are called as part of an automatic initialisation/cleanup process.\nThe class inherits from a ClickOverlay which has an inner and outerElement, don't worry about it. I used outerElement.parentNode.appendChild to avoid conflicts.

\n\n
import ClickOverlay from './ClickOverlay.js'\n\n/* CSS */\n// .unselect-helper {\n//  position: fixed; left: -100vw; top: -100vh;\n//  width: 200vw; height: 200vh;\n// }\n// .selected {outline: 1px solid black}\n\nexport default class ResizeOverlay extends ClickOverlay {\n    _init(_opts) {\n        this.enterListener = () => this.onEnter()\n        this.innerElement.addEventListener('mouseenter', this.enterListener)\n        this.leaveListener = () => this.onLeave()\n        this.innerElement.addEventListener('mouseleave', this.leaveListener)\n        this.selectListener = () => {\n            if (this.unselectHelper)\n                return\n            this.unselectHelper = document.createElement('div')\n            this.unselectHelper.classList.add('unselect-helper')\n            this.unselectListener = () => {\n                if (this.mouseInside)\n                    return\n                this.clearUnselectHelper()\n                this.onUnselect()\n            }\n            this.unselectHelper.addEventListener('pointerdown'\n                , this.unselectListener)\n            this.outerElement.parentNode.appendChild(this.unselectHelper)\n            this.onSelect()\n        }\n        this.innerElement.addEventListener('pointerup', this.selectListener)\n    }\n\n    _release() {\n        this.innerElement.removeEventListener('mouseenter', this.enterListener)\n        this.innerElement.removeEventListener('mouseleave', this.leaveListener)\n        this.innerElement.removeEventListener('pointerup', this.selectListener)\n        this.clearUnselectHelper()\n    }\n\n    clearUnselectHelper() {\n        if (!this.unselectHelper)\n            return\n        this.unselectHelper.removeEventListener('pointerdown'\n            , this.unselectListener)\n        this.unselectHelper.remove()\n        delete this.unselectListener\n        delete this.unselectHelper\n    }\n\n    onEnter() {\n        this.mouseInside = true\n    }\n\n    onLeave() {\n        delete this.mouseInside\n    }\n\n    onSelect() {\n        this.innerElement.classList.add('selected')\n    }\n\n    onUnselect() {\n        this.innerElement.classList.remove('selected')\n    }\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b40", + "creator": "Илья Зеленько", + "createdAt": 1593519912000, + "text": "

2020 solution using native JS API closest method.

\n

\r\n
\r\n
document.addEventListener('click', ({ target }) => {\n  if (!target.closest('#menupop')) {\n    document.querySelector('#menupop').style.display = 'none'\n  }\n})
\r\n
#menupop {\n    width: 300px;\n    height: 300px;\n    background-color: red;\n}
\r\n
<div id=\"menupop\">\nclicking outside will close this\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32764082fcc3049e92480", + "creator": "Jesse Reza Khorasanee", + "createdAt": 1656379923000, + "text": "How does this work sorry? I'm not sure what '.el1' etc are referencing here.", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b3f", + "creator": "JBarros", + "createdAt": 1589895513000, + "text": "

The easiest way: mouseleave(function())

\n\n

More info: https://www.w3schools.com/jquery/jquery_events.asp

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32764082fcc3049e92483", + "creator": "Paul Roub", + "createdAt": 1589919187000, + "text": "@Daniil this is not a link-only answer, though. If the link were removed, the first sentence would still constitute an answer.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b42", + "creator": "online Thomas", + "createdAt": 1601301191000, + "text": "

All of these answers solve the problem, but I would like to contribute with a moders es6 solution that does exactly what is needed. I just hope to make someone happy with this runnable demo.

\n

\r\n
\r\n
window.clickOutSide = (element, clickOutside, clickInside) => {\n  document.addEventListener('click', (event) => {\n    if (!element.contains(event.target)) {\n      if (typeof clickInside === 'function') {\n        clickOutside();\n      }\n    } else {\n      if (typeof clickInside === 'function') {\n        clickInside();\n      }\n    }\n  });\n};\n\nwindow.clickOutSide(document.querySelector('.block'), () => alert('clicked outside'), () => alert('clicked inside'));
\r\n
.block {\n  width: 400px;\n  height: 400px;\n  background-color: red;\n}
\r\n
<div class=\"block\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b41", + "creator": "Mu-Tsun Tsai", + "createdAt": 1598535042000, + "text": "

Still looking for that perfect solution for detecting clicking outside? Look no further! Introducing Clickout-Event, a package that provides universal support for clickout and other similar events, and it works in all scenarios: plain HTML onclickout attributes, .addEventListener('clickout') of vanilla JavaScript, .on('clickout') of jQuery, v-on:clickout directives of Vue.js, you name it. As long as a front-end framework internally uses addEventListener to handle events, Clickout-Event works for it. Just add the script tag anywhere in your page, and it simply works like magic.

\n

HTML attribute

\n
<div onclickout="console.log('clickout detected')">...</div>\n
\n

Vanilla JavaScript

\n
document.getElementById('myId').addEventListener('clickout', myListener);\n
\n

jQuery

\n
$('#myId').on('clickout', myListener);\n
\n

Vue.js

\n
<div v-on:clickout="open=false">...</div>\n
\n

Angular

\n
<div (clickout)="close()">...</div>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b43", + "creator": "aksl", + "createdAt": 1602618949000, + "text": "

this works fine for me. i am not an expert.

\n
$(document).click(function(event) {\n  var $target = $(event.target);\n  if(!$target.closest('#hamburger, a').length &&\n  $('#hamburger, a').is(":visible")) {\n    $('nav').slideToggle();\n  }\n});\n
\n", + "upvotes": 792, + "upvoterUsernames": [], + "downvotes": 792, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b44", + "creator": "Cezar Augusto", + "createdAt": 1604417437000, + "text": "

It's 2020 and you can use event.composedPath()

\n

From: https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath

\n
\n

The composedPath() method of the Event interface returns the event’s path, which is an array of the objects on which listeners will be invoked.

\n
\n

\r\n
\r\n
const target = document.querySelector('#myTarget')\n\ndocument.addEventListener('click', (event) => {\n  const withinBoundaries = event.composedPath().includes(target)\n\n  if (withinBoundaries) {\n    target.innerText = 'Click happened inside element'\n  } else {\n    target.innerText = 'Click happened **OUTSIDE** element'\n  } \n})
\r\n
/* just to make it good looking. you don't need this */\n#myTarget {\n  margin: 50px auto;\n  width: 500px;\n  height: 500px;\n  background: gray;\n  border: 10px solid black;\n}
\r\n
<div id=\"myTarget\">\n  click me (or not!)\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 162, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32765082fcc3049e92489", + "creator": "WorldOfEmre", + "createdAt": 1639061404000, + "text": "that worked thanks a lot!", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b45", + "creator": "axew3", + "createdAt": 1613492813000, + "text": "

I've read all on 2021, but if not wrong, nobody suggested something easy like this, to unbind and remove event. Using two of the above answers and a more little trick so put all in one (could also be added param to the function to pass selectors, for more popups).\nMay it is useful for someone to know that the joke could be done also this way:

\n
<div id="container" style="display:none"><h1>my menu is nice but disappear if i click outside it</h1></div>\n\n<script>\n function printPopup(){\n  $("#container").css({ "display":"block" });\n  var remListener = $(document).mouseup(function (e) {\n   if ($(e.target).closest("#container").length === 0 && (e.target != $('html').get(0))) \n   {\n    //alert('closest call');\n    $("#container").css({ "display":"none" });\n    remListener.unbind('mouseup'); // isn't it?\n   } \n  });\n }\n\n printPopup();\n\n</script>\n
\n

cheers

\n", + "upvotes": 455, + "upvoterUsernames": [], + "downvotes": 455, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b46", + "creator": "tim-mccurrach", + "createdAt": 1618072962000, + "text": "

Use focusout for accessability

\n

There is one answer here that says (quite correctly) that focusing on click events is an accessibility problem since we want to cater for keyboard users. The focusout event is the correct thing to use here, but it can be done much more simply than in the other answer (and in pure javascript too):

\n

A simpler way of doing it:

\n

The 'problem' with using focusout is that if an element inside your dialog/modal/menu loses focus, to something also 'inside' the event will still get fired. We can check that this isn't the case by looking at event.relatedTarget (which tells us what element will have gained focus).

\n
dialog = document.getElementById("dialogElement")\n\ndialog.addEventListener("focusout", function (event) {\n    if (\n        // we are still inside the dialog so don't close\n        dialog.contains(event.relatedTarget) ||\n        // we have switched to another tab so probably don't want to close \n        !document.hasFocus()  \n    ) {\n        return;\n    }\n    dialog.close();  // or whatever logic you want to use to close\n});\n
\n

There is one slight gotcha to the above, which is that relatedTarget may be null. This is fine if the user is clicking outside the dialog, but will be a problem if unless the user clicks inside the dialog and the dialog happens to not be focusable. To fix this you have to make sure to set tabIndex=0 so your dialog is focusable.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32765082fcc3049e9248d", + "creator": "Vladimir Jovanović", + "createdAt": 1620754978000, + "text": "This is the best solution so far, since it takes accessibility into account.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32765082fcc3049e9248f", + "creator": "Juan Lanus", + "createdAt": 1647009931000, + "text": "... or `tabIndex="-1" so it is not inserted in the tabbing sequence", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32765082fcc3049e92490", + "creator": "catwith", + "createdAt": 1656458386000, + "text": "Note: you can't use this for non-focusable elements", + "upvotes": 1170, + "upvoterUsernames": [], + "downvotes": 1170, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b48", + "creator": "Sommelier", + "createdAt": 1625584718000, + "text": "

A way to write in pure JavaScript

\n
let menu = document.getElementById("menu");\n\ndocument.addEventListener("click", function(){\n    // Hide the menus\n    menu.style.display = "none";\n}, false);\n\ndocument.getElementById("menuscontainer").addEventListener("click", function(e){\n    // Show the menus\n    menu.style.display = "block";\n    e.stopPropagation();\n}, false);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b47", + "creator": "Normajean", + "createdAt": 1620717011000, + "text": "

This is the simplest answer I have found to this question:

\n
window.addEventListener('click', close_window = function () {\n  if(event.target !== windowEl){\n    windowEl.style.display = "none";\n    window.removeEventListener('click', close_window, false);\n  }\n});\n
\n

And you will see I named the function "close_window" so that I could remove the event listener when the window closes.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b49", + "creator": "jameshfisher", + "createdAt": 1636126122000, + "text": "

You don't need (much) JavaScript, just the :focus-within selector:

\n\n

\r\n
\r\n
const menuButton = document.querySelector('.menu-button');\nconst sidebar = document.querySelector('.sidebar');\n\nmenuButton.onmousedown = ev => {\n  ev.preventDefault();\n  (sidebar.contains(document.activeElement) ?\n    document.body : sidebar).focus();\n};
\r\n
* { box-sizing: border-box; }\n\n.sidebar {\n  position: fixed;\n  width: 15em;\n  left: -15em;\n  top: 0;\n  bottom: 0;\n  transition: left 0.3s ease-in-out;\n  background-color: #eef;\n  padding: 3em 1em;\n}\n\n.sidebar:focus-within {\n  left: 0;\n}\n\n.sidebar:focus {\n  outline: 0;\n}\n\n.menu-button {\n  position: fixed;\n  top: 0;\n  left: 0;\n  padding: 1em;\n  background-color: #eef;\n  border: 0;\n}\n\nbody {\n  max-width: 30em;\n  margin: 3em;\n}
\r\n
<body tabindex='-1'>\n  <nav class='sidebar' tabindex='-1'>\n    Sidebar content\n    <input type=\"text\"/>\n  </nav>\n  <button class=\"menu-button\">☰</button>\n  Body content goes here, Lorem ipsum sit amet, etc\n</body>
\r\n
\r\n
\r\n

\n", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 149, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b4a", + "creator": "Felix Furtmayr", + "createdAt": 1645562676000, + "text": "

For those who want a short solution to integrate into their JS code - a small library without JQuery:

\n

Usage:

\n
// demo code\nvar htmlElem = document.getElementById('my-element')\nfunction doSomething(){ console.log('outside click') }\n\n// use the lib\nvar removeListener = new elemOutsideClickListener(htmlElem, doSomething);\n\n// deregister on your wished event\n$scope.$on('$destroy', removeListener);\n\n
\n

Here is the lib:

\n
\nfunction elemOutsideClickListener (element, outsideClickFunc, insideClickFunc) {\n   function onClickOutside (e) {\n      var targetEl = e.target; // clicked element\n      do {\n         // click inside\n         if (targetEl === element) {\n            if (insideClickFunc) insideClickFunc();\n            return;\n\n         // Go up the DOM\n         } else {\n            targetEl = targetEl.parentNode;\n         }\n      } while (targetEl);\n\n      // click outside\n      if (!targetEl && outsideClickFunc) outsideClickFunc();\n   }\n\n   window.addEventListener('click', onClickOutside);\n\n   return function () {\n      window.removeEventListener('click', onClickOutside);\n   };\n}\n
\n

I took the code from here and put it in a function:\nhttps://www.w3docs.com/snippets/javascript/how-to-detect-a-click-outside-an-element.html

\n", + "upvotes": 428, + "upvoterUsernames": [], + "downvotes": 428, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c7082fcc3049e90ab2", + "creator": "Ted", + "createdAt": 1320858128000, + "text": "Here's a sample of this strategy: jsfiddle.net/tedp/aL7Xe/1", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + }, + { + "_id": "62f321c7082fcc3049e90ab3", + "creator": "Rohit Kumar", + "createdAt": 1436298181000, + "text": "get a reference to the element and then event.target, and finally != or == both of them then execute code accordingly..", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c7082fcc3049e90ab4", + "creator": "Juan Lanus", + "createdAt": 1647010139000, + "text": "All answers based on a click event fail to work when the click happens in another window or application", + "upvotes": 6558, + "upvoterUsernames": [], + "downvotes": 6558, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1572431, + "uvac": 1572517 + } + }, + { + "_id": "62f321bb082fcc3049e8fecc", + "title": "How do I check for an empty/undefined/null string in JavaScript?", + "title-lowercase": "how do i check for an empty/undefined/null string in javascript?", + "creator": "casademora", + "createdAt": 1222795065000, + "status": "open", + "text": "

Is there a string.Empty in JavaScript, or is it just a case of checking for ""?

\n", + "upvotes": 4518, + "upvoterUsernames": [], + "downvotes": 865, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 4589621, + "answers": 49, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e905b8", + "creator": "bdukes", + "createdAt": 1222795214000, + "text": "

Empty string, undefined, null, ...

\n

To check for a truthy value:

\n
if (strValue) {\n    // strValue was non-empty string, true, 42, Infinity, [], ...\n}\n
\n

To check for a falsy value:

\n
if (!strValue) {\n    // strValue was empty string, false, 0, null, undefined, ...\n}\n
\n
\n

Empty string (only!)

\n

To check for exactly an empty string, compare for strict equality against "" using the === operator:

\n
if (strValue === "") {\n    // strValue was empty string\n}\n
\n

To check for not an empty string strictly, use the !== operator:

\n
if (strValue !== "") {\n    // strValue was not an empty string\n}\n
\n", + "upvotes": 8892, + "upvoterUsernames": [], + "downvotes": 4405, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a4e", + "creator": "rashidnk", + "createdAt": 1525425862000, + "text": "str=0; if(str) retun false", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a50", + "creator": "Mike Brockington", + "createdAt": 1626945002000, + "text": "Surprised not to see any mention of "falsy values" here...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ba", + "creator": "cllpse", + "createdAt": 1222796552000, + "text": "
var s; // undefined\nvar s = \"\"; // \"\"\ns.length // 0\n
\n\n

There's nothing representing an empty string in JavaScript. Do a check against either length (if you know that the var will always be a string) or against \"\"

\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b9", + "creator": "Chris Noe", + "createdAt": 1222795257000, + "text": "

I would not worry too much about the most efficient method. Use what is most clear to your intention. For me that's usually strVar == \"\".

\n\n

As per the comment from Constantin, if strVar could some how end up containing an integer 0 value, then that would indeed be one of those intention-clarifying situations.

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a52", + "creator": "Constantin", + "createdAt": 1222802480000, + "text": "Bad idea. You'll get true if strVar is accidentally assigned 0.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a53", + "creator": "Valentin H", + "createdAt": 1451224623000, + "text": "The check fails if undefined. So if(str) works better", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a55", + "creator": "Brent Bradburn", + "createdAt": 1516310356000, + "text": "Which equals operator (== vs ===) should be used in JavaScript comparisons?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905bb", + "creator": "Ates Goral", + "createdAt": 1222802252000, + "text": "

The closest thing you can get to str.Empty (with the precondition that str is a String) is:

\n\n
if (!str.length) { ...\n
\n", + "upvotes": 180, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a58", + "creator": "Pic Mickael", + "createdAt": 1536673835000, + "text": "Wouldn't that throw an exception is str is null?", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a5a", + "creator": "Ates Goral", + "createdAt": 1536722743000, + "text": "@PicMickael Yes! So would str.Empty.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a5b", + "creator": "Flimm", + "createdAt": 1628694928000, + "text": "Note that strings aren't the only type of variable that have a length attribute. Arrays do as well.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905bc", + "creator": "Sugendran", + "createdAt": 1222816112000, + "text": "

If you need to make sure that the string is not just a bunch of empty spaces (I'm assuming this is for form validation) you need to do a replace on the spaces.

\n\n
if(str.replace(/\\s/g,\"\") == \"\"){\n}\n
\n", + "upvotes": 165, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a5c", + "creator": "flash", + "createdAt": 1287741746000, + "text": "But does the job if what you actually want to test for is a string with non-space content. Is there a less-expensive way to test this?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a5e", + "creator": "driAn", + "createdAt": 1289483834000, + "text": "How about the length property?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a60", + "creator": "Luke101", + "createdAt": 1341509408000, + "text": "+1 I tried this method and it worked for me. The user may enter blank spaces which is still blank.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a62", + "creator": "vhanla", + "createdAt": 1347924193000, + "text": "if(str.replace(/^\\s+|\\s+$/g,"")){...}+ would be enough", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905bd", + "creator": "jmc734", + "createdAt": 1266202009000, + "text": "

I usually use something like:

\n\n
if (str == \"\") {\n     //Do Something\n}\nelse {\n     //Do Something Else\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905be", + "creator": "oem", + "createdAt": 1275317858000, + "text": "

You could also go with regular expressions:

\n\n
if((/^\\s*$/).test(str)) { }\n
\n\n

Checks for strings that are either empty or filled with whitespace.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a66", + "creator": "Orpheus", + "createdAt": 1431362320000, + "text": "It works, but it's also horribly expensive ops-wise. Good if you just want to check one or two things, not a large set.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905bf", + "creator": "Jet", + "createdAt": 1278701560000, + "text": "

I use:

\n
function empty(e) {\n  switch (e) {\n    case "":\n    case 0:\n    case "0":\n    case null:\n    case false:\n    case undefined:\n      return true;\n    default:\n      return false;\n  }\n}\n\nempty(null) // true\nempty(0) // true\nempty(7) // false\nempty("") // true\nempty((function() {\n    return ""\n})) // false\n
\n", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a69", + "creator": "xarlymg89", + "createdAt": 1516874812000, + "text": "I'd go even a bit further, and nail it with a === operator for the undefined case. Otherwise it's just simply the perfect answer.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a6b", + "creator": "Boris J.", + "createdAt": 1585337660000, + "text": "Could we use case undefined: instead of case typeof(e) == "undefined":?", + "upvotes": 1864, + "upvoterUsernames": [], + "downvotes": 1864, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a6d", + "creator": "Joseph Hamilton", + "createdAt": 1628101612000, + "text": "Just goes to show how ludicrous this beautiful language is :(", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a6f", + "creator": "Scotty Jamison", + "createdAt": 1652551461000, + "text": "@BorisJ. yes, using case undefined: would be a correct fix for this bug. (I edited the answer to use this instead).", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c0", + "creator": "Jano González", + "createdAt": 1279244758000, + "text": "

For checking if a variable is falsey or if it has length attribute equal to zero (which for a string, means it is empty), I use:

\n
function isEmpty(str) {\n    return (!str || str.length === 0 );\n}\n
\n

(Note that strings aren't the only variables with a length attribute, arrays have them as well, for example.)

\n

Alternativaly, you can use the (not so) newly optional chaining and arrow functions to simplify:

\n
const isEmpty = (str) => (!str?.length);\n
\n

It will check the length, returning undefined in case of a nullish value, without throwing an error. In the case of an empty value, zero is falsy and the result is still valid.

\n

For checking if a variable is falsey or if the string only contains whitespace or is empty, I use:

\n
function isBlank(str) {\n    return (!str || /^\\s*$/.test(str));\n}\n
\n

If you want, you can monkey-patch the String prototype like this:

\n
String.prototype.isEmpty = function() {\n    // This doesn't work the same way as the isEmpty function used \n    // in the first example, it will return true for strings containing only whitespace\n    return (this.length === 0 || !this.trim());\n};\nconsole.log("example".isEmpty());\n
\n

Note that monkey-patching built-in types are controversial, as it can break code that depends on the existing structure of built-in types, for whatever reason.

\n", + "upvotes": 2373, + "upvoterUsernames": [], + "downvotes": 1001, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a72", + "creator": "Vincent", + "createdAt": 1379923901000, + "text": "why 0 === str.length instead of str.length === 0 ?", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a74", + "creator": "grandouassou", + "createdAt": 1379930463000, + "text": "I forgot to mention that as you mostly use '===' or '!==' you are less exposed to that kind of mistake.", + "upvotes": 3290, + "upvoterUsernames": [], + "downvotes": 3290, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a76", + "creator": "khebbie", + "createdAt": 1417161996000, + "text": "I like that, and I added this to the String prototype String.prototype.isEmpty = function () { return (!this || 0 === this.length); };", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a78", + "creator": "Schadenfreude", + "createdAt": 1434717324000, + "text": "/^\\s*$/.test(str) can be replaced with str.trim().length === 0", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a7a", + "creator": "Harsh Pandey", + "createdAt": 1560281377000, + "text": "How is return (this.length === 0 || !this.trim()) any better than just return !this.trim()?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a7c", + "creator": "Giorgi Moniava", + "createdAt": 1563193090000, + "text": "@Jano Gonzales In first version, why not just use function isEmpty(str) { return !str; }?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a7e", + "creator": "Flimm", + "createdAt": 1628694760000, + "text": "Empty strings are falsey, so there's the second part of the condition isn't really needed: (! str || str.length == 0)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a7f", + "creator": "johnw182", + "createdAt": 1650667424000, + "text": "This is the best answer and handles every scenario I can test against it! (the isBlank() one)", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c1", + "creator": "Doug", + "createdAt": 1279812653000, + "text": "

Try this

\n\n
str.value.length == 0\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a82", + "creator": "AndFisher", + "createdAt": 1481713036000, + "text": """.value.length will cause an error. It should be str.length === 0", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a83", + "creator": "alexandre-rousseau", + "createdAt": 1568185700000, + "text": "This trow a TypeError If str is equal to undefined or null", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c3", + "creator": "karthick.sk", + "createdAt": 1301492062000, + "text": "

All the previous answers are good, but this will be even better. Use dual NOT operators (!!):

\n
if (!!str) {\n    // Some code here\n}\n
\n

Or use type casting:

\n
if (Boolean(str)) {\n    // Code here\n}\n
\n

Both do the same function. Typecast the variable to Boolean, where str is a variable.

\n\n", + "upvotes": 569, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a86", + "creator": "Peter Olson", + "createdAt": 1419013684000, + "text": "Is there any difference between the behavior of if(str) and if(!!str)?", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a87", + "creator": "Dario Oddenino", + "createdAt": 1455188174000, + "text": "This is the solution I always use. !!str.trim() to make sure the string is not made of whitespaces only.", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a89", + "creator": "shinzou", + "createdAt": 1476739681000, + "text": "Not not looks like a hack, Boolean(str) is a lot more readable and less "wtfish".", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a8b", + "creator": "Oliver Spryn", + "createdAt": 1492190877000, + "text": "Note that !! is not an operator. It is one operator applied twice.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a8d", + "creator": "johnw182", + "createdAt": 1650667387000, + "text": "This doesnt work with string whitespace. Adding Darias suggestion doesnt work on nulls.", + "upvotes": 1188, + "upvoterUsernames": [], + "downvotes": 1188, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c2", + "creator": "Muhammad Salman", + "createdAt": 1281112086000, + "text": "
function tell()\n{\n    var pass = document.getElementById('pasword').value;\n    var plen = pass.length;\n\n    // Now you can check if your string is empty as like\n    if(plen==0)\n    {\n        alert('empty');\n    }\n    else\n    {\n        alert('you entered something');\n    }\n}\n\n<input type='text' id='pasword' />\n
\n\n

This is also a generic way to check if field is empty.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905c5", + "creator": "mricci", + "createdAt": 1342663200000, + "text": "

Ignoring whitespace strings, you could use this to check for null, empty and undefined:

\n\n
var obj = {};\n(!!obj.str) // Returns false\n\nobj.str = \"\";\n(!!obj.str) // Returns false\n\nobj.str = null;\n(!!obj.str) // Returns false\n
\n\n

It is concise and it works for undefined properties, although it's not the most readable.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905c4", + "creator": "Will", + "createdAt": 1306942833000, + "text": "

I use a combination, and the fastest checks are first.

\n
function isBlank(pString) {\n    if (!pString) {\n        return true;\n    }\n    // Checks for a non-white space character\n    // which I think [citation needed] is faster\n    // than removing all the whitespace and checking\n    // against an empty string\n    return !/[^\\s]+/.test(pString);\n}\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fe082fcc3049e91a91", + "creator": "Will", + "createdAt": 1625497564000, + "text": "I didn't see this comment until a decade later. But yes, you're right, I'll update. :)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a92", + "creator": "Nicholi", + "createdAt": 1625588214000, + "text": "Combining our knowledge 1 decade at a time :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fe082fcc3049e91a94", + "creator": "Will", + "createdAt": 1626483194000, + "text": "@Nicholi see you in 2031!", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c6", + "creator": "Bikush", + "createdAt": 1343743504000, + "text": "

I have not noticed an answer that takes into account the possibility of null characters in a string. For example, if we have a null character string:

\n\n
var y = \"\\0\"; // an empty string, but has a null character\n(y === \"\") // false, testing against an empty string does not work\n(y.length === 0) // false\n(y) // true, this is also not expected\n(y.match(/^[\\s]*$/)) // false, again not wanted\n
\n\n

To test its nullness one could do something like this:

\n\n
String.prototype.isNull = function(){ \n  return Boolean(this.match(/^[\\0]*$/)); \n}\n...\n\"\\0\".isNull() // true\n
\n\n

It works on a null string, and on an empty string and it is accessible for all strings. In addition, it could be expanded to contain other JavaScript empty or whitespace characters (i.e. nonbreaking space, byte order mark, line/paragraph separator, etc.).

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905c7", + "creator": "dkinzer", + "createdAt": 1355869877000, + "text": "

It's a good idea too to check that you are not trying to pass an undefined term.

\n\n
function TestMe() {\n  if((typeof str != 'undefined') && str) {\n    alert(str);\n  }\n };\n\nTestMe();\n\nvar str = 'hello';\n\nTestMe();\n
\n\n

I usually run into the case where I want to do something when a string attribute for an object instance is not empty. Which is fine, except that attribute is not always present.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905c8", + "creator": "Yang Dong", + "createdAt": 1357700090000, + "text": "

Try:

\n\n
if (str && str.trim().length) {  \n    //...\n}\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ff082fcc3049e91a97", + "creator": "devildelta", + "createdAt": 1551853977000, + "text": "str.trim().length will do faster than str.trim(), by around 1% according to my own testing result.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905c9", + "creator": "GibboK", + "createdAt": 1360491648000, + "text": "

An alternative way, but I believe bdukes's answer is best.

\n\n
var myString = 'hello'; \nif(myString.charAt(0)){\n    alert('no empty');\n}\nalert('empty');\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905ca", + "creator": "Andron", + "createdAt": 1368033340000, + "text": "

All these answers are nice.

\n\n

But I cannot be sure that variable is a string, doesn't contain only spaces (this is important for me), and can contain '0' (string).

\n\n

My version:

\n\n
function empty(str){\n    return !str || !/[^\\s]+/.test(str);\n}\n\nempty(null); // true\nempty(0); // true\nempty(7); // false\nempty(\"\"); // true\nempty(\"0\"); // false\nempty(\"  \"); // true\n
\n\n

Sample on jsfiddle.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ff082fcc3049e91a9a", + "creator": "Bennett McElwee", + "createdAt": 1396392286000, + "text": "Huh? If you are expecting a string, empty(0) and empty(7) should return the same value.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905cb", + "creator": "Wab_Z", + "createdAt": 1368629715000, + "text": "

Also, in case you consider a whitespace filled string as \"empty\".

\n\n

You can test it with this regular expression:

\n\n
!/\\S/.test(string); // Returns true if blank.\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905cc", + "creator": "Mubashar", + "createdAt": 1372824966000, + "text": "

I prefer to use not blank test instead of blank

\n\n
function isNotBlank(str) {\n   return (str && /^\\s*$/.test(str));\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905cd", + "creator": "Kev", + "createdAt": 1373885744000, + "text": "

Don't assume that the variable you check is a string. Don't assume that if this var has a length, then it's a string.

\n\n

The thing is: think carefully about what your app must do and can accept. Build something robust.

\n\n

If your method / function should only process a non empty string then test if the argument is a non empty string and don't do some 'trick'.

\n\n

As an example of something that will explode if you follow some advices here not carefully.

\n\n
\nvar getLastChar = function (str) {\n if (str.length > 0)\n   return str.charAt(str.length - 1)\n}\n\ngetLastChar('hello')\n=> \"o\"\n\ngetLastChar([0,1,2,3])\n=> TypeError: Object [object Array] has no method 'charAt'\n\n
\n\n

So, I'd stick with

\n\n
\nif (myVar === '')\n  ...\n\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ff082fcc3049e91a9e", + "creator": "Flimm", + "createdAt": 1628695432000, + "text": "This answer should be pinned to the top.", + "upvotes": 3244, + "upvoterUsernames": [], + "downvotes": 3244, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ce", + "creator": "user2086641", + "createdAt": 1376043050000, + "text": "

I usually use something like this,

\n\n
if (!str.length) {\n    // Do something\n}\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ff082fcc3049e91aa0", + "creator": "Adrian Hope-Bailie", + "createdAt": 1392891403000, + "text": "Fastest if you know that the variable is a string. Throws an error if the variable is undefined.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324ff082fcc3049e91aa2", + "creator": "Abimael Martell", + "createdAt": 1396311517000, + "text": "@AdrianHope-Bailie why would you test an undefined variable?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d0", + "creator": "Gaurav", + "createdAt": 1396949822000, + "text": "
var x =\"  \";\nvar patt = /^\\s*$/g;\nisBlank = patt.test(x);\nalert(isBlank); // Is it blank or not??\nx = x.replace(/\\s*/g, \"\"); // Another way of replacing blanks with \"\"\nif (x===\"\"){\n    alert(\"ya it is blank\")\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905cf", + "creator": "T.Todua", + "createdAt": 1393533088000, + "text": "

Very generic "All-In-One" Function (not recommended though):

\n
function is_empty(x)\n{\n    return (                                                           //don't put newline after return\n        (typeof x == 'undefined')\n              ||\n        (x == null)\n              ||\n        (x == false)        //same as: !x\n              ||\n        (x.length == 0)\n              ||\n        (x == 0)            // note this line, you might not need this. \n              ||\n        (x == "")\n              ||\n        (x.replace(/\\s/g,"") == "")\n              ||\n        (!/[^\\s]/.test(x))\n              ||\n        (/^\\s*$/.test(x))\n    );\n}\n
\n

However, I don't recommend to use that, because your target variable should be of specific type (i.e. string, or numeric, or object?), so apply the checks that are relative to that variable.

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ff082fcc3049e91aa5", + "creator": "DanV", + "createdAt": 1395404000000, + "text": "Any chance you could explain what each check is doing? :)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324ff082fcc3049e91aa7", + "creator": "Bennett McElwee", + "createdAt": 1396391984000, + "text": "-1 They are testing for different things. It makes no sense to put them all into one if statement.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d1", + "creator": "Sazid", + "createdAt": 1399358280000, + "text": "

You should always check for the type too, since JavaScript is a duck typed language, so you may not know when and how the data changed in the middle of the process. So, here's the better solution:

\n

\r\n
\r\n
    let undefinedStr;\n    if (!undefinedStr) {\n      console.log(\"String is undefined\");\n    }\n    \n    let emptyStr = \"\";\n    if (!emptyStr) {\n      console.log(\"String is empty\");\n    }\n    \n    let nullStr = null;\n    if (!nullStr) {\n      console.log(\"String is null\");\n    }
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91aa8", + "creator": "Flimm", + "createdAt": 1628695524000, + "text": "The first condition works for any falsey value, not just undefined. This answer is not checking for type.", + "upvotes": 1622, + "upvoterUsernames": [], + "downvotes": 1622, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d2", + "creator": "Alban Kaperi", + "createdAt": 1405793750000, + "text": "

To check if it is empty:

\n
var str = "Hello World!";\nif(str === ''){alert("THE string str is EMPTY");}\n
\n

To check if it is of type string:

\n
var str = "Hello World!";\nif(typeof(str) === 'string'){alert("This is a String");}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905d3", + "creator": "Josef.B", + "createdAt": 1412146513000, + "text": "

If one needs to detect not only empty but also blank strings, I'll add to Goral's answer:

\n\n
function isEmpty(s){\n    return !s.length;    \n}\n\nfunction isBlank(s){\n    return isEmpty(s.trim());    \n}\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905d4", + "creator": "Timothy Nwanwene", + "createdAt": 1424792322000, + "text": "
    \n
  1. check that var a; exist
  2. \n
  3. trim out the false spaces in the value, then test for emptiness

    \n\n
    if ((a)&&(a.trim()!=''))\n{\n  // if variable a is not empty do this \n}\n
  4. \n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91aac", + "creator": "Flimm", + "createdAt": 1628695124000, + "text": "The string " " is not empty, but it would be considered empty by this condition.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d5", + "creator": "Thaddeus Albers", + "createdAt": 1433925891000, + "text": "

The Underscore.js JavaScript library, http://underscorejs.org/, provides a very useful _.isEmpty() function for checking for empty strings and other empty objects.

\n\n

Reference: http://underscorejs.org/#isEmpty

\n\n
\n

isEmpty _.isEmpty(object)
\n Returns true if an enumerable object contains no values (no enumerable own-properties). For strings and array-like objects _.isEmpty checks if the length property is 0.

\n \n

_.isEmpty([1, 2, 3]);
\n => false

\n \n

_.isEmpty({});
\n => true

\n
\n\n

Other very useful Underscore.js functions include:

\n\n\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905d7", + "creator": "Agustí Sánchez", + "createdAt": 1478298525000, + "text": "

There's no isEmpty() method, you have to check for the type and the length:

\n\n
if (typeof test === 'string' && test.length === 0){\n  ...\n
\n\n

The type check is needed in order to avoid runtime errors when test is undefined or null.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91aae", + "creator": "Flimm", + "createdAt": 1628695269000, + "text": "I'm pretty sure test === "" is equivalent, and it's shorter.", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 67, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d6", + "creator": "tfont", + "createdAt": 1459412986000, + "text": "

A lot of answers, and a lot of different possibilities!

\n

Without a doubt for quick and simple implementation the winner is: if (!str.length) {...}

\n

However, as many other examples are available. The best functional method to go about this, I would suggest:

\n

\r\n
\r\n
function empty(str)\n{\n    if (typeof str == 'undefined' || !str || str.length === 0 || str === \"\" || !/[^\\s]/.test(str) || /^\\s*$/.test(str) || str.replace(/\\s/g,\"\") === \"\")\n        return true;\n    else\n        return false;\n}
\r\n
\r\n
\r\n

\n

A bit excessive, I know.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91ab0", + "creator": "SvdSinner", + "createdAt": 1466086234000, + "text": "Checking for undefined would need to be moved to first in the checks, or undefined items will throw exceptions on the earlier checks.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32500082fcc3049e91ab2", + "creator": "tfont", + "createdAt": 1466176645000, + "text": "Completely agree! NICE CATCH. I will edit my above answer!", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f32500082fcc3049e91ab3", + "creator": "RobG", + "createdAt": 1571230349000, + "text": "str.length === 0 returns true for any function that has no formal parameters.", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f32500082fcc3049e91ab4", + "creator": "Praveen M P", + "createdAt": 1604405184000, + "text": "str.length === 0 || str === "" both would do the same task.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d8", + "creator": "Moshi", + "createdAt": 1502952674000, + "text": "

You can use lodash:\n_.isEmpty(value).

\n\n

It covers a lot of cases like {}, '', null, undefined, etc.

\n\n

But it always returns true for Number type of JavaScript primitive data types like _.isEmpty(10) or _.isEmpty(Number.MAX_VALUE) both returns true.

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91ab6", + "creator": "Erich", + "createdAt": 1581609377000, + "text": "_.isEmpty(" "); // => false", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32500082fcc3049e91ab8", + "creator": "Moshi", + "createdAt": 1585121670000, + "text": "@Erich Because " " is not empty. _.isEmpty(""); returns true.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905d9", + "creator": "Alireza", + "createdAt": 1507343581000, + "text": "

You can easily add it to native String object in JavaScript and reuse it over and over...
\nSomething simple like below code can do the job for you if you want to check '' empty strings:

\n\n
String.prototype.isEmpty = String.prototype.isEmpty || function() {\n  return !(!!this.length);\n}\n
\n\n

Otherwise if you'd like to check both '' empty string and ' ' with space, you can do that by just adding trim(), something like the code below:

\n\n
String.prototype.isEmpty = String.prototype.isEmpty || function() {\n   return !(!!this.trim().length);\n}\n
\n\n

and you can call it this way:

\n\n
''.isEmpty(); //return true\n'alireza'.isEmpty(); //return false\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32500082fcc3049e91aba", + "creator": "sean", + "createdAt": 1607075485000, + "text": "Please don't pollute prototypes.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32500082fcc3049e91abc", + "creator": "Flimm", + "createdAt": 1628695337000, + "text": "It's generally considered bad practise to use monkey-patching like this.", + "upvotes": 263, + "upvoterUsernames": [], + "downvotes": 263, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905da", + "creator": "KARTHIKEYAN.A", + "createdAt": 1512023210000, + "text": "

You can able to validate following ways and understand the difference.

\n\n

\r\n
\r\n
var j = undefined;\r\nconsole.log((typeof j == 'undefined') ? \"true\":\"false\");\r\nvar j = null; \r\nconsole.log((j == null) ? \"true\":\"false\");\r\nvar j = \"\";\r\nconsole.log((!j) ? \"true\":\"false\");\r\nvar j = \"Hi\";\r\nconsole.log((!j) ? \"true\":\"false\");
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905dc", + "creator": "oviniciusfeitosa", + "createdAt": 1557524018000, + "text": "

Try this:

\n\n
export const isEmpty = string => (!string || !string.length);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905db", + "creator": "Imran Ahmad", + "createdAt": 1520844503000, + "text": "

Meanwhile we can have one function that checks for all 'empties' like null, undefined, '', ' ', {}, [].\nSo I just wrote this.

\n\n
var isEmpty = function(data) {\n    if(typeof(data) === 'object'){\n        if(JSON.stringify(data) === '{}' || JSON.stringify(data) === '[]'){\n            return true;\n        }else if(!data){\n            return true;\n        }\n        return false;\n    }else if(typeof(data) === 'string'){\n        if(!data.trim()){\n            return true;\n        }\n        return false;\n    }else if(typeof(data) === 'undefined'){\n        return true;\n    }else{\n        return false;\n    }\n}\n
\n\n

Use cases and results.

\n\n
console.log(isEmpty()); // true\nconsole.log(isEmpty(null)); // true\nconsole.log(isEmpty('')); // true\nconsole.log(isEmpty('  ')); // true\nconsole.log(isEmpty(undefined)); // true\nconsole.log(isEmpty({})); // true\nconsole.log(isEmpty([])); // true\nconsole.log(isEmpty(0)); // false\nconsole.log(isEmpty('Hey')); // false\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905dd", + "creator": "Japesh", + "createdAt": 1571389367000, + "text": "

The following regular expression is another solution, that can be used for null, empty or undefined string.

\n\n
(/(null|undefined|^$)/).test(null)\n
\n\n

I added this solution, because it can be extended further to check empty or some value like as follow. The following regular expression is checking either string can be empty null undefined or it has integers only.

\n\n
(/(null|undefined|^$|^\\d+$)/).test()\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91abf", + "creator": "sean", + "createdAt": 1607075414000, + "text": "A major flaw: this regex matches the string literal "null". (/(null|undefined|^$)/).test("null")", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905df", + "creator": "Abhishek Luthra", + "createdAt": 1581323671000, + "text": "

Starting with:

\n\n
return (!value || value == undefined || value == \"\" || value.length == 0);\n
\n\n

Looking at the last condition, if value == \"\", its length must be 0. Therefore drop it:

\n\n
return (!value || value == undefined || value == \"\");\n
\n\n

But wait! In JavaScript, an empty string is false. Therefore, drop value == \"\":

\n\n
return (!value || value == undefined);\n
\n\n

And !undefined is true, so that check isn't needed. So we have:

\n\n
return (!value);\n
\n\n

And we don't need parentheses:

\n\n
return !value\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91ac1", + "creator": "nerez", + "createdAt": 1588047314000, + "text": "what happens if value = false or value = 0. will you return the correct response according to the question?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905de", + "creator": "Davi Daniel Siepmann", + "createdAt": 1571865237000, + "text": "

I didn't see a good answer here (at least not an answer that fits for me)

\n\n

So I decided to answer myself:

\n\n

value === undefined || value === null || value === \"\";

\n\n

You need to start checking if it's undefined. Otherwise your method can explode, and then you can check if it equals null or is equal to an empty string.

\n\n

You cannot have !! or only if(value) since if you check 0 it's going to give you a false answer (0 is false).

\n\n

With that said, wrap it up in a method like:

\n\n

public static isEmpty(value: any): boolean {\n return value === undefined || value === null || value === \"\";\n}

\n\n

PS.: You don't need to check typeof, since it would explode and throw even before it enters the method

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905e0", + "creator": "Ibraheem", + "createdAt": 1598262930000, + "text": "
if ((str?.trim()?.length || 0) > 0) {\n   // str must not be any of:\n   // undefined\n   // null\n   // ""\n   // " " or just whitespace\n}\n
\n

Update:\nSince this answer is getting popular I thought I'd write a function form too:

\n
const isNotNilOrWhitespace = input => (input?.trim()?.length || 0) > 0;\n\nconst isNilOrWhitespace = input => (input?.trim()?.length || 0) === 0;\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91ac5", + "creator": "gfan", + "createdAt": 1638881992000, + "text": "could you please give more explanation?", + "upvotes": 1028, + "upvoterUsernames": [], + "downvotes": 1028, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905e1", + "creator": "trn450", + "createdAt": 1604604266000, + "text": "

Lots of useful information here, but in my opinion, one of the most important elements was not addressed.

\n

null, undefined, and "" are all falsy.

\n

When evaluating for an empty string, it's often because you need to replace it with something else.

\n

In which case, you can expect the following behavior.

\n
var a = ""\nvar b = null\nvar c = undefined\n\nconsole.log(a || "falsy string provided") // prints ->"falsy string provided"\nconsole.log(b || "falsy string provided") // prints ->"falsy string provided"\nconsole.log(c || "falsy string provided") // prints ->"falsy string provided"\n
\n

With that in mind, a method or function that can return whether or not a string is "", null, or undefined (an invalid string) versus a valid string is as simple as this:

\n
const validStr = (str) => str ? true : false\n\nvalidStr(undefined) // returns false\nvalidStr(null) // returns false\nvalidStr("") // returns false\nvalidStr("My String") // returns true\n
\n

I hope that's helpful.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905e3", + "creator": "Anastasios Tsournos", + "createdAt": 1610643869000, + "text": "

You can check this using the typeof operator along with the length method.

\n
const isNonEmptyString = (value) => typeof(value) == 'string' && value.length > 0\n
\n

edited as per @kip2 suggestion.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91ac9", + "creator": "kip2", + "createdAt": 1620897154000, + "text": "shouldn't the name be more like isNonEmptyString() or isValidString()? currently it looks inverted...", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905e2", + "creator": "sean", + "createdAt": 1607075727000, + "text": "

Trimming whitespace with the null-coalescing operator:

\n
if (!str?.trim()) {\n  // do something...\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91acc", + "creator": "Hexodus", + "createdAt": 1614093404000, + "text": "It looks cool but str.trim() is sufficient. One should never overcomplicate things IMO.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905e4", + "creator": "Labham Jain", + "createdAt": 1625379418000, + "text": "

Well, the simplest function to check this is...

\n

const checkEmpty = string => (string.trim() === "") || !string.trim();

\n

Usage:

\n
checkEmpty(""); // returns true.\ncheckEmpty("mystr"); // returns false.\n
\n

It's that dead simple. :)

\n", + "upvotes": 205, + "upvoterUsernames": [], + "downvotes": 205, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32501082fcc3049e91ace", + "creator": "Flimm", + "createdAt": 1628695719000, + "text": "This will return true, even for strings that aren't empty that contain whitespace, such as " "", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905e5", + "creator": "CrazyStack", + "createdAt": 1629627174000, + "text": "

The Ultimate and shortest variant of isBlank Function:

\n

\r\n
\r\n
/**\n * Will return:\n * False for: for all strings with chars\n * True for: false, null, undefined, 0, 0.0, \"\", \" \".\n *\n * @param str\n * @returns {boolean}\n */\nfunction isBlank(str){\n    return (!!!str || /^\\s*$/.test(str));\n}\n\n// tests\nconsole.log(\"isBlank TRUE variants:\");\nconsole.log(isBlank(false));\nconsole.log(isBlank(undefined));\nconsole.log(isBlank(null));\nconsole.log(isBlank(0));\nconsole.log(isBlank(0.0));\nconsole.log(isBlank(\"\"));\nconsole.log(isBlank(\" \"));\n\nconsole.log(\"isBlank FALSE variants:\");\nconsole.log(isBlank(\"0\"));\nconsole.log(isBlank(\"0.0\"));\nconsole.log(isBlank(\" 0\"));\nconsole.log(isBlank(\"0 \"));\nconsole.log(isBlank(\"Test string\"));\nconsole.log(isBlank(\"true\"));\nconsole.log(isBlank(\"false\"));\nconsole.log(isBlank(\"null\"));\nconsole.log(isBlank(\"undefined\"));
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905e6", + "creator": "Anis KCHAOU", + "createdAt": 1633679806000, + "text": "

Try this code :

\n
function isEmpty(strValue)\n{\n      // Test whether strValue is empty\n    if (!strValue || strValue.trim() === "" || (strValue.trim()).length === 0) {\n        //do something\n    }\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905e8", + "creator": "sMyles", + "createdAt": 1636491876000, + "text": "

Here's some custom functions I use for handling this. Along with examples of how the code runs.

\n

\r\n
\r\n
const v1 = 0\nconst v2 = '4'\nconst v2e = undefined\nconst v2e2 = null\nconst v3 = [1, 2, 3, 4]\nconst v3e = []\nconst v4 = true\nconst v4e = false\nconst v5 = {\n  test: 'value'\n}\nconst v5e = {}\nconst v6 = 'NotEmpty'\nconst v6e = ''\n\nfunction isNumeric(n) {\n  return !isNaN(parseFloat(n)) && isFinite(n)\n}\n\nfunction isEmpty(v, zeroIsEmpty = false) {\n  /**\n   * When doing a typeof check, null will always return \"object\" so we filter that out first\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#typeof_null\n   */\n  if (v === null) {\n    return true\n  }\n\n  if (v === true) {\n    return false\n  }\n\n  if (typeof v === 'object') {\n    return !Object.keys(v).length\n  }\n\n  if (isNumeric(v)) {\n    return zeroIsEmpty ? parseFloat(v) === 0 : false\n  }\n\n  return !v || !v.length || v.length < 1\n}\n\nconsole.log(isEmpty(v1), isEmpty(v1, true))\nconsole.log(isEmpty(v2), isEmpty(v2e), isEmpty(v2e))\nconsole.log(isEmpty(v3), isEmpty(v3e))\nconsole.log(isEmpty(v4), isEmpty(v4e))\nconsole.log(isEmpty(v5), isEmpty(v5e))\nconsole.log(isEmpty(v6), isEmpty(v6e))
\r\n
\r\n
\r\n

\n

Also for reference, here's the source for lodash isEmpty:\nhttps://github.com/lodash/lodash/blob/master/isEmpty.js

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905e7", + "creator": "Ali Yaghoubi", + "createdAt": 1634148796000, + "text": "

This is falsy value.\nFalsy Values

\n

The first solution:

\n
const str = "";\nreturn str || "Hello"\n
\n

The second solution:

\n
const str = "";\nreturn (!!str) || "Hello"; // !!str is Boolean\n
\n

The third solution:

\n
const str = "";\nreturn (+str) || "Hello"; // !!str is Boolean\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91ad3", + "creator": "devildelta", + "createdAt": 1637137282000, + "text": "The third solution is basically wrong when input is not a number, so that parsing result will be NaN which is also falsy.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c1082fcc3049e90557", + "creator": "Himanshu Shekhar", + "createdAt": 1557745136000, + "text": "Check this one : stackoverflow.com/a/36491147/7026966", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 168, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90558", + "creator": "Omar bakhsh", + "createdAt": 1612067349000, + "text": "str.length > -1", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90559", + "creator": "Flimm", + "createdAt": 1628695220000, + "text": "Also, an empty string cannot contain whitespace, like a lot of answers are assuming!", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 4594142, + "uvac": 4594191 + } + }, + { + "_id": "62f321bb082fcc3049e8fedb", + "title": "How can I upload files asynchronously with jQuery?", + "title-lowercase": "how can i upload files asynchronously with jquery?", + "creator": "Sergio del Amo", + "createdAt": 1223029337000, + "status": "open", + "text": "

I would like to upload a file asynchronously with jQuery.

\n\n

\r\n
\r\n
$(document).ready(function () {\r\n    $(\"#uploadbutton\").click(function () {\r\n        var filename = $(\"#file\").val();\r\n\r\n        $.ajax({\r\n            type: \"POST\",\r\n            url: \"addFile.do\",\r\n            enctype: 'multipart/form-data',\r\n            data: {\r\n                file: filename\r\n            },\r\n            success: function () {\r\n                alert(\"Data Uploaded: \");\r\n            }\r\n        });\r\n    });\r\n});
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js\"></script>\r\n<span>File</span>\r\n<input type=\"file\" id=\"file\" name=\"file\" size=\"10\"/>\r\n<input id=\"uploadbutton\" type=\"button\" value=\"Upload\"/>
\r\n
\r\n
\r\n

\n\n

Instead of the file being uploaded, I am only getting the filename. What can I do to fix this problem?

\n", + "upvotes": 5558, + "upvoterUsernames": [], + "downvotes": 2467, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1461702, + "answers": 31, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e908d3", + "creator": "Mattias", + "createdAt": 1223030198000, + "text": "

Note: This answer is outdated, it is now possible to upload files using XHR.

\n\n
\n\n

You cannot upload files using XMLHttpRequest (Ajax). You can simulate the effect using an iframe or Flash. The excellent jQuery Form Plugin that posts your files through an iframe to get the effect.

\n", + "upvotes": 139, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d5", + "creator": "Darryl Hein", + "createdAt": 1224358250000, + "text": "

A solution I found was to have the <form> target a hidden iFrame. The iFrame can then run JS to display to the user that it's complete (on page load).

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d4", + "creator": "Oli", + "createdAt": 1223030491000, + "text": "

2019 Update: It still depends on the browsers your demographic uses.

\n

An important thing to understand with the "new" HTML5 file API is that it wasn't supported until IE 10. If the specific market you're aiming at has a higher-than-average propensity toward older versions of Windows, you might not have access to it.

\n

As of 2017, about 5% of browsers are one of IE 6, 7, 8 or 9. If you head into a big corporation (e.g., this is a B2B tool or something you're delivering for training) that number can skyrocket. In 2016, I dealt with a company using IE8 on over 60% of their machines.

\n

It's 2019 as of this edit, almost 11 years after my initial answer. IE9 and lower are globally around the 1% mark but there are still clusters of higher usage.

\n

The important take-away from this —whatever the feature— is, check what browser your users use. If you don't, you'll learn a quick and painful lesson in why "works for me" isn't good enough in a deliverable to a client. caniuse is a useful tool but note where they get their demographics from. They may not align with yours. This is never truer than enterprise environments.

\n

My answer from 2008 follows.

\n
\n

However, there are viable non-JS methods of file uploads. You can create an iframe on the page (that you hide with CSS) and then target your form to post to that iframe. The main page doesn't need to move.

\n

It's a "real" post so it's not wholly interactive. If you need status you need something server-side to process that. This varies massively depending on your server. ASP.NET has nicer mechanisms. PHP plain fails, but you can use Perl or Apache modifications to get around it.

\n

If you need multiple file uploads, it's best to do each file one at a time (to overcome maximum file upload limits). Post the first form to the iframe, monitor its progress using the above and when it has finished, post the second form to the iframe, and so on.

\n

Or use a Java/Flash solution. They're a lot more flexible in what they can do with their posts...

\n", + "upvotes": 350, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d6", + "creator": "pixxaar", + "createdAt": 1227285486000, + "text": "

I recommend using the Fine Uploader plugin for this purpose. Your JavaScript code would be:

\n\n
$(document).ready(function() {\n  $(\"#uploadbutton\").jsupload({\n    action: \"addFile.do\",\n    onComplete: function(response){\n      alert( \"server response: \" + response);\n    }\n  });\n});\n
\n", + "upvotes": 172, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d8", + "creator": "Jordan Feldstein", + "createdAt": 1311899660000, + "text": "

This AJAX file upload jQuery plugin uploads the file somehwere, and passes the\nresponse to a callback, nothing else.

\n\n\n\n

-- Use as little as --

\n\n
$('#one-specific-file').ajaxfileupload({\n  'action': '/upload.php'\n});\n
\n\n

-- or as much as --

\n\n
$('input[type=\"file\"]').ajaxfileupload({\n  'action': '/upload.php',\n  'params': {\n    'extra': 'info'\n  },\n  'onComplete': function(response) {\n    console.log('custom handler for file:');\n    alert(JSON.stringify(response));\n  },\n  'onStart': function() {\n    if(weWantedTo) return false; // cancels upload\n  },\n  'onCancel': function() {\n    console.log('no file selected');\n  }\n});\n
\n", + "upvotes": 141, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d7", + "creator": "wbharding", + "createdAt": 1250203479000, + "text": "

I've written this up in a Rails environment. It's only about five lines of JavaScript, if you use the lightweight jQuery-form plugin.

\n\n

The challenge is in getting AJAX upload working as the standard remote_form_for doesn't understand multi-part form submission. It's not going to send the file data Rails seeks back with the AJAX request.

\n\n

That's where the jQuery-form plugin comes into play.

\n\n

Here’s the Rails code for it:

\n\n
<% remote_form_for(:image_form, \n                   :url => { :controller => \"blogs\", :action => :create_asset }, \n                   :html => { :method => :post, \n                              :id => 'uploadForm', :multipart => true }) \n                                                                        do |f| %>\n Upload a file: <%= f.file_field :uploaded_data %>\n<% end %>\n
\n\n

Here’s the associated JavaScript:

\n\n
$('#uploadForm input').change(function(){\n $(this).parent().ajaxSubmit({\n  beforeSubmit: function(a,f,o) {\n   o.dataType = 'json';\n  },\n  complete: function(XMLHttpRequest, textStatus) {\n   // XMLHttpRequest.responseText will contain the URL of the uploaded image.\n   // Put it in an image element you create, or do with it what you will.\n   // For example, if you have an image elemtn with id \"my_image\", then\n   //  $('#my_image').attr('src', XMLHttpRequest.responseText);\n   // Will set that image tag to display the uploaded image.\n  },\n });\n});\n
\n\n

And here’s the Rails controller action, pretty vanilla:

\n\n
 @image = Image.new(params[:image_form])\n @image.save\n render :text => @image.public_filename\n
\n\n

I’ve been using this for the past few weeks with Bloggity, and it’s worked like a champ.

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908da", + "creator": "Techie", + "createdAt": 1359111808000, + "text": "

I have been using the below script to upload images which happens to work fine.

\n\n

HTML

\n\n
<input id=\"file\" type=\"file\" name=\"file\"/>\n<div id=\"response\"></div>\n
\n\n

JavaScript

\n\n
jQuery('document').ready(function(){\n    var input = document.getElementById(\"file\");\n    var formdata = false;\n    if (window.FormData) {\n        formdata = new FormData();\n    }\n    input.addEventListener(\"change\", function (evt) {\n        var i = 0, len = this.files.length, img, reader, file;\n\n        for ( ; i < len; i++ ) {\n            file = this.files[i];\n\n            if (!!file.type.match(/image.*/)) {\n                if ( window.FileReader ) {\n                    reader = new FileReader();\n                    reader.onloadend = function (e) {\n                        //showUploadedItem(e.target.result, file.fileName);\n                    };\n                    reader.readAsDataURL(file);\n                }\n\n                if (formdata) {\n                    formdata.append(\"image\", file);\n                    formdata.append(\"extra\",'extra-data');\n                }\n\n                if (formdata) {\n                    jQuery('div#response').html('<br /><img src=\"ajax-loader.gif\"/>');\n\n                    jQuery.ajax({\n                        url: \"upload.php\",\n                        type: \"POST\",\n                        data: formdata,\n                        processData: false,\n                        contentType: false,\n                        success: function (res) {\n                         jQuery('div#response').html(\"Successfully uploaded\");\n                        }\n                    });\n                }\n            }\n            else\n            {\n                alert('Not a vaild image!');\n            }\n        }\n\n    }, false);\n});\n
\n\n

Explanation

\n\n

I use response div to show the uploading animation and response after upload is done.

\n\n

Best part is you can send extra data such as ids & etc with the file when you use this script. I have mention it extra-data as in the script.

\n\n

At the PHP level this will work as normal file upload. extra-data can be retrieved as $_POST data.

\n\n

Here you are not using a plugin and stuff. You can change the code as you want. You are not blindly coding here. This is the core functionality of any jQuery file upload. Actually Javascript.

\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d9", + "creator": "olanod", + "createdAt": 1325856888000, + "text": "

With HTML5 you can make file uploads with Ajax and jQuery. Not only that, you can do file validations (name, size, and MIME type) or handle the progress event with the HTML5 progress tag (or a div). Recently I had to make a file uploader, but I didn't want to use Flash nor Iframes or plugins and after some research I came up with the solution.

\n\n

The HTML:

\n\n
<form enctype=\"multipart/form-data\">\n    <input name=\"file\" type=\"file\" />\n    <input type=\"button\" value=\"Upload\" />\n</form>\n<progress></progress>\n
\n\n

First, you can do some validation if you want. For example, in the .on('change') event of the file:

\n\n
$(':file').on('change', function () {\n  var file = this.files[0];\n\n  if (file.size > 1024) {\n    alert('max upload size is 1k');\n  }\n\n  // Also see .name, .type\n});\n
\n\n

Now the $.ajax() submit with the button's click:

\n\n
$(':button').on('click', function () {\n  $.ajax({\n    // Your server script to process the upload\n    url: 'upload.php',\n    type: 'POST',\n\n    // Form data\n    data: new FormData($('form')[0]),\n\n    // Tell jQuery not to process data or worry about content-type\n    // You *must* include these options!\n    cache: false,\n    contentType: false,\n    processData: false,\n\n    // Custom XMLHttpRequest\n    xhr: function () {\n      var myXhr = $.ajaxSettings.xhr();\n      if (myXhr.upload) {\n        // For handling the progress of the upload\n        myXhr.upload.addEventListener('progress', function (e) {\n          if (e.lengthComputable) {\n            $('progress').attr({\n              value: e.loaded,\n              max: e.total,\n            });\n          }\n        }, false);\n      }\n      return myXhr;\n    }\n  });\n});\n
\n\n

As you can see, with HTML5 (and some research) file uploading not only becomes possible but super easy. Try it with Google Chrome as some of the HTML5 components of the examples aren't available in every browser.

\n", + "upvotes": 3023, + "upvoterUsernames": [], + "downvotes": 426, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3263a082fcc3049e92091", + "creator": "Alessandro Cosentino", + "createdAt": 1351863708000, + "text": "Can I then use $_FILES in the upload.php?", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f3263a082fcc3049e92093", + "creator": "Tyler", + "createdAt": 1357144424000, + "text": "This should work in Internet Explorer but only Version 10. (caniuse.com/xhr2)", + "upvotes": 142, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908dc", + "creator": "farnoush resa", + "createdAt": 1371376169000, + "text": "

jQuery Uploadify is another good plugin which I have used before to upload files. The JavaScript code is as simple as the following: code. However, the new version does not work in Internet Explorer.

\n\n
$('#file_upload').uploadify({\n    'swf': '/public/js/uploadify.swf',\n    'uploader': '/Upload.ashx?formGuid=' + $('#formGuid').val(),\n    'cancelImg': '/public/images/uploadify-cancel.png',\n    'multi': true,\n    'onQueueComplete': function (queueData) {\n        // ...\n    },\n    'onUploadStart': function (file) {\n        // ...\n    }\n});\n
\n\n

I have done a lot of searching and I have come to another solution for uploading files without any plugin and only with ajax. The solution is as below:

\n\n
$(document).ready(function () {\n    $('#btn_Upload').live('click', AjaxFileUpload);\n});\n\nfunction AjaxFileUpload() {\n    var fileInput = document.getElementById(\"#Uploader\");\n    var file = fileInput.files[0];\n    var fd = new FormData();\n    fd.append(\"files\", file);\n    var xhr = new XMLHttpRequest();\n    xhr.open(\"POST\", 'Uploader.ashx');\n    xhr.onreadystatechange = function () {\n        if (xhr.readyState == 4) {\n             alert('success');\n        }\n        else if (uploadResult == 'success')\n            alert('error');\n    };\n    xhr.send(fd);\n}\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908dd", + "creator": "user1091949", + "createdAt": 1372209141000, + "text": "

Simple Ajax Uploader is another option:

\n\n

https://github.com/LPology/Simple-Ajax-Uploader

\n\n\n\n

Example usage:

\n\n
var uploader = new ss.SimpleUpload({\n    button: $('#uploadBtn'), // upload button\n    url: '/uploadhandler', // URL of server-side upload handler\n    name: 'userfile', // parameter name of the uploaded file\n    onSubmit: function() {\n        this.setProgressBar( $('#progressBar') ); // designate elem as our progress bar\n    },\n    onComplete: function(file, response) {\n        // do whatever after upload is finished\n    }\n});\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908de", + "creator": "Amit", + "createdAt": 1399889403000, + "text": "

You can use

\n\n
$(function() {\n    $(\"#file_upload_1\").uploadify({\n        height        : 30,\n        swf           : '/uploadify/uploadify.swf',\n        uploader      : '/uploadify/uploadify.php',\n        width         : 120\n    });\n});\n
\n\n

Demo

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908db", + "creator": "mpen", + "createdAt": 1361093681000, + "text": "

You can do it in vanilla JavaScript pretty easily. Here's a snippet from my current project:

\n\n
var xhr = new XMLHttpRequest();\nxhr.upload.onprogress = function(e) {\n    var percent = (e.position/ e.totalSize);\n    // Render a pretty progress bar\n};\nxhr.onreadystatechange = function(e) {\n    if(this.readyState === 4) {\n        // Handle file upload complete\n    }\n};\nxhr.open('POST', '/upload', true);\nxhr.setRequestHeader('X-FileName',file.name); // Pass the filename along\nxhr.send(file);\n
\n", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908df", + "creator": "tnt-rox", + "createdAt": 1403549447000, + "text": "

Convert file to base64 using |HTML5's readAsDataURL() or some base64 encoder. \nFiddle here

\n\n
var reader = new FileReader();\n\n        reader.onload = function(readerEvt) {\n            var binaryString = readerEvt.target.result;\n            document.getElementById(\"base64textarea\").value = btoa(binaryString);\n        };\n\n        reader.readAsBinaryString(file);\n
\n\n

Then to retrieve:

\n\n
window.open(\"data:application/octet-stream;base64,\" + base64);\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e1", + "creator": "Zayn Ali", + "createdAt": 1407466770000, + "text": "

You can upload simply with jQuery .ajax().

\n\n

HTML:

\n\n
<form id=\"upload-form\">\n    <div>\n        <label for=\"file\">File:</label>\n        <input type=\"file\" id=\"file\" name=\"file\" />\n        <progress class=\"progress\" value=\"0\" max=\"100\"></progress>\n    </div>\n    <hr />\n    <input type=\"submit\" value=\"Submit\" />\n</form>\n
\n\n

CSS

\n\n
.progress { display: none; }\n
\n\n

Javascript:

\n\n
$(document).ready(function(ev) {\n    $(\"#upload-form\").on('submit', (function(ev) {\n        ev.preventDefault();\n        $.ajax({\n            xhr: function() {\n                var progress = $('.progress'),\n                    xhr = $.ajaxSettings.xhr();\n\n                progress.show();\n\n                xhr.upload.onprogress = function(ev) {\n                    if (ev.lengthComputable) {\n                        var percentComplete = parseInt((ev.loaded / ev.total) * 100);\n                        progress.val(percentComplete);\n                        if (percentComplete === 100) {\n                            progress.hide().val(0);\n                        }\n                    }\n                };\n\n                return xhr;\n            },\n            url: 'upload.php',\n            type: 'POST',\n            data: new FormData(this),\n            contentType: false,\n            cache: false,\n            processData: false,\n            success: function(data, status, xhr) {\n                // ...\n            },\n            error: function(xhr, status, error) {\n                // ...\n            }\n       });\n    }));\n});\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3263b082fcc3049e92098", + "creator": "Marco", + "createdAt": 1652863359000, + "text": "The progress is nice ;-)", + "upvotes": 478, + "upvoterUsernames": [], + "downvotes": 478, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908e0", + "creator": "ArtisticPhoenix", + "createdAt": 1403757792000, + "text": "

The simplest and most robust way I have done this in the past, is to simply target a hidden iFrame tag with your form - then it will submit within the iframe without reloading the page.

\n\n

That is if you don't want to use a plugin, JavaScript or any other forms of \"magic\" other than HTML. Of course you can combine this with JavaScript or what have you...

\n\n
<form target=\"iframe\" action=\"\" method=\"post\" enctype=\"multipart/form-data\">\n    <input name=\"file\" type=\"file\" />\n    <input type=\"button\" value=\"Upload\" />\n</form>\n\n<iframe name=\"iframe\" id=\"iframe\" style=\"display:none\" ></iframe>\n
\n\n

You can also read the contents of the iframe onLoad for server errors or success responses and then output that to user.

\n\n

Chrome, iFrames, and onLoad

\n\n

-note- you only need to keep reading if you are interested in how to setup a UI blocker when doing uploading/downloading

\n\n

Currently Chrome doesn't trigger the onLoad event for the iframe when it's used to transfer files. Firefox, IE, and Edge all fire the onload event for file transfers.

\n\n

The only solution that I found works for Chrome was to use a cookie.

\n\n

To do that basically when the upload/download is started:

\n\n\n\n

Using a cookie for this is ugly but it works.

\n\n

I made a jQuery plugin to handle this issue for Chrome when downloading, you can find here

\n\n

https://github.com/ArtisticPhoenix/jQuery-Plugins/blob/master/iDownloader.js

\n\n

The same basic principal applies to uploading, as well.

\n\n

To use the downloader ( include the JS, obviously )

\n\n
 $('body').iDownloader({\n     \"onComplete\" : function(){\n          $('#uiBlocker').css('display', 'none'); //hide ui blocker on complete\n     }\n });\n\n $('somebuttion').click( function(){\n      $('#uiBlocker').css('display', 'block'); //block the UI\n      $('body').iDownloader('download', 'htttp://example.com/location/of/download');\n });\n
\n\n

And on the server side, just before transferring the file data, create the cookie

\n\n
 setcookie('iDownloader', true, time() + 30, \"/\");\n
\n\n

The plugin will see the cookie, and then trigger the onComplete callback.

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e2", + "creator": "Allende", + "createdAt": 1436378374000, + "text": "

Look for Handling the upload process for a file, asynchronously in here:\nhttps://developer.mozilla.org/en-US/docs/Using_files_from_web_applications

\n\n

Sample from the link

\n\n
<?php\nif (isset($_FILES['myFile'])) {\n    // Example:\n    move_uploaded_file($_FILES['myFile']['tmp_name'], \"uploads/\" . $_FILES['myFile']['name']);\n    exit;\n}\n?><!DOCTYPE html>\n<html>\n<head>\n    <title>dnd binary upload</title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <script type=\"text/javascript\">\n        function sendFile(file) {\n            var uri = \"/index.php\";\n            var xhr = new XMLHttpRequest();\n            var fd = new FormData();\n\n            xhr.open(\"POST\", uri, true);\n            xhr.onreadystatechange = function() {\n                if (xhr.readyState == 4 && xhr.status == 200) {\n                    // Handle response.\n                    alert(xhr.responseText); // handle response.\n                }\n            };\n            fd.append('myFile', file);\n            // Initiate a multipart/form-data upload\n            xhr.send(fd);\n        }\n\n        window.onload = function() {\n            var dropzone = document.getElementById(\"dropzone\");\n            dropzone.ondragover = dropzone.ondragenter = function(event) {\n                event.stopPropagation();\n                event.preventDefault();\n            }\n\n            dropzone.ondrop = function(event) {\n                event.stopPropagation();\n                event.preventDefault();\n\n                var filesArray = event.dataTransfer.files;\n                for (var i=0; i<filesArray.length; i++) {\n                    sendFile(filesArray[i]);\n                }\n            }\n        }\n    </script>\n</head>\n<body>\n    <div>\n        <div id=\"dropzone\" style=\"margin:30px; width:500px; height:300px; border:1px dotted grey;\">Drag & drop your file here...</div>\n    </div>\n</body>\n</html>\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e4", + "creator": "Erick Lanford Xenes", + "createdAt": 1446583645000, + "text": "

This is my solution.

\n\n
<form enctype=\"multipart/form-data\">    \n\n    <div class=\"form-group\">\n        <label class=\"control-label col-md-2\" for=\"apta_Description\">Description</label>\n        <div class=\"col-md-10\">\n            <input class=\"form-control text-box single-line\" id=\"apta_Description\" name=\"apta_Description\" type=\"text\" value=\"\">\n        </div>\n    </div>\n\n    <input name=\"file\" type=\"file\" />\n    <input type=\"button\" value=\"Upload\" />\n</form>\n
\n\n

and the js

\n\n
<script>\n\n    $(':button').click(function () {\n        var formData = new FormData($('form')[0]);\n        $.ajax({\n            url: '@Url.Action(\"Save\", \"Home\")',  \n            type: 'POST',                \n            success: completeHandler,\n            data: formData,\n            cache: false,\n            contentType: false,\n            processData: false\n        });\n    });    \n\n    function completeHandler() {\n        alert(\":)\");\n    }    \n</script>\n
\n\n

Controller

\n\n
[HttpPost]\npublic ActionResult Save(string apta_Description, HttpPostedFileBase file)\n{\n    [...]\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3263b082fcc3049e9209c", + "creator": "nonchip", + "createdAt": 1452119267000, + "text": "so there's actually a mvc framework called "mvc"? and it uses csharpish syntax? that's cruel.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908e3", + "creator": "Vivek Aasaithambi", + "createdAt": 1438090750000, + "text": "
var formData=new FormData();\nformData.append(\"fieldname\",\"value\");\nformData.append(\"image\",$('[name=\"filename\"]')[0].files[0]);\n\n$.ajax({\n    url:\"page.php\",\n    data:formData,\n    type: 'POST',\n    dataType:\"JSON\",\n    cache: false,\n    contentType: false,\n    processData: false,\n    success:function(data){ }\n});\n
\n\n

You can use form data to post all your values including images.

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e5", + "creator": "Siddhartha Chowdhury", + "createdAt": 1459356511000, + "text": "

Here's just another solution of how to upload file (without any plugin)

\n\n

Using simple Javascripts and AJAX (with progress-bar)

\n\n

HTML part

\n\n
<form id=\"upload_form\" enctype=\"multipart/form-data\" method=\"post\">\n    <input type=\"file\" name=\"file1\" id=\"file1\"><br>\n    <input type=\"button\" value=\"Upload File\" onclick=\"uploadFile()\">\n    <progress id=\"progressBar\" value=\"0\" max=\"100\" style=\"width:300px;\"></progress>\n    <h3 id=\"status\"></h3>\n    <p id=\"loaded_n_total\"></p>\n</form>\n
\n\n

JS part

\n\n
function _(el){\n    return document.getElementById(el);\n}\nfunction uploadFile(){\n    var file = _(\"file1\").files[0];\n    // alert(file.name+\" | \"+file.size+\" | \"+file.type);\n    var formdata = new FormData();\n    formdata.append(\"file1\", file);\n    var ajax = new XMLHttpRequest();\n    ajax.upload.addEventListener(\"progress\", progressHandler, false);\n    ajax.addEventListener(\"load\", completeHandler, false);\n    ajax.addEventListener(\"error\", errorHandler, false);\n    ajax.addEventListener(\"abort\", abortHandler, false);\n    ajax.open(\"POST\", \"file_upload_parser.php\");\n    ajax.send(formdata);\n}\nfunction progressHandler(event){\n    _(\"loaded_n_total\").innerHTML = \"Uploaded \"+event.loaded+\" bytes of \"+event.total;\n    var percent = (event.loaded / event.total) * 100;\n    _(\"progressBar\").value = Math.round(percent);\n    _(\"status\").innerHTML = Math.round(percent)+\"% uploaded... please wait\";\n}\nfunction completeHandler(event){\n    _(\"status\").innerHTML = event.target.responseText;\n    _(\"progressBar\").value = 0;\n}\nfunction errorHandler(event){\n    _(\"status\").innerHTML = \"Upload Failed\";\n}\nfunction abortHandler(event){\n    _(\"status\").innerHTML = \"Upload Aborted\";\n}\n
\n\n

PHP part

\n\n
<?php\n$fileName = $_FILES[\"file1\"][\"name\"]; // The file name\n$fileTmpLoc = $_FILES[\"file1\"][\"tmp_name\"]; // File in the PHP tmp folder\n$fileType = $_FILES[\"file1\"][\"type\"]; // The type of file it is\n$fileSize = $_FILES[\"file1\"][\"size\"]; // File size in bytes\n$fileErrorMsg = $_FILES[\"file1\"][\"error\"]; // 0 for false... and 1 for true\nif (!$fileTmpLoc) { // if file not chosen\n    echo \"ERROR: Please browse for a file before clicking the upload button.\";\n    exit();\n}\nif(move_uploaded_file($fileTmpLoc, \"test_uploads/$fileName\")){ // assuming the directory name 'test_uploads'\n    echo \"$fileName upload is complete\";\n} else {\n    echo \"move_uploaded_file function failed\";\n}\n?>\n
\n\n

Here's the EXAMPLE application

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e6", + "creator": "Daniel Nyamasyo", + "createdAt": 1468905514000, + "text": "

You can see a solved solution with a working demo here that allows you to preview and submit form files to the server. For your case, you need to use Ajax to facilitate the file upload to the server:

\n\n
<from action=\"\" id=\"formContent\" method=\"post\" enctype=\"multipart/form-data\">\n    <span>File</span>\n    <input type=\"file\" id=\"file\" name=\"file\" size=\"10\"/>\n    <input id=\"uploadbutton\" type=\"button\" value=\"Upload\"/>\n</form>\n
\n\n

The data being submitted is a formdata. On your jQuery, use a form submit function instead of a button click to submit the form file as shown below.

\n\n
$(document).ready(function () {\n   $(\"#formContent\").submit(function(e){\n\n     e.preventDefault();\n     var formdata = new FormData(this);\n\n $.ajax({\n     url: \"ajax_upload_image.php\",\n     type: \"POST\",\n     data: formdata,\n     mimeTypes:\"multipart/form-data\",\n     contentType: false,\n     cache: false,\n     processData: false,\n     success: function(){\n\n     alert(\"successfully submitted\");\n\n     });\n   });\n});\n
\n\n

View more details

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e8", + "creator": "lat94", + "createdAt": 1519224097000, + "text": "

It is an old question, but still has no answer correct answer, so:

\n\n

Have you tried jQuery-File-Upload?

\n\n

Here is an example from the link above that might solve your problem:

\n\n
$('#fileupload').fileupload({\n    add: function (e, data) {\n        var that = this;\n        $.getJSON('/example/url', function (result) {\n            data.formData = result; // e.g. {id: 123}\n            $.blueimp.fileupload.prototype\n                .options.add.call(that, e, data);\n        });\n    } \n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e7", + "creator": "MEAbid", + "createdAt": 1476429466000, + "text": "

Sample: If you use jQuery, you can do easy to an upload file. This is a small and strong jQuery plugin, http://jquery.malsup.com/form/.

\n

Example

\n
var $bar   = $('.ProgressBar');\n$('.Form').ajaxForm({\n  dataType: 'json',\n\n  beforeSend: function(xhr) {\n    var percentVal = '0%';\n    $bar.width(percentVal);\n  },\n\n  uploadProgress: function(event, position, total, percentComplete) {\n    var percentVal = percentComplete + '%';\n    $bar.width(percentVal)\n  },\n\n  success: function(response) {\n    // Response\n  }\n});\n
\n

I hope it would be helpful

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908e9", + "creator": "Alireza", + "createdAt": 1520857933000, + "text": "

Using HTML5 and JavaScript, uploading async is quite easy, I create the uploading logic along with your html, this is not fully working as it needs the api, but demonstrate how it works, if you have the endpoint called /upload from root of your website, this code should work for you:

\n

\r\n
\r\n
const asyncFileUpload = () => {\n  const fileInput = document.getElementById(\"file\");\n  const file = fileInput.files[0];\n  const uri = \"/upload\";\n  const xhr = new XMLHttpRequest();\n  xhr.upload.onprogress = e => {\n    const percentage = e.loaded / e.total;\n    console.log(percentage);\n  };\n  xhr.onreadystatechange = e => {\n    if (xhr.readyState === 4 && xhr.status === 200) {\n      console.log(\"file uploaded\");\n    }\n  };\n  xhr.open(\"POST\", uri, true);\n  xhr.setRequestHeader(\"X-FileName\", file.name);\n  xhr.send(file);\n}
\r\n
<form>\n  <span>File</span>\n  <input type=\"file\" id=\"file\" name=\"file\" size=\"10\" />\n  <input onclick=\"asyncFileUpload()\" id=\"upload\" type=\"button\" value=\"Upload\" />\n</form>
\r\n
\r\n
\r\n

\n

Also some further information about XMLHttpReques:

\n
\n

The XMLHttpRequest Object

\n

All modern browsers support the XMLHttpRequest object.\nThe XMLHttpRequest object can be used to exchange data with a web\nserver behind the scenes. This means that it is possible to update\nparts of a web page, without reloading the whole page.

\n
\n
\n
\n

Create an XMLHttpRequest Object

\n

All modern browsers (Chrome, Firefox,\nIE7+, Edge, Safari, Opera) have a built-in XMLHttpRequest object.

\n

Syntax for creating an XMLHttpRequest object:

\n

variable = new XMLHttpRequest();

\n
\n
\n
\n

Access Across Domains

\n

For security reasons, modern browsers do not\nallow access across domains.

\n

This means that both the web page and the XML file it tries to load,\nmust be located on the same server.

\n

The examples on W3Schools all open XML files located on the W3Schools\ndomain.

\n

If you want to use the example above on one of your own web pages, the\nXML files you load must be located on your own server.

\n
\n

For more details, you can continue reading here...

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ef", + "creator": "Michael Wang", + "createdAt": 1554725693000, + "text": "

You can use the following code.

\n\n
async: false(true)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32679082fcc3049e920a4", + "creator": "kabirbaidhya", + "createdAt": 1600071477000, + "text": "Can you elaborate your answer? It's not clear where to make this change.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920a5", + "creator": "Michael Wang", + "createdAt": 1600214969000, + "text": "You can add it on ajax call function", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920a7", + "creator": "Sebastian Simon", + "createdAt": 1614931483000, + "text": "This is invalid syntax and doesn’t answer this question.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908f1", + "creator": "Diego Vinícius", + "createdAt": 1582902409000, + "text": "

What if using promises which ajax and checking if the file is valid and well saved in your backend, so you can use some animation in front while user is navigating thought your page.

\n\n

You can even make it paralel upload or stacking with recursive approach

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f0", + "creator": "Kamil Kiełczewski", + "createdAt": 1562322717000, + "text": "

Try

\n\n

\r\n
\r\n
async function saveFile() \r\n{\r\n    let formData = new FormData();           \r\n    formData.append(\"file\", file.files[0]);\r\n    await fetch('addFile.do', {method: \"POST\", body: formData});    \r\n    alert(\"Data Uploaded: \");\r\n}
\r\n
<span>File</span>\r\n<input type=\"file\" id=\"file\" name=\"file\" size=\"10\"/>\r\n<input type=\"button\" value=\"Upload\" onclick=\"saveFile()\"/>
\r\n
\r\n
\r\n

\n\n

The content-type='multipart/form-data' is set by browser automatically, the file name is added automatically too to filename FormData parameter (and can be easy read by server). Here is more developed example with err handling and json adding

\n\n

\r\n
\r\n
async function saveFile(inp) \r\n{\r\n    let user = { name:'john', age:34 };\r\n    let formData = new FormData();\r\n    let photo = inp.files[0];      \r\n         \r\n    formData.append(\"photo\", photo);\r\n    formData.append(\"user\", JSON.stringify(user));  \r\n    \r\n    try {\r\n       let r = await fetch('/upload/image', {method: \"POST\", body: formData}); \r\n       console.log('HTTP response code:',r.status); \r\n       alert('success');\r\n    } catch(e) {\r\n       console.log('Huston we have problem...:', e);\r\n    }\r\n    \r\n}
\r\n
<input type=\"file\" onchange=\"saveFile(this)\" >\r\n<br><br>\r\nBefore selecting the file Open chrome console > network tab to see the request details.\r\n<br><br>\r\n<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908eb", + "creator": "Alister", + "createdAt": 1534506439000, + "text": "

A modern approach without Jquery is to use the FileList object you get back from <input type=\"file\"> when user selects a file(s) and then use Fetch to post the FileList wrapped around a FormData object.

\n\n
// The input DOM element // <input type=\"file\">\nconst inputElement = document.querySelector('input[type=file]');\n\n// Listen for a file submit from user\ninputElement.addEventListener('change', () => {\n    const data = new FormData();\n    data.append('file', inputElement.files[0]);\n    data.append('imageName', 'flower');\n\n    // You can then post it to your server.\n    // Fetch can accept an object of type FormData on its  body\n    fetch('/uploadImage', {\n        method: 'POST',\n        body: data\n    });\n});\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ea", + "creator": "BlackBeard", + "createdAt": 1526897675000, + "text": "

You can use newer Fetch API by JavaScript. Like this:

\n\n
function uploadButtonCLicked(){\n    var input = document.querySelector('input[type=\"file\"]')\n\n    fetch('/url', {\n      method: 'POST',\n      body: input.files[0]\n    }).then(res => res.json())   // you can do something with response\n      .catch(error => console.error('Error:', error))\n      .then(response => console.log('Success:', response));\n}                               \n
\n\n

Advantage: Fetch API is natively supported by all modern browsers, so you don't have to import anything. Also, note that fetch() returns a Promise which is then handled by using .then(..code to handle response..) asynchronously.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ec", + "creator": "Joe Clinton", + "createdAt": 1535572319000, + "text": "

You can do the Asynchronous Multiple File uploads using JavaScript or jQuery and that to without using any plugin. You can also show the real time progress of file upload in the progress control. I have come across 2 nice links -

\n\n
    \n
  1. ASP.NET Web Forms based Mulitple File Upload Feature with Progress Bar
  2. \n
  3. ASP.NET MVC based Multiple File Upload made in jQuery
  4. \n
\n\n

The server side language is C# but you can do some modification for making it work with other language like PHP.

\n\n

File Upload ASP.NET Core MVC:

\n\n

In the View create file upload control in html:

\n\n
<form method=\"post\" asp-action=\"Add\" enctype=\"multipart/form-data\">\n    <input type=\"file\" multiple name=\"mediaUpload\" />\n    <button type=\"submit\">Submit</button>\n</form>\n
\n\n

Now create action method in your controller:

\n\n
[HttpPost]\npublic async Task<IActionResult> Add(IFormFile[] mediaUpload)\n{\n    //looping through all the files\n    foreach (IFormFile file in mediaUpload)\n    {\n        //saving the files\n        string path = Path.Combine(hostingEnvironment.WebRootPath, \"some-folder-path\"); \n        using (var stream = new FileStream(path, FileMode.Create))\n        {\n            await file.CopyToAsync(stream);\n        }\n    }\n}\n
\n\n

hostingEnvironment variable is of type IHostingEnvironment which can be injected to the controller using dependency injection, like:

\n\n
private IHostingEnvironment hostingEnvironment;\npublic MediaController(IHostingEnvironment environment)\n{\n    hostingEnvironment = environment;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ed", + "creator": "Karthik Ravichandran", + "createdAt": 1542199339000, + "text": "

You can pass additional parameters along with file name on making asynchronous upload using XMLHttpRequest (without flash and iframe dependency). Append the additional parameter value with FormData and send the upload request.

\n\n
\n\n
var formData = new FormData();\nformData.append('parameter1', 'value1');\nformData.append('parameter2', 'value2'); \nformData.append('file', $('input[type=file]')[0].files[0]);\n\n$.ajax({\n    url: 'post back url',\n    data: formData,\n// other attributes of AJAX\n});\n
\n\n
\n\n

Also, Syncfusion JavaScript UI file upload provides solution for this scenario simply using event argument. you can find documentation here and further details about this control here enter link description here

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ee", + "creator": "kvz", + "createdAt": 1545382810000, + "text": "

You could also consider using something like https://uppy.io.

\n\n

It does file uploading without navigating away from the page and offers a few bonuses like drag & drop, resuming uploads in case of browser crashes/flaky networks, and importing from e.g. Instagram.\nIt's open source and does not rely on jQuery/React/Angular/Vue, but can be used with it. Disclaimer: as its creator I'm biased ;)

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c5082fcc3049e90887", + "creator": "Jimmy", + "createdAt": 1257264112000, + "text": "you are only getting the file name because your var filename is getting the value of $('#file'), not the file that lies in the input", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e90888", + "creator": "alex", + "createdAt": 1417802744000, + "text": "@Jimmy How would he get the get the file that lies in the input instead?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e90889", + "creator": "Ravi Makwana", + "createdAt": 1654069681000, + "text": "@Sergio none of the 34 Answers help you? if do then please appreciate and Mark as Accepted", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1467263, + "uvac": 1467294 + } + }, + { + "_id": "62f321bb082fcc3049e8feef", + "title": "Add table row in jQuery", + "title-lowercase": "add table row in jquery", + "creator": "Darryl Hein", + "createdAt": 1223155990000, + "status": "open", + "text": "

What is the best method in jQuery to add an additional row to a table as the last row?

\n\n

Is this acceptable?

\n\n
$('#myTable').append('<tr><td>my data</td><td>more data</td></tr>');\n
\n\n

Are there limitations to what you can add to a table like this (such as inputs, selects, number of rows)?

\n", + "upvotes": 4007, + "upvoterUsernames": [], + "downvotes": 1406, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1922621, + "answers": 41, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90bef", + "creator": "Luke Bennett", + "createdAt": 1223156972000, + "text": "

The approach you suggest is not guaranteed to give you the result you're looking for - what if you had a tbody for example:

\n\n
<table id=\"myTable\">\n  <tbody>\n    <tr>...</tr>\n    <tr>...</tr>\n  </tbody>\n</table>\n
\n\n

You would end up with the following:

\n\n
<table id=\"myTable\">\n  <tbody>\n    <tr>...</tr>\n    <tr>...</tr>\n  </tbody>\n  <tr>...</tr>\n</table>\n
\n\n

I would therefore recommend this approach instead:

\n\n
$('#myTable tr:last').after('<tr>...</tr><tr>...</tr>');\n
\n\n

You can include anything within the after() method as long as it's valid HTML, including multiple rows as per the example above.

\n\n

Update: Revisiting this answer following recent activity with this question. eyelidlessness makes a good comment that there will always be a tbody in the DOM; this is true, but only if there is at least one row. If you have no rows, there will be no tbody unless you have specified one yourself.

\n\n

DaRKoN_ suggests appending to the tbody rather than adding content after the last tr. This gets around the issue of having no rows, but still isn't bulletproof as you could theoretically have multiple tbody elements and the row would get added to each of them.

\n\n

Weighing everything up, I'm not sure there is a single one-line solution that accounts for every single possible scenario. You will need to make sure the jQuery code tallies with your markup.

\n\n

I think the safest solution is probably to ensure your table always includes at least one tbody in your markup, even if it has no rows. On this basis, you can use the following which will work however many rows you have (and also account for multiple tbody elements):

\n\n
$('#myTable > tbody:last-child').append('<tr>...</tr><tr>...</tr>');\n
\n", + "upvotes": 3816, + "upvoterUsernames": [], + "downvotes": 1542, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf0", + "creator": "ChadT", + "createdAt": 1232606823000, + "text": "

What if you had a <tbody> and a <tfoot>?

\n\n

Such as:

\n\n
<table>\n    <tbody>\n        <tr><td>Foo</td></tr>\n    </tbody>\n    <tfoot>\n        <tr><td>footer information</td></tr>\n    </tfoot>\n</table>\n
\n\n

Then it would insert your new row in the footer - not to the body.

\n\n

Hence the best solution is to include a <tbody> tag and use .append, rather than .after.

\n\n
$(\"#myTable > tbody\").append(\"<tr><td>row content</td></tr>\");\n
\n", + "upvotes": 231, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf3", + "creator": "Avron Olshewsky", + "createdAt": 1254770221000, + "text": "

I was having some related issues, trying to insert a table row after the clicked row. All is fine except the .after() call does not work for the last row.

\n\n
$('#traffic tbody').find('tr.trafficBody).filter(':nth-child(' + (column + 1) + ')').after(insertedhtml);\n
\n\n

I landed up with a very untidy solution:

\n\n

create the table as follows (id for each row):

\n\n
<tr id=\"row1\"> ... </tr>\n<tr id=\"row2\"> ... </tr>\n<tr id=\"row3\"> ... </tr>\n
\n\n

etc ...

\n\n

and then :

\n\n
$('#traffic tbody').find('tr.trafficBody' + idx).after(html);\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ed082fcc3049e925b1", + "creator": "Darryl Hein", + "createdAt": 1254771314000, + "text": "I think this should be a new question, instead of an answer to an existing one...", + "upvotes": 877, + "upvoterUsernames": [], + "downvotes": 877, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bf1", + "creator": "Uzbekjon", + "createdAt": 1236000108000, + "text": "

You can use this great jQuery add table row function. It works great with tables that have <tbody> and that don't. Also it takes into the consideration the colspan of your last table row.

\n\n

Here is an example usage:

\n\n
// One table\naddTableRow($('#myTable'));\n// add table row to number of tables\naddTableRow($('.myTables'));\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ed082fcc3049e925b3", + "creator": "Yevgeniy Afanasyev", + "createdAt": 1442381908000, + "text": "If you propose the function that is not a part of a common package, please put the full function code here. Thank you.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bf2", + "creator": "Neil", + "createdAt": 1250263837000, + "text": "

jQuery has a built-in facility to manipulate DOM elements on the fly.

\n\n

You can add anything to your table like this:

\n\n
$(\"#tableID\").find('tbody')\n    .append($('<tr>')\n        .append($('<td>')\n            .append($('<img>')\n                .attr('src', 'img.png')\n                .text('Image cell')\n            )\n        )\n    );\n
\n\n

The $('<some-tag>') thing in jQuery is a tag object that can have several attr attributes that can be set and get, as well as text, which represents the text between the tag here: <tag>text</tag>.

\n\n

This is some pretty weird indenting, but it's easier for you to see what's going on in this example.

\n", + "upvotes": 1384, + "upvoterUsernames": [], + "downvotes": 562, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf5", + "creator": "Curtor", + "createdAt": 1287783265000, + "text": "

I recommend

\n\n
$('#myTable > tbody:first').append('<tr>...</tr><tr>...</tr>'); \n
\n\n

as opposed to

\n\n
$('#myTable > tbody:last').append('<tr>...</tr><tr>...</tr>'); \n
\n\n

The first and last keywords work on the first or last tag to be started, not closed. Therefore, this plays nicer with nested tables, if you don't want the nested table to be changed, but instead add to the overall table. At least, this is what I found.

\n\n
<table id=myTable>\n  <tbody id=first>\n    <tr><td>\n      <table id=myNestedTable>\n        <tbody id=last>\n        </tbody>\n      </table>\n    </td></tr>\n  </tbody>\n</table>\n
\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf4", + "creator": "Nischal", + "createdAt": 1264512855000, + "text": "

This can be done easily using the \"last()\" function of jQuery.

\n\n
$(\"#tableId\").last().append(\"<tr><td>New row</td></tr>\");\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ed082fcc3049e925b7", + "creator": "Darryl Hein", + "createdAt": 1264525988000, + "text": "Good idea, but I think you'd want to change the selector to #tableId tr as right now you'd get the table add the row below the table.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bf6", + "creator": "Dominic", + "createdAt": 1292477414000, + "text": "

I'm using this way when there is not any row in the table, as well as, each row is quite complicated.

\n\n

style.css:

\n\n
...\n#templateRow {\n  display:none;\n}\n...\n
\n\n

xxx.html

\n\n
...\n<tr id=\"templateRow\"> ... </tr>\n...\n\n$(\"#templateRow\").clone().removeAttr(\"id\").appendTo( $(\"#templateRow\").parent() );\n\n...\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf7", + "creator": "Oshua", + "createdAt": 1295648503000, + "text": "

For the best solution posted here, if there's a nested table on the last row, the new row will be added to the nested table instead of the main table. A quick solution (considering tables with/without tbody and tables with nested tables):

\n\n
function add_new_row(table, rowcontent) {\n    if ($(table).length > 0) {\n        if ($(table + ' > tbody').length == 0) $(table).append('<tbody />');\n        ($(table + ' > tr').length > 0) ? $(table).children('tbody:last').children('tr:last').append(rowcontent): $(table).children('tbody:last').append(rowcontent);\n    }\n}\n
\n\n

Usage example:

\n\n
add_new_row('#myTable','<tr><td>my new row</td></tr>');\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf8", + "creator": "Vitalii Fedorenko", + "createdAt": 1323047397000, + "text": "

I found this AddRow plugin quite useful for managing table rows. Though, Luke's solution would be the best fit if you just need to add a new row.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bfa", + "creator": "Pavel Shkleinik", + "createdAt": 1334314032000, + "text": "\n\n
<table id=myTable>\n    <tr><td></td></tr>\n    <style=\"height=0px;\" tfoot></tfoot>\n</table>\n
\n\n\n\n

You can cache the footer variable and reduce access to DOM (Note: may be it will be better to use a fake row instead of footer).

\n\n
var footer = $(\"#mytable tfoot\")\nfooter.before(\"<tr><td></td></tr>\")\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bfb", + "creator": "Alexey Pavlov", + "createdAt": 1334832815000, + "text": "
$('#myTable').append('<tr><td>my data</td><td>more data</td></tr>');\n
\n\n

will add a new row to the first TBODY of the table, without depending of any THEAD or TFOOT present.\n(I didn't find information from which version of jQuery .append() this behavior is present.)

\n\n

You may try it in these examples:

\n\n
<table class=\"t\"> <!-- table with THEAD, TBODY and TFOOT -->\n<thead>\n  <tr><th>h1</th><th>h2</th></tr>\n</thead>\n<tbody>\n  <tr><td>1</td><td>2</td></tr>\n</tbody>\n<tfoot>\n  <tr><th>f1</th><th>f2</th></tr>\n</tfoot>\n</table><br>\n\n<table class=\"t\"> <!-- table with two TBODYs -->\n<thead>\n  <tr><th>h1</th><th>h2</th></tr>\n</thead>\n<tbody>\n  <tr><td>1</td><td>2</td></tr>\n</tbody>\n<tbody>\n  <tr><td>3</td><td>4</td></tr>\n</tbody>\n<tfoot>\n  <tr><th>f1</th><th>f2</th></tr>\n</tfoot>\n</table><br>\n\n<table class=\"t\">  <!-- table without TBODY -->\n<thead>\n  <tr><th>h1</th><th>h2</th></tr>\n</thead>\n</table><br>\n\n<table class=\"t\">  <!-- table with TR not in TBODY  -->\n  <tr><td>1</td><td>2</td></tr>\n</table>\n<br>\n<table class=\"t\">\n</table>\n\n<script>\n$('.t').append('<tr><td>a</td><td>a</td></tr>');\n</script>\n
\n\n

In which example a b row is inserted after 1 2, not after 3 4 in second example. If the table were empty, jQuery creates TBODY for a new row.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bf9", + "creator": "Ricky G", + "createdAt": 1331254083000, + "text": "

My solution:

\n\n
//Adds a new table row\n$.fn.addNewRow = function (rowId) {\n    $(this).find('tbody').append('<tr id=\"' + rowId + '\"> </tr>');\n};\n
\n\n

usage:

\n\n
$('#Table').addNewRow(id1);\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ee082fcc3049e925bc", + "creator": "Chaos", + "createdAt": 1387742011000, + "text": "I think if you're going to make this a function you could perhaps return a jQuery object for the new row so it's chainable.", + "upvotes": 3402, + "upvoterUsernames": [], + "downvotes": 3402, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bfd", + "creator": "Salman von Abbas", + "createdAt": 1338927168000, + "text": "

So things have changed ever since @Luke Bennett answered this question. Here is an update.

\n\n

jQuery since version 1.4(?) automatically detects if the element you are trying to insert (using any of the append(), prepend(), before(), or after() methods) is a <tr> and inserts it into the first <tbody> in your table or wraps it into a new <tbody> if one doesn't exist.

\n\n

So yes your example code is acceptable and will work fine with jQuery 1.4+. ;)

\n\n
$('#myTable').append('<tr><td>my data</td><td>more data</td></tr>');\n
\n", + "upvotes": 600, + "upvoterUsernames": [], + "downvotes": 266, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ee082fcc3049e925bf", + "creator": "Tristanisginger", + "createdAt": 1653918466000, + "text": "nice, +1, me too", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bfc", + "creator": "shashwat", + "createdAt": 1334943523000, + "text": "

I know you have asked for a jQuery method. I looked a lot and find that we can do it in a better way than using JavaScript directly by the following function.

\n
tableObject.insertRow(index)\n
\n

index is an integer that specifies the position of the row to insert (starts at 0). The value of -1 can also be used; which result in that the new row will be inserted at the last position.

\n

This parameter is required in Firefox and Opera, but it is optional in Internet Explorer, Chrome and Safari.

\n

If this parameter is omitted, insertRow() inserts a new row at the last position in Internet Explorer and at the first position in Chrome and Safari.

\n

It will work for every acceptable structure of HTML table.

\n

The following example will insert a row in last (-1 is used as index):

\n
<html>\n    <head>\n        <script type="text/javascript">\n        function displayResult()\n        {\n            document.getElementById("myTable").insertRow(-1).innerHTML = '<td>1</td><td>2</td>';\n        }\n        </script>\n    </head>\n\n    <body>       \n        <table id="myTable" border="1">\n            <tr>\n                <td>cell 1</td>\n                <td>cell 2</td>\n            </tr>\n            <tr>\n                <td>cell 3</td>\n                <td>cell 4</td>\n            </tr>\n        </table>\n        <br />\n        <button type="button" onclick="displayResult()">Insert new row</button>            \n    </body>\n</html>\n
\n

I hope it helps.

\n", + "upvotes": 151, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bfe", + "creator": "Jaid07", + "createdAt": 1369041290000, + "text": "

As i have also got a way too add row at last or any specific place so i think i should also share this:

\n\n

First find out the length or rows:

\n\n
var r=$(\"#content_table\").length;\n
\n\n

and then use below code to add your row:

\n\n
$(\"#table_id\").eq(r-1).after(row_html);\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bff", + "creator": "d.raev", + "createdAt": 1377776158000, + "text": "

To add a good example on the topic, here is working solution if you need to add a row at specific position.

\n\n

The extra row is added after the 5th row, or at the end of the table if there are less then 5 rows.

\n\n
var ninja_row = $('#banner_holder').find('tr');\n\nif( $('#my_table tbody tr').length > 5){\n    $('#my_table tbody tr').filter(':nth-child(5)').after(ninja_row);\n}else{\n    $('#my_table tr:last').after(ninja_row);\n}\n
\n\n

I put the content on a ready (hidden) container below the table ..so if you(or the designer) have to change it is not required to edit the JS.

\n\n
<table id=\"banner_holder\" style=\"display:none;\"> \n    <tr>\n        <td colspan=\"3\">\n            <div class=\"wide-banner\"></div>\n        </td>   \n    </tr> \n</table>\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c01", + "creator": "Vivek S", + "createdAt": 1387626885000, + "text": "
<tr id=\"tablerow\"></tr>\n\n$('#tablerow').append('<tr>...</tr><tr>...</tr>');\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c00", + "creator": "Praveena M", + "createdAt": 1380690373000, + "text": "

If you are using Datatable JQuery plugin you can try.

\n\n
oTable = $('#tblStateFeesSetup').dataTable({\n            \"bScrollCollapse\": true,\n            \"bJQueryUI\": true,\n            ...\n            ...\n            //Custom Initializations.\n            });\n\n//Data Row Template of the table.\nvar dataRowTemplate = {};\ndataRowTemplate.InvoiceID = '';\ndataRowTemplate.InvoiceDate = '';\ndataRowTemplate.IsOverRide = false;\ndataRowTemplate.AmountOfInvoice = '';\ndataRowTemplate.DateReceived = '';\ndataRowTemplate.AmountReceived = '';\ndataRowTemplate.CheckNumber = '';\n\n//Add dataRow to the table.\noTable.fnAddData(dataRowTemplate);\n
\n\n

Refer Datatables fnAddData Datatables API

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c02", + "creator": "SNEH PANDYA", + "createdAt": 1391148838000, + "text": "

I Guess i have done in my project , here it is:

\n\n

html

\n\n
<div class=\"container\">\n    <div class = \"row\">\n    <div class = \"span9\">\n        <div class = \"well\">\n          <%= form_for (@replication) do |f| %>\n    <table>\n    <tr>\n      <td>\n          <%= f.label :SR_NO %>\n      </td>\n      <td>\n          <%= f.text_field :sr_no , :id => \"txt_RegionName\" %>\n      </td>\n    </tr>\n    <tr>\n      <td>\n        <%= f.label :Particular %>\n      </td>\n      <td>\n        <%= f.text_area :particular , :id => \"txt_Region\" %>\n      </td>\n    </tr>\n    <tr>\n      <td>\n        <%= f.label :Unit %>\n      </td>\n      <td>\n        <%= f.text_field :unit ,:id => \"txt_Regio\" %>\n      </td>\n      </tr>\n      <tr>\n\n      <td> \n        <%= f.label :Required_Quantity %>\n      </td>\n      <td>\n        <%= f.text_field :quantity ,:id => \"txt_Regi\" %>\n      </td>\n    </tr>\n    <tr>\n    <td></td>\n    <td>\n    <table>\n    <tr><td>\n    <input type=\"button\"  name=\"add\" id=\"btn_AddToList\" value=\"add\" class=\"btn btn-primary\" />\n    </td><td><input type=\"button\"  name=\"Done\" id=\"btn_AddToList1\" value=\"Done\" class=\"btn btn-success\" />\n    </td></tr>\n    </table>\n    </td>\n    </tr>\n    </table>\n    <% end %>\n    <table id=\"lst_Regions\" style=\"width: 500px;\" border= \"2\" class=\"table table-striped table-bordered table-condensed\">\n    <tr>\n    <td>SR_NO</td>\n    <td>Item Name</td>\n    <td>Particular</td>\n    <td>Cost</td>\n    </tr>\n    </table>\n    <input type=\"button\" id= \"submit\" value=\"Submit Repication\"  class=\"btn btn-success\" />\n    </div>\n    </div>\n    </div>\n</div>\n
\n\n

js

\n\n
$(document).ready(function() {     \n    $('#submit').prop('disabled', true);\n    $('#btn_AddToList').click(function () {\n     $('#submit').prop('disabled', true);\n    var val = $('#txt_RegionName').val();\n    var val2 = $('#txt_Region').val();\n    var val3 = $('#txt_Regio').val();\n    var val4 = $('#txt_Regi').val();\n    $('#lst_Regions').append('<tr><td>' + val + '</td>' + '<td>' + val2 + '</td>' + '<td>' + val3 + '</td>' + '<td>' + val4 + '</td></tr>');\n    $('#txt_RegionName').val('').focus();\n    $('#txt_Region').val('');\n        $('#txt_Regio').val('');\n        $('#txt_Regi').val('');\n    $('#btn_AddToList1').click(function () {\n         $('#submit').prop('disabled', false).addclass('btn btn-warning');\n    });\n      });\n});\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c03", + "creator": "GabrielOshiro", + "createdAt": 1399809863000, + "text": "

Neil's answer is by far the best one. However things get messy really fast. My suggestion would be to use variables to store elements and append them to the DOM hierarchy.

\n\n

HTML

\n\n
<table id=\"tableID\">\n    <tbody>\n    </tbody>\n</table>\n
\n\n

JAVASCRIPT

\n\n
// Reference to the table body\nvar body = $(\"#tableID\").find('tbody');\n\n// Create a new row element\nvar row = $('<tr>');\n\n// Create a new column element\nvar column = $('<td>');\n\n// Create a new image element\nvar image = $('<img>');\nimage.attr('src', 'img.png');\nimage.text('Image cell');\n\n// Append the image to the column element\ncolumn.append(image);\n// Append the column to the row element\nrow.append(column);\n// Append the row to the table body\nbody.append(row);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c04", + "creator": "sulest", + "createdAt": 1404132029000, + "text": "

In my opinion the fastest and clear way is

\n
//Try to get tbody first with jquery children. works faster!\nvar tbody = $('#myTable').children('tbody');\n\n//Then if no tbody just select your table \nvar table = tbody.length ? tbody : $('#myTable');\n\n//Add row\ntable.append('<tr><td>hello></td></tr>');\n
\n

here is demo Fiddle

\n

Also I can recommend a small function to make more html changes

\n
//Compose template string\nString.prototype.compose = (function (){\nvar re = /\\{{(.+?)\\}}/g;\nreturn function (o){\n        return this.replace(re, function (_, k){\n            return typeof o[k] != 'undefined' ? o[k] : '';\n        });\n    }\n}());\n
\n

If you use my string composer you can do this like

\n
var tbody = $('#myTable').children('tbody');\nvar table = tbody.length ? tbody : $('#myTable');\nvar row = '<tr>'+\n    '<td>{{id}}</td>'+\n    '<td>{{name}}</td>'+\n    '<td>{{phone}}</td>'+\n'</tr>';\n\n\n//Add row\ntable.append(row.compose({\n    'id': 3,\n    'name': 'Lee',\n    'phone': '123 456 789'\n}));\n
\n

Here is demo\nFiddle

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ef082fcc3049e925c7", + "creator": "Mark Schultheiss", + "createdAt": 1538770652000, + "text": "If you have multiple tbody you can get multiple inserts", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ef082fcc3049e925c8", + "creator": "sulest", + "createdAt": 1539003031000, + "text": "Yes you can :) You can try $("SELECTOR").last() to limit your tag collection", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90c05", + "creator": "vipul sorathiya", + "createdAt": 1434456704000, + "text": "

In a simple way:

\n\n
$('#yourTableId').append('<tr><td>your data1</td><td>your data2</td><td>your data3</td></tr>');\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ef082fcc3049e925ca", + "creator": "abarisone", + "createdAt": 1434460188000, + "text": "Could you please elaborate more your answer adding a little more description about the solution you provide?", + "upvotes": 120, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90c07", + "creator": "Jakir Hossain", + "createdAt": 1453234173000, + "text": "
<table id=\"myTable\">\n  <tbody>\n    <tr>...</tr>\n    <tr>...</tr>\n  </tbody>\n  <tr>...</tr>\n</table>\n
\n\n

Write with a javascript function

\n\n
document.getElementById(\"myTable\").insertRow(-1).innerHTML = '<tr>...</tr><tr>...</tr>';\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c06", + "creator": "Pankaj Garg", + "createdAt": 1449375194000, + "text": "
    // Create a row and append to table\n    var row = $('<tr />', {})\n        .appendTo(\"#table_id\");\n\n    // Add columns to the row. <td> properties can be given in the JSON\n    $('<td />', {\n        'text': 'column1'\n    }).appendTo(row);\n\n    $('<td />', {\n        'text': 'column2',\n        'style': 'min-width:100px;'\n    }).appendTo(row);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ef082fcc3049e925cd", + "creator": "Jayan", + "createdAt": 1449379409000, + "text": "While this may be an answer, it is not very useful to a visitor later... Please add some explanation around your answer.", + "upvotes": 720, + "upvoterUsernames": [], + "downvotes": 720, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90c09", + "creator": "Sumon Sarker", + "createdAt": 1489655265000, + "text": "

If you want to add row before the <tr> first child.

\n\n
$(\"#myTable > tbody\").prepend(\"<tr><td>my data</td><td>more data</td></tr>\");\n
\n\n

If you want to add row after the <tr> last child.

\n\n
$(\"#myTable > tbody\").append(\"<tr><td>my data</td><td>more data</td></tr>\");\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c08", + "creator": "MEAbid", + "createdAt": 1476422282000, + "text": "

if you have another variable you can access in <td> tag like that try.

\n\n

This way I hope it would be helpful

\n\n
var table = $('#yourTableId');\nvar text  = 'My Data in td';\nvar image = 'your/image.jpg'; \nvar tr = (\n  '<tr>' +\n    '<td>'+ text +'</td>'+\n    '<td>'+ text +'</td>'+\n    '<td>'+\n      '<img src=\"' + image + '\" alt=\"yourImage\">'+\n    '</td>'+\n  '</tr>'\n);\n\n$('#yourTableId').append(tr);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c0b", + "creator": "Daniel Ortegón", + "createdAt": 1500518082000, + "text": "

This is my solution

\n\n
$('#myTable').append('<tr><td>'+data+'</td><td>'+other data+'</td>...</tr>');\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c0a", + "creator": "Prashant", + "createdAt": 1490858653000, + "text": "

This could also be done :

\n\n
$(\"#myTable > tbody\").html($(\"#myTable > tbody\").html()+\"<tr><td>my data</td><td>more data</td></tr>\")\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ef082fcc3049e925d2", + "creator": "Darryl Hein", + "createdAt": 1490886450000, + "text": "That seems quite complicated and maybe even slower for the browser/JS to deal with as it needs to parse a lot more HTML.", + "upvotes": 120, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90c0d", + "creator": "Hassan Sadeghi", + "createdAt": 1534579101000, + "text": "

TIP: Inserting rows in html table via innerHTML or .html() is not valid in some browsers (similar IE9), and using .append(\"<tr></tr>\") is not good suggestion in any browser. best and fastest way is using the pure javascript codes.

\n\n

for combine this way with jQuery, only add new plugin similar this to jQuery:

\n\n
$.fn.addRow=function(index/*-1: add to end  or  any desired index*/, cellsCount/*optional*/){\n    if(this[0].tagName.toLowerCase()!=\"table\") return null;\n    var i=0, c, r = this[0].insertRow((index<0||index>this[0].rows.length)?this[0].rows.length:index);\n    for(;i<cellsCount||0;i++) c = r.insertCell(); //you can use c for set its content or etc\n    return $(r);\n};\n
\n\n

And now use it in whole the project similar this:

\n\n
var addedRow = $(\"#myTable\").addRow(-1/*add to end*/, 2);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c0f", + "creator": "rajmobiapp", + "createdAt": 1553600871000, + "text": "

Try this : very simple way

\n\n

\r\n
\r\n
$('<tr><td>3</td></tr><tr><td>4</td></tr>').appendTo(\"#myTable tbody\");
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\r\n<table id=\"myTable\">\r\n  <tbody>\r\n    <tr><td>1</td></tr>\r\n    <tr><td>2</td></tr>\r\n  </tbody>\r\n</table>
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c0e", + "creator": "Rizwan", + "createdAt": 1549394859000, + "text": "

Add tabe row using JQuery:

\n\n

if you want to add row after last of table's row child, you can try this

\n\n
$('#myTable tr:last').after('<tr>...</tr><tr>...</tr>');\n
\n\n

if you want to add row 1st of table's row child, you can try this

\n\n
$('#myTable tr').after('<tr>...</tr><tr>...</tr>');\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c0c", + "creator": "Anton vBR", + "createdAt": 1507830873000, + "text": "

I solved it this way.

\n\n

Using jquery

\n\n
$('#tab').append($('<tr>')\n    .append($('<td>').append(\"text1\"))\n    .append($('<td>').append(\"text2\"))\n    .append($('<td>').append(\"text3\"))\n    .append($('<td>').append(\"text4\"))\n  )\n
\n\n
\n\n

Snippet

\n\n

\r\n
\r\n
$('#tab').append($('<tr>')\r\n  .append($('<td>').append(\"text1\"))\r\n  .append($('<td>').append(\"text2\"))\r\n  .append($('<td>').append(\"text3\"))\r\n  .append($('<td>').append(\"text4\"))\r\n)
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n\r\n<table id=\"tab\">\r\n  <tr>\r\n    <th>Firstname</th>\r\n    <th>Lastname</th> \r\n    <th>Age</th>\r\n    <th>City</th>\r\n  </tr>\r\n  <tr>\r\n    <td>Jill</td>\r\n    <td>Smith</td> \r\n    <td>50</td>\r\n    <td>New York</td>\r\n  </tr>\r\n</table>
\r\n
\r\n
\r\n

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f0082fcc3049e925d6", + "creator": "Anton vBR", + "createdAt": 1548397321000, + "text": "I think you need to call an update function of some sort. Hade to tell without an example.", + "upvotes": 909, + "upvoterUsernames": [], + "downvotes": 909, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90c10", + "creator": "Hien Nguyen", + "createdAt": 1557639578000, + "text": "

The answers above are very helpful, but when student refer this link to add data from form they often require a sample.

\n\n

I want to contribute an sample get input from from and use .after() to insert tr to table using string interpolation.

\n\n
function add(){\n  let studentname = $(\"input[name='studentname']\").val();\n  let studentmark = $(\"input[name='studentmark']\").val();\n\n  $('#student tr:last').after(`<tr><td>${studentname}</td><td>${studentmark}</td></tr>`);\n}\n
\n\n

\r\n
\r\n
function add(){\r\nlet studentname = $(\"input[name='studentname']\").val();\r\nlet studentmark = $(\"input[name='studentmark']\").val();\r\n\r\n$('#student tr:last').after(`<tr><td>${studentname}</td><td>${studentmark}</td></tr>`);\r\n}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<style>\r\ntable {\r\n  font-family: arial, sans-serif;\r\n  border-collapse: collapse;\r\n  width: 100%;\r\n}\r\n\r\ntd, th {\r\n  border: 1px solid #dddddd;\r\n  text-align: left;\r\n  padding: 8px;\r\n}\r\n\r\ntr:nth-child(even) {\r\n  background-color: #dddddd;\r\n}\r\n</style>\r\n</head>\r\n<body>\r\n<form>\r\n<input type='text' name='studentname' />\r\n<input type='text' name='studentmark' />\r\n<input type='button' onclick=\"add()\" value=\"Add new\" />\r\n</form>\r\n<table id='student'>\r\n  <thead>\r\n    <th>Name</th>\r\n    <th>Mark</th>\r\n  </thead>\r\n</table>\r\n</body>\r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c11", + "creator": "Hemant N. Karmur", + "createdAt": 1579884841000, + "text": "

To add a new row at the last of current row, you can use like this

\n\n
$('#yourtableid tr:last').after('<tr>...</tr><tr>...</tr>');\n
\n\n

You can append multiple row as above. Also you can add inner data as like

\n\n
$('#yourtableid tr:last').after('<tr><td>your data</td></tr>');\n
\n\n

in another way you can do like this

\n\n
let table = document.getElementById(\"tableId\");\n\nlet row = table.insertRow(1); // pass position where you want to add a new row\n\n\n//then add cells as you want with index\nlet cell0 = row.insertCell(0);\nlet cell1 = row.insertCell(1);\nlet cell2 = row.insertCell(2);\nlet cell3 = row.insertCell(3);\n\n\n//add value to added td cell\n cell0.innerHTML = \"your td content here\";\n cell1.innerHTML = \"your td content here\";\n cell2.innerHTML = \"your td content here\";\n cell3.innerHTML = \"your td content here\";\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c13", + "creator": "Kamil Kiełczewski", + "createdAt": 1589399429000, + "text": "

Pure JS is quite short in your case

\n\n
myTable.firstChild.innerHTML += '<tr><td>my data</td><td>more data</td></tr>'\n
\n\n

\r\n
\r\n
function add() {\r\n  myTable.firstChild.innerHTML+=`<tr><td>date</td><td>${+new Date}</td></tr>`\r\n}
\r\n
td {border: 1px solid black;}
\r\n
<button onclick=\"add()\">Add</button><br>\r\n<table id=\"myTable\"><tbody></tbody> </table>
\r\n
\r\n
\r\n

\n\n

(if we remove <tbody> and firstChild it will also works but wrap every row with <tbody>)

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c12", + "creator": "Nikhil B", + "createdAt": 1580295872000, + "text": "

Here, You can Just Click on button then you will get Output. When You Click on Add row button then one more row Added.

\n\n

I hope It is very helpful.

\n\n
<html> \n\n<head> \n    <script src= \n            \"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"> \n    </script> \n\n    <style> \n        table { \n            margin: 25px 0; \n            width: 200px; \n        } \n\n        table th, table td { \n            padding: 10px; \n            text-align: center; \n        } \n\n        table, th, td { \n            border: 1px solid; \n        } \n    </style> \n</head> \n\n<body> \n\n    <b>Add table row in jQuery</b> \n\n    <p> \n        Click on the button below to \n        add a row to the table \n    </p> \n\n    <button class=\"add-row\"> \n        Add row \n    </button> \n\n    <table> \n        <thead> \n            <tr> \n                <th>Rows</th> \n            </tr> \n        </thead> \n        <tbody> \n            <tr> \n                <td>This is row 0</td> \n            </tr> \n        </tbody> \n    </table> \n\n    <!-- Script to add table row -->\n    <script> \n        let rowno = 1; \n        $(document).ready(function () { \n            $(\".add-row\").click(function () { \n                rows = \"<tr><td>This is row \" \n                    + rowno + \"</td></tr>\"; \n                tableBody = $(\"table tbody\"); \n                tableBody.append(rows); \n                rowno++; \n            }); \n        }); \n    </script> \n</body> \n</html>                  \n
\n", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 67, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c14", + "creator": "Rinto George", + "createdAt": 1599395984000, + "text": "

I have tried the most upvoted one, but it did not work for me, but below works well.

\n
$('#mytable tr').last().after('<tr><td></td></tr>');\n
\n

Which will work even there is a tobdy there.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c15", + "creator": "Mark", + "createdAt": 1612059651000, + "text": "

)Daryl:

\n

You can append it to the tbody using the appendTo method like this:

\n
$(() => {\n   $("<tr><td>my data</td><td>more data</td></tr>").appendTo("tbody");\n});\n
\n

You'll probably want to use the latest JQuery and ECMAScript. Then you can use a back-end language to add your data to the table. You can also wrap it in a variable like so:

\n
$(() => {\n  var t_data = $('<tr><td>my data</td><td>more data</td></tr>');\n  t_data.appendTo('tbody');\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c17", + "creator": "Sahil Thummar", + "createdAt": 1650431571000, + "text": "
    \n
  1. Using jQuery .append()
  2. \n
  3. Using jQuery .appendTo()
  4. \n
  5. Using jQuery .after()
  6. \n
  7. Using Javascript .insertRow()
  8. \n
  9. Using jQuery - add html row
  10. \n
\n

Try This:

\n

\r\n
\r\n
// Using jQuery - append\n$('#myTable > tbody').append('<tr><td>3</td><td>Smith Patel</td></tr>');\n\n// Using jQuery - appendTo\n$('<tr><td>4</td><td>J. Thomson</td></tr>').appendTo(\"#myTable > tbody\");\n\n// Using jQuery - add html row\nlet tBodyHtml = $('#myTable > tbody').html();\ntBodyHtml += '<tr><td>5</td><td>Patel S.</td></tr>';\n$('#myTable > tbody').html(tBodyHtml);\n\n// Using jQuery - after\n$('#myTable > tbody tr:last').after('<tr><td>6</td><td>Angel Bruice</td></tr>');\n\n// Using JavaScript - insertRow\nconst tableBody = document.getElementById('myTable').getElementsByTagName('tbody')[0];\nconst newRow = tableBody.insertRow(tableBody.rows.length);\nnewRow.innerHTML = '<tr><td>7</td><td>K. Ashwin</td></tr>';
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n<table id=\"myTable\">\n    <thead>\n        <tr>\n            <th>Id</th>\n            <th>Name</th>\n        </tr>\n    </thead>\n    <tbody>\n        <tr>\n            <td>1</td>\n            <td>John Smith</td>\n        </tr>\n        <tr>\n            <td>2</td>\n            <td>Tom Adam</td>\n        </tr>\n    </tbody>\n</table>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90c16", + "creator": "mostafa toloo", + "createdAt": 1643119322000, + "text": "
var html = $('#myTableBody').html();\nhtml += '<tr><td>my data</td><td>more data</td></tr>';\n$('#myTableBody').html(html);\n
\n

or

\n
$('#myTableBody').html($('#myTableBody').html() + '<tr><td>my data</td><td>more data</td></tr>');\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925e2", + "creator": "mostafa toloo", + "createdAt": 1643119690000, + "text": "variable fixed, thank you. I used body html not entire table.", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90bb0", + "creator": "Darryl Hein", + "createdAt": 1254783256000, + "text": "Because of this: google.com/search?q=jquery+add+table+row", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bb1", + "creator": "Gerrit Brink", + "createdAt": 1271410360000, + "text": "FYI - Avoid using multiple appends (slows down performance tremendously), rather build up your string or use JavaScript join which is much faster.", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bb2", + "creator": "dev", + "createdAt": 1526459074000, + "text": "see: stackoverflow.com/a/50365764/7186739", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1926631, + "uvac": 1926672 + } + }, + { + "_id": "62f321bb082fcc3049e8fedd", + "title": "How can I merge properties of two JavaScript objects dynamically?", + "title-lowercase": "how can i merge properties of two javascript objects dynamically?", + "creator": "JC Grubbs", + "createdAt": 1223166654000, + "status": "open", + "text": "

I need to be able to merge two (very simple) JavaScript objects at runtime. For example I'd like to:

\n
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog' }\n\nobj1.merge(obj2);\n\n//obj1 now has three properties: food, car, and animal\n
\n

Is there a built in way to do this? I do not need recursion, and I do not need to merge functions, just methods on flat objects.

\n", + "upvotes": 5402, + "upvoterUsernames": [], + "downvotes": 2342, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1740512, + "answers": 65, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e90903", + "creator": "John Millikin", + "createdAt": 1223166805000, + "text": "

ECMAScript 2018 Standard Method

\n

You would use object spread:

\n
let merged = {...obj1, ...obj2};\n
\n

merged is now the union of obj1 and obj2. Properties in obj2 will overwrite those in obj1.

\n
/** There's no limit to the number of objects you can merge.\n *  Later properties overwrite earlier properties with the same name. */\nconst allRules = {...obj1, ...obj2, ...obj3};\n
\n

Here is also the MDN documentation for this syntax. If you're using babel you'll need the babel-plugin-transform-object-rest-spread plugin for it to work.

\n

ECMAScript 2015 (ES6) Standard Method

\n
/* For the case in question, you would do: */\nObject.assign(obj1, obj2);\n\n/** There's no limit to the number of objects you can merge.\n *  All objects get merged into the first object. \n *  Only the object in the first argument is mutated and returned.\n *  Later properties overwrite earlier properties with the same name. */\nconst allRules = Object.assign({}, obj1, obj2, obj3, etc);\n
\n

(see MDN JavaScript Reference)

\n
\n

Method for ES5 and Earlier

\n
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }\n
\n

Note that this will simply add all attributes of obj2 to obj1 which might not be what you want if you still want to use the unmodified obj1.

\n

If you're using a framework that craps all over your prototypes then you have to get fancier with checks like hasOwnProperty, but that code will work for 99% of cases.

\n

Example function:

\n
/**\n * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1\n * @param obj1\n * @param obj2\n * @returns obj3 a new object based on obj1 and obj2\n */\nfunction merge_options(obj1,obj2){\n    var obj3 = {};\n    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }\n    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }\n    return obj3;\n}\n
\n", + "upvotes": 5310, + "upvoterUsernames": [], + "downvotes": 1614, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3267b082fcc3049e920c6", + "creator": "Xiè Jìléi", + "createdAt": 1287917779000, + "text": "This doesn't work if objects have same name attributes, and you would also want to merge the attributes.", + "upvotes": 150, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [] + }, + { + "_id": "62f3267b082fcc3049e920c8", + "creator": "Jay Taylor", + "createdAt": 1307029192000, + "text": "This only does a shallow copy/merge. Has the potential to clobber a lot of elements.", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [] + }, + { + "_id": "62f3267b082fcc3049e920ca", + "creator": "thejonwithnoh", + "createdAt": 1462594687000, + "text": "​+1 for acknowledging that some poor souls are forced to use frameworks that crap all over their prototypes...", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90904", + "creator": "ephemient", + "createdAt": 1223166961000, + "text": "

Prototype has this:

\n\n
Object.extend = function(destination,source) {\n    for (var property in source)\n        destination[property] = source[property];\n    return destination;\n}\n
\n\n

obj1.extend(obj2) will do what you want.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90905", + "creator": "Avdi", + "createdAt": 1223179000000, + "text": "

jQuery also has a utility for this: http://api.jquery.com/jQuery.extend/.

\n\n

Taken from the jQuery documentation:

\n\n
// Merge options object into settings object\nvar settings = { validate: false, limit: 5, name: \"foo\" };\nvar options  = { validate: true, name: \"bar\" };\njQuery.extend(settings, options);\n\n// Now the content of settings object is the following:\n// { validate: true, limit: 5, name: \"bar\" }\n
\n\n

The above code will mutate the existing object named settings.

\n\n
\n\n

If you want to create a new object without modifying either argument, use this:

\n\n
var defaults = { validate: false, limit: 5, name: \"foo\" };\nvar options = { validate: true, name: \"bar\" };\n\n/* Merge defaults and options, without modifying defaults */\nvar settings = $.extend({}, defaults, options);\n\n// The content of settings variable is now the following:\n// {validate: true, limit: 5, name: \"bar\"}\n// The 'defaults' and 'options' variables remained the same.\n
\n", + "upvotes": 2289, + "upvoterUsernames": [], + "downvotes": 1057, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90906", + "creator": "Markus", + "createdAt": 1229774747000, + "text": "

I googled for code to merge object properties and ended up here. However since there wasn't any code for recursive merge I wrote it myself. (Maybe jQuery extend is recursive BTW?) Anyhow, hopefully someone else will find it useful as well.

\n\n

(Now the code does not use Object.prototype :)

\n\n

Code

\n\n
/*\n* Recursively merge properties of two objects \n*/\nfunction MergeRecursive(obj1, obj2) {\n\n  for (var p in obj2) {\n    try {\n      // Property in destination object set; update its value.\n      if ( obj2[p].constructor==Object ) {\n        obj1[p] = MergeRecursive(obj1[p], obj2[p]);\n\n      } else {\n        obj1[p] = obj2[p];\n\n      }\n\n    } catch(e) {\n      // Property in destination object not set; create it and set its value.\n      obj1[p] = obj2[p];\n\n    }\n  }\n\n  return obj1;\n}\n
\n\n

An example

\n\n
o1 = {  a : 1,\n        b : 2,\n        c : {\n          ca : 1,\n          cb : 2,\n          cc : {\n            cca : 100,\n            ccb : 200 } } };\n\no2 = {  a : 10,\n        c : {\n          ca : 10,\n          cb : 20, \n          cc : {\n            cca : 101,\n            ccb : 202 } } };\n\no3 = MergeRecursive(o1, o2);\n
\n\n

Produces object o3 like

\n\n
o3 = {  a : 10,\n        b : 2,\n        c : {\n          ca : 10,\n          cb : 20,\n          cc : { \n            cca : 101,\n            ccb : 202 } } };\n
\n", + "upvotes": 407, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267b082fcc3049e920ce", + "creator": "skerit", + "createdAt": 1295193646000, + "text": "Nice, but I would make a deepcopy of the objects first. This way o1 would be modified too, as objects are passed by reference.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90907", + "creator": "Christoph", + "createdAt": 1229864521000, + "text": "

The given solutions should be modified to check source.hasOwnProperty(property) in the for..in loops before assigning - otherwise, you end up copying the properties of the whole prototype chain, which is rarely desired...

\n", + "upvotes": 69, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090b", + "creator": "David Coallier", + "createdAt": 1289340575000, + "text": "

The best way for you to do this is to add a proper property that is non-enumerable using Object.defineProperty.

\n\n

This way you will still be able to iterate over your objects properties without having the newly created \"extend\" that you would get if you were to create the property with Object.prototype.extend.

\n\n

Hopefully this helps:

\n\n
\nObject.defineProperty(Object.prototype, \"extend\", {\n    enumerable: false,\n    value: function(from) {\n        var props = Object.getOwnPropertyNames(from);\n        var dest = this;\n        props.forEach(function(name) {\n            if (name in dest) {\n                var destination = Object.getOwnPropertyDescriptor(from, name);\n                Object.defineProperty(dest, name, destination);\n            }\n        });\n        return this;\n    }\n});\n
\n\n

Once you have that working, you can do:

\n\n
\nvar obj = {\n    name: 'stack',\n    finish: 'overflow'\n}\nvar replacement = {\n    name: 'stock'\n};\n\nobj.extend(replacement);\n
\n\n

I just wrote a blog post about it here: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90909", + "creator": "philfreo", + "createdAt": 1249070798000, + "text": "

In MooTools, there's Object.merge():

\n\n
Object.merge(obj1, obj2);\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090a", + "creator": "Algy", + "createdAt": 1267211638000, + "text": "

For not-too-complicated objects you could use JSON:

\n\n
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog', car: 'chevy'}\nvar objMerge;\n\nobjMerge = JSON.stringify(obj1) + JSON.stringify(obj2);\n\n// {\"food\": \"pizza\",\"car\":\"ford\"}{\"animal\":\"dog\",\"car\":\"chevy\"}\n\nobjMerge = objMerge.replace(/\\}\\{/, \",\"); //  \\_ replace with comma for valid JSON\n\nobjMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}\n// Of same keys in both objects, the last object's value is retained_/\n
\n\n

Mind you that in this example \"}{\" must not occur within a string!

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90908", + "creator": "Tobi", + "createdAt": 1236948494000, + "text": "

The correct implementation in Prototype should look like this:

\n\n
var obj1 = {food: 'pizza', car: 'ford'}\nvar obj2 = {animal: 'dog'}\n\nobj1 = Object.extend(obj1, obj2);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090c", + "creator": "antony", + "createdAt": 1309524333000, + "text": "

I'm kind of getting started with JavaScript, so correct me if I'm wrong.

\n\n

But wouldn't it be better if you could merge any number of objects? Here's how I do it using the native Arguments object.

\n\n

The key to is that you can actually pass any number of arguments to a JavaScript function without defining them in the function declaration. You just can't access them without using the Arguments object.

\n\n
function mergeObjects() (\n    var tmpObj = {};\n\n    for(var o in arguments) {\n        for(var m in arguments[o]) {\n            tmpObj[m] = arguments[o][m];\n        }\n    }\n    return tmpObj;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090d", + "creator": "RobKohr", + "createdAt": 1310223186000, + "text": "

Use:

\n\n
//Takes any number of objects and returns one merged object\nvar objectMerge = function(){\n    var out = {};\n    if(!arguments.length)\n        return out;\n    for(var i=0; i<arguments.length; i++) {\n        for(var key in arguments[i]){\n            out[key] = arguments[i][key];\n        }\n    }\n    return out;\n}\n
\n\n

It was tested with:

\n\n
console.log(objectMerge({a:1, b:2}, {a:2, c:4}));\n
\n\n

It results in:

\n\n
{ a: 2, b: 2, c: 4 }\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090e", + "creator": "Mark", + "createdAt": 1311324653000, + "text": "

In Ext JS 4 it can be done as follows:

\n\n
var mergedObject = Ext.Object.merge(object1, object2)\n\n// Or shorter:\nvar mergedObject2 = Ext.merge(object1, object2)\n
\n\n

See merge( object ) : Object.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9090f", + "creator": "user909278", + "createdAt": 1314178767000, + "text": "

gossi's extension of David Coallier's method:

\n\n

Check these two lines:

\n\n
from = arguments[i];\nObject.getOwnPropertyNames(from).forEach(function (name) {\n
\n\n

One need to check \"from\" against null object... If for example merging an object that comes from an Ajax response, previously created on a server, an object property can have a value of \"null\", and in that case the above code generates an error saying:

\n\n
\n

\"from\" is not a valid object

\n
\n\n

So for example, wrapping the \"...Object.getOwnPropertyNames(from).forEach...\" function with an \"if (from != null) { ... }\" will prevent that error occurring.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90910", + "creator": "vsync", + "createdAt": 1320142814000, + "text": "

\r\n
\r\n
function extend(o, o1, o2){\r\n    if( !(o instanceof Object) ) o = {};\r\n\r\n    copy(o, o1);\r\n    if( o2 )\r\n        copy(o, o2)\r\n\r\n    function isObject(obj) {\r\n        var type = Object.prototype.toString.call(obj);\r\n        return obj === Object(obj) && type != '[object Array]' && type != '[object Function]';\r\n    };\r\n\r\n    function copy(a,b){\r\n        // copy o2 to o\r\n        for( var key in b )\r\n            if( b.hasOwnProperty(key) ){\r\n                if( isObject(b[key]) ){\r\n                    if( !isObject(a[key]) )\r\n                        a[key] = Object.assign({}, b[key]); \r\n                    else copy(a[key], b[key])\r\n                }\r\n                else\r\n                    a[key] = b[key];\r\n            }\r\n    }\r\n\r\n    return o;\r\n};\r\n\r\n\r\nvar o1 = {a:{foo:1}, b:1},\r\n    o2 = {a:{bar:2}, b:[1], c:()=>{}},\r\n    newMerged = extend({}, o1, o2);\r\n    \r\nconsole.log( newMerged )\r\nconsole.log( o1 )\r\nconsole.log( o2 )
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267c082fcc3049e920d6", + "creator": "vsync", + "createdAt": 1378210583000, + "text": "no it doesn't fail, I've just tried what you've said with success.", + "upvotes": 658, + "upvoterUsernames": [], + "downvotes": 658, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90912", + "creator": "Prabhakar Kasi", + "createdAt": 1328725775000, + "text": "

In YUI Y.merge should get the job done:

\n\n
Y.merge(obj1, obj2, obj3....) \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90911", + "creator": "Emre Erkan", + "createdAt": 1324739395000, + "text": "

I need to merge objects today, and this question (and answers) helped me a lot. I tried some of the answers, but none of them fit my needs, so I combined some of the answers, added something myself and came up with a new merge function. Here it is:

\n\n\n\n
var merge = function() {\n    var obj = {},\n        i = 0,\n        il = arguments.length,\n        key;\n    for (; i < il; i++) {\n        for (key in arguments[i]) {\n            if (arguments[i].hasOwnProperty(key)) {\n                obj[key] = arguments[i][key];\n            }\n        }\n    }\n    return obj;\n};\n
\n\n

Some example usages:

\n\n
var t1 = {\n    key1: 1,\n    key2: \"test\",\n    key3: [5, 2, 76, 21]\n};\nvar t2 = {\n    key1: {\n        ik1: \"hello\",\n        ik2: \"world\",\n        ik3: 3\n    }\n};\nvar t3 = {\n    key2: 3,\n    key3: {\n        t1: 1,\n        t2: 2,\n        t3: {\n            a1: 1,\n            a2: 3,\n            a4: [21, 3, 42, \"asd\"]\n        }\n    }\n};\n\nconsole.log(merge(t1, t2));\nconsole.log(merge(t1, t3));\nconsole.log(merge(t2, t3));\nconsole.log(merge(t1, t2, t3));\nconsole.log(merge({}, t1, { key1: 1 }));\n
\n", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90913", + "creator": "Industrial", + "createdAt": 1329651892000, + "text": "

Note that underscore.js's extend-method does this in a one-liner:

\n\n
_.extend({name : 'moe'}, {age : 50});\n=> {name : 'moe', age : 50}\n
\n", + "upvotes": 330, + "upvoterUsernames": [], + "downvotes": 152, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90915", + "creator": "Michael Benin", + "createdAt": 1338648739000, + "text": "
function extend()\n{ \n    var o = {}; \n\n    for (var i in arguments)\n    { \n        var s = arguments[i]; \n\n        for (var i in s)\n        { \n            o[i] = s[i]; \n        } \n    } \n\n    return o;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90914", + "creator": "Paweł Szczur", + "createdAt": 1334870584000, + "text": "

Just if anyone is using Google Closure Library:

\n\n
goog.require('goog.object');\nvar a = {'a': 1, 'b': 2};\nvar b = {'b': 3, 'c': 4};\ngoog.object.extend(a, b);\n// Now object a == {'a': 1, 'b': 3, 'c': 4};\n
\n\n

Similar helper function exists for array:

\n\n
var a = [1, 2];\nvar b = [3, 4];\ngoog.array.extend(a, b); // Extends array 'a'\ngoog.array.concat(a, b); // Returns concatenation of array 'a' and 'b'\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90916", + "creator": "Andreas Linden", + "createdAt": 1348059490000, + "text": "

Just by the way, what you're all doing is overwriting properties, not merging...

\n\n

This is how JavaScript objects area really merged: Only keys in the to object which are not objects themselves will be overwritten by from. Everything else will be really merged. Of course you can change this behaviour to not overwrite anything which exists like only if to[n] is undefined, etc...:

\n\n
var realMerge = function (to, from) {\n\n    for (n in from) {\n\n        if (typeof to[n] != 'object') {\n            to[n] = from[n];\n        } else if (typeof from[n] == 'object') {\n            to[n] = realMerge(to[n], from[n]);\n        }\n    }\n    return to;\n};\n
\n\n

Usage:

\n\n
var merged = realMerge(obj1, obj2);\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90917", + "creator": "Aram Kocharyan", + "createdAt": 1349080306000, + "text": "

This merges obj into a \"default\" def. obj has precedence for anything that exists in both, since obj is copied into def. Note also that this is recursive.

\n\n
function mergeObjs(def, obj) {\n    if (typeof obj == 'undefined') {\n        return def;\n    } else if (typeof def == 'undefined') {\n        return obj;\n    }\n    for (var i in obj) {\n        if (obj[i] != null && obj[i].constructor == Object) {\n            def[i] = mergeObjs(def[i], obj[i]);\n        } else {\n            def[i] = obj[i];\n        }\n    }\n    return def;\n}\n\na = {x : {y : [123]}}\nb = {x : {z : 123}}\nconsole.log(mergeObjs(a, b));\n// {x: {y : [123], z : 123}}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90918", + "creator": "pagid", + "createdAt": 1350639878000, + "text": "

It's worth mentioning that the version from the 140byt.es collection is solving the task within minimum space and is worth a try for this purpose:

\n\n

Code:

\n\n
function m(a,b,c){for(c in b)b.hasOwnProperty(c)&&((typeof a[c])[0]=='o'?m(a[c],b[c]):a[c]=b[c])}\n
\n\n

Usage for your purpose:

\n\n
m(obj1,obj2);\n
\n\n

Here's the original Gist.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90919", + "creator": "jleviaguirre", + "createdAt": 1355938772000, + "text": "
A={a:1,b:function(){alert(9)}}\nB={a:2,c:3}\nA.merge = function(){for(var i in B){A[i]=B[i]}}\nA.merge()\n
\n\n

Result is: {a:2,c:3,b:function()}

\n", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 175, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267d082fcc3049e920de", + "creator": "jleviaguirre", + "createdAt": 1355938877000, + "text": "btw, dojo has a function called mixin so, dojo.mixin(A,B) will do the trick", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9091a", + "creator": "Paul Spaulding", + "createdAt": 1366749920000, + "text": "

Here's my stab which

\n\n
    \n
  1. Supports deep merge
  2. \n
  3. Does not mutate arguments
  4. \n
  5. Takes any number of arguments
  6. \n
  7. Does not extend the object prototype
  8. \n
  9. Does not depend on another library (jQuery, MooTools, Underscore.js, etc.)
  10. \n
  11. Includes check for hasOwnProperty
  12. \n
  13. Is short :)

    \n\n
    /*\n    Recursively merge properties and return new object\n    obj1 &lt;- obj2 [ &lt;- ... ]\n*/\nfunction merge () {\n    var dst = {}\n        ,src\n        ,p\n        ,args = [].splice.call(arguments, 0)\n    ;\n\n    while (args.length > 0) {\n        src = args.splice(0, 1)[0];\n        if (toString.call(src) == '[object Object]') {\n            for (p in src) {\n                if (src.hasOwnProperty(p)) {\n                    if (toString.call(src[p]) == '[object Object]') {\n                        dst[p] = merge(dst[p] || {}, src[p]);\n                    } else {\n                        dst[p] = src[p];\n                    }\n                }\n            }\n        }\n    }\n\n   return dst;\n}\n
  14. \n
\n\n

Example:

\n\n
a = {\n    \"p1\": \"p1a\",\n    \"p2\": [\n        \"a\",\n        \"b\",\n        \"c\"\n    ],\n    \"p3\": true,\n    \"p5\": null,\n    \"p6\": {\n        \"p61\": \"p61a\",\n        \"p62\": \"p62a\",\n        \"p63\": [\n            \"aa\",\n            \"bb\",\n            \"cc\"\n        ],\n        \"p64\": {\n            \"p641\": \"p641a\"\n        }\n    }\n};\n\nb = {\n    \"p1\": \"p1b\",\n    \"p2\": [\n        \"d\",\n        \"e\",\n        \"f\"\n    ],\n    \"p3\": false,\n    \"p4\": true,\n    \"p6\": {\n        \"p61\": \"p61b\",\n        \"p64\": {\n            \"p642\": \"p642b\"\n        }\n    }\n};\n\nc = {\n    \"p1\": \"p1c\",\n    \"p3\": null,\n    \"p6\": {\n        \"p62\": \"p62c\",\n        \"p64\": {\n            \"p643\": \"p641c\"\n        }\n    }\n};\n\nd = merge(a, b, c);\n\n\n/*\n    d = {\n        \"p1\": \"p1c\",\n        \"p2\": [\n            \"d\",\n            \"e\",\n            \"f\"\n        ],\n        \"p3\": null,\n        \"p5\": null,\n        \"p6\": {\n            \"p61\": \"p61b\",\n            \"p62\": \"p62c\",\n            \"p63\": [\n                \"aa\",\n                \"bb\",\n                \"cc\"\n            ],\n            \"p64\": {\n                \"p641\": \"p641a\",\n                \"p642\": \"p642b\",\n                \"p643\": \"p641c\"\n            }\n        },\n        \"p4\": true\n    };\n*/\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9091c", + "creator": "korywka", + "createdAt": 1375372990000, + "text": "

My way:

\n\n
function mergeObjects(defaults, settings) {\n    Object.keys(defaults).forEach(function(key_default) {\n        if (typeof settings[key_default] == \"undefined\") {\n            settings[key_default] = defaults[key_default];\n        } else if (isObject(defaults[key_default]) && isObject(settings[key_default])) {\n            mergeObjects(defaults[key_default], settings[key_default]);\n        }\n    });\n\n    function isObject(object) {\n        return Object.prototype.toString.call(object) === '[object Object]';\n    }\n\n    return settings;\n}\n
\n\n

:)

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9091b", + "creator": "Paul", + "createdAt": 1375124223000, + "text": "

You could assign every object a default merge (perhaps 'inherit' a better name) method:

\n\n

It should work with either objects or instantiated functions.

\n\n

The below code handles overriding the merged values if so desired:

\n\n
Object.prototype.merge = function(obj, override) {\n// Don't override by default\n\n    for (var key in obj) {\n        var n = obj[key];\n        var t = this[key];\n        this[key] = (override && t) ? n : t;\n    };\n\n};\n
\n\n

Test data is below:

\n\n
var Mammal = function () {\n    this.eyes = 2;\n    this.thinking_brain = false;\n    this.say = function () {\n    console.log('screaming like a mammal')};\n}\n\nvar Human = function () {\n    this.thinking_brain = true;\n    this.say = function() {console.log('shouting like a human')};\n}\n\njohn = new Human();\n\n// Extend mammal, but do not override from mammal\njohn.merge(new Mammal());\njohn.say();\n\n// Extend mammal and override from mammal\njohn.merge(new Mammal(), true);\njohn.say();\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9091e", + "creator": "AndreasE", + "createdAt": 1390856457000, + "text": "

Similar to jQuery extend(), you have the same function in AngularJS:

\n\n
// Merge the 'options' object into the 'settings' object\nvar settings = {validate: false, limit: 5, name: \"foo\"};\nvar options  = {validate: true, name: \"bar\"};\nangular.extend(settings, options);\n
\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9091d", + "creator": "Egor Kloos", + "createdAt": 1386175340000, + "text": "

I've used Object.create() to keep the default settings (utilising __proto__ or Object.getPrototypeOf() ).

\n\n
function myPlugin( settings ){\n    var defaults = {\n        \"keyName\": [ \"string 1\", \"string 2\" ]\n    }\n    var options = Object.create( defaults );\n    for (var key in settings) { options[key] = settings[key]; }\n}\nmyPlugin( { \"keyName\": [\"string 3\", \"string 4\" ] } );\n
\n\n

This way I can always 'concat()' or 'push()' later.

\n\n
var newArray = options['keyName'].concat( options.__proto__['keyName'] );\n
\n\n

Note: You'll need to do a hasOwnProperty check before concatenation to avoid duplication.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9091f", + "creator": "devmao", + "createdAt": 1394880896000, + "text": "

With Underscore.js, to merge an array of objects do:

\n\n
var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];\n_(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });\n
\n\n

It results in:

\n\n
Object {a: 1, b: 2, c: 3, d: 4}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90920", + "creator": "Ryan Walls", + "createdAt": 1396992626000, + "text": "

For those using Node.js, there's an NPM module: node.extend

\n\n

Install:

\n\n
npm install node.extend\n
\n\n

Usage:

\n\n
var extend = require('node.extend');\nvar destObject = extend(true, {}, sourceObject);\n// Where sourceObject is the object whose properties will be copied into another.\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267d082fcc3049e920e6", + "creator": "ItalyPaleAle", + "createdAt": 1398803696000, + "text": "This is not a builtin library, it's a NPM module.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90922", + "creator": "NanoWizard", + "createdAt": 1412536476000, + "text": "

The Harmony ECMAScript 2015 (ES6) specifies Object.assign which will do this.

\n\n
Object.assign(obj1, obj2);\n
\n\n

Current browser support is getting better, but if you're developing for browsers that don't have support, you can use a polyfill.

\n", + "upvotes": 615, + "upvoterUsernames": [], + "downvotes": 238, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267e082fcc3049e920e9", + "creator": "Joachim Lous", + "createdAt": 1458122674000, + "text": "Note that this is only a shallow merge", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90921", + "creator": "antitoxic", + "createdAt": 1403470967000, + "text": "

There's a library called deepmerge on GitHub: That seems to be getting some traction. It's a standalone, available through both the npm and bower package managers.

\n\n

I would be inclined to use or improve on this instead of copy-pasting code from answers.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90924", + "creator": "T.Todua", + "createdAt": 1427821781000, + "text": "

Another method:

\n\n
function concat_collection(obj1, obj2) {\n    var i;\n    var arr = new Array();\n\n    var len1 = obj1.length;\n    for (i=0; i<len1; i++) {\n        arr.push(obj1[i]);\n    }\n\n    var len2 = obj2.length;\n    for (i=0; i<len2; i++) {\n        arr.push(obj2[i]);\n    }\n\n    return arr;\n}\n\nvar ELEMENTS = concat_collection(A,B);\nfor(var i = 0; i < ELEMENTS.length; i++) {\n    alert(ELEMENTS[i].value);\n}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90923", + "creator": "appsmatics", + "createdAt": 1422579588000, + "text": "

The following two are probably a good starting point. lodash also has a customizer function for those special needs!

\n\n

_.extend (http://underscorejs.org/#extend)
\n_.merge (https://lodash.com/docs#merge)

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90926", + "creator": "Dinusha", + "createdAt": 1441003876000, + "text": "

You can simply use jQuery extend

\n\n
var obj1 = { val1: false, limit: 5, name: \"foo\" };\nvar obj2 = { val2: true, name: \"bar\" };\n\njQuery.extend(obj1, obj2);\n
\n\n

Now obj1 contains all the values of obj1 and obj2

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90925", + "creator": "Etherealone", + "createdAt": 1429651107000, + "text": "

I use the following which is in pure JavaScript. It starts from the right-most argument and combines them all the way up to the first argument. There is no return value, only the first argument is modified and the left-most parameter (except the first one) has the highest weight on properties.

\n\n
var merge = function() {\n  var il = arguments.length;\n\n  for (var i = il - 1; i > 0; --i) {\n    for (var key in arguments[i]) {\n      if (arguments[i].hasOwnProperty(key)) {\n        arguments[0][key] = arguments[i][key];\n      }\n    }\n  }\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90927", + "creator": "Vikash Pandey", + "createdAt": 1443291429000, + "text": "

If you are using Dojo Toolkit then the best way to merge two object is using a mixin.

\n\n

Below is the sample for Dojo Toolkit mixin:

\n\n
// Dojo 1.7+ (AMD)\nrequire([\"dojo/_base/lang\"], function(lang){\n  var a = { b:\"c\", d:\"e\" };\n  lang.mixin(a, { d:\"f\", g:\"h\" });\n  console.log(a); // b:c, d:f, g:h\n});\n\n// Dojo < 1.7\nvar a = { b:\"c\", d:\"e\" };\ndojo.mixin(a, { d:\"f\", g:\"h\" });\nconsole.log(a); // b:c, d:f, g:h\n
\n\n

For more details, please mixin.

\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90929", + "creator": "Reza Roshan", + "createdAt": 1447691704000, + "text": "

Object.assign()

\n\n

ECMAScript 2015 (ES6)

\n\n

This is a new technology, part of the ECMAScript 2015 (ES6) standard.\nThis technology's specification has been finalized, but check the compatibility table for usage and implementation status in various browsers.

\n\n

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

\n\n
var o1 = { a: 1 };\nvar o2 = { b: 2 };\nvar o3 = { c: 3 };\n\nvar obj = Object.assign(o1, o2, o3);\nconsole.log(obj); // { a: 1, b: 2, c: 3 }\nconsole.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90928", + "creator": "Sherali Turdiyev", + "createdAt": 1444817431000, + "text": "

You can merge objects through following my method

\n\n

\r\n
\r\n
var obj1 = { food: 'pizza', car: 'ford' };\r\nvar obj2 = { animal: 'dog' };\r\n\r\nvar result = mergeObjects([obj1, obj2]);\r\n\r\nconsole.log(result);\r\ndocument.write(\"result: <pre>\" + JSON.stringify(result, 0, 3) + \"</pre>\");\r\n\r\nfunction mergeObjects(objectArray) {\r\n    if (objectArray.length) {\r\n        var b = \"\", i = -1;\r\n        while (objectArray[++i]) {\r\n            var str = JSON.stringify(objectArray[i]);\r\n            b += str.slice(1, str.length - 1);\r\n            if (objectArray[i + 1]) b += \",\";\r\n        }\r\n        return JSON.parse(\"{\" + b + \"}\");\r\n    }\r\n    return {};\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092a", + "creator": "Eugene Tiurin", + "createdAt": 1450198761000, + "text": "

Merge properties of N objects in one line of code

\n

An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need. (IE not supported)

\n
var clone = Object.assign({}, obj);\n
\n
\n

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

\n
\n

Read more...

\n

The polyfill to support older browsers:

\n
if (!Object.assign) {\n  Object.defineProperty(Object, 'assign', {\n    enumerable: false,\n    configurable: true,\n    writable: true,\n    value: function(target) {\n      'use strict';\n      if (target === undefined || target === null) {\n        throw new TypeError('Cannot convert first argument to object');\n      }\n\n      var to = Object(target);\n      for (var i = 1; i < arguments.length; i++) {\n        var nextSource = arguments[i];\n        if (nextSource === undefined || nextSource === null) {\n          continue;\n        }\n        nextSource = Object(nextSource);\n\n        var keysArray = Object.keys(nextSource);\n        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {\n          var nextKey = keysArray[nextIndex];\n          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);\n          if (desc !== undefined && desc.enumerable) {\n            to[nextKey] = nextSource[nextKey];\n          }\n        }\n      }\n      return to;\n    }\n  });\n}\n
\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092b", + "creator": "Raphaël", + "createdAt": 1450782169000, + "text": "

You should use lodash's defaultsDeep

\n\n
_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });\n// → { 'user': { 'name': 'barney', 'age': 36 } }\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092c", + "creator": "Yaki Klein", + "createdAt": 1451493627000, + "text": "

A possible way to achieve this is the following.

\n\n
if (!Object.prototype.merge){\n    Object.prototype.merge = function(obj){\n        var self = this;\n        Object.keys(obj).forEach(function(key){\n            self[key] = obj[key]\n        });\n    }\n};\n
\n\n

I don't know if it's better then the other answers. In this method you add the merge function to Objects prototype. This way you can call obj1.merge(obj2);

\n\n

Note : you should validate your argument to see if its an object and 'throw' a proper Error. If not Object.keys will 'throw' an 'Error'

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092d", + "creator": "Nishant Kumar", + "createdAt": 1452504026000, + "text": "

Here what I used in my codebase to merge.

\n\n
function merge(to, from) {\n  if (typeof to === 'object' && typeof from === 'object') {\n    for (var pro in from) {\n      if (from.hasOwnProperty(pro)) {\n        to[pro] = from[pro];\n      }\n    }\n  }\n  else{\n      throw \"Merge function can apply only on object\";\n  }\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092e", + "creator": "trincot", + "createdAt": 1483182257000, + "text": "

If you need a deep merge that will also \"merge\" arrays by concatenating them in the result, then this ES6 function might be what you need:

\n\n

\r\n
\r\n
function deepMerge(a, b) {\r\n    // If neither is an object, return one of them:\r\n    if (Object(a) !== a && Object(b) !== b) return b || a;\r\n    // Replace remaining primitive by empty object/array\r\n    if (Object(a) !== a) a = Array.isArray(b) ? [] : {};\r\n    if (Object(b) !== b) b = Array.isArray(a) ? [] : {};\r\n    // Treat arrays differently:\r\n    if (Array.isArray(a) && Array.isArray(b)) {\r\n        // Merging arrays is interpreted as concatenation of their deep clones:\r\n        return [...a.map(v => deepMerge(v)), ...b.map(v => deepMerge(v))];\r\n    } else {\r\n        // Get the keys that exist in either object\r\n        var keys = new Set([...Object.keys(a),...Object.keys(b)]);\r\n        // Recurse and assign to new object\r\n        return Object.assign({}, ...Array.from(keys,\r\n            key => ({ [key]: deepMerge(a[key], b[key]) }) ));\r\n    }\r\n}\r\n\r\n// Sample data for demo:\r\nvar a = {\r\n    groups: [{\r\n        group: [{\r\n            name: 'John',\r\n            age: 12\r\n        },{\r\n            name: 'Mary',\r\n            age: 20\r\n        }],\r\n        groupName: 'Pair'\r\n    }],\r\n    config: {\r\n        color: 'blue',\r\n        range: 'far'\r\n    }\r\n};\r\n\r\n\r\nvar b = {\r\n    groups: [{\r\n        group: [{\r\n            name: 'Bill',\r\n            age: 15\r\n        }],\r\n        groupName: 'Loner'\r\n    }],\r\n    config: {\r\n        range: 'close',\r\n        strength: 'average'\r\n    }\r\n};\r\n\r\nvar merged = deepMerge(a, b);\r\n\r\nconsole.log(merged);
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
\r\n
\r\n

\n\n

Note that if only one argument is passed to this function, it acts as a deep clone function.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9092f", + "creator": "Jaime Asm", + "createdAt": 1485510911000, + "text": "

You can use the object spread syntax to achieve this. It's a part of ES2018 and beyond.

\n

\r\n
\r\n
const obj1 = { food: 'pizza', car: 'ford' };\nconst obj2 = { animal: 'dog' };\n\nconst obj3 = { ...obj1, ...obj2 };\nconsole.log(obj3);
\r\n
\r\n
\r\n

\n", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3267f082fcc3049e920f6", + "creator": "Slava", + "createdAt": 1507716086000, + "text": "Browser support?", + "upvotes": 350, + "upvoterUsernames": [], + "downvotes": 350, + "downvoterUsernames": [] + }, + { + "_id": "62f3267f082fcc3049e920f7", + "creator": "connexo", + "createdAt": 1514889148000, + "text": "@Alph.Dev You do know caniuse.com?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3267f082fcc3049e920f9", + "creator": "klewis", + "createdAt": 1521229969000, + "text": "I can't get the 3 dot syntax to work in node.js. node complains about it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3267f082fcc3049e920fb", + "creator": "Merrin K", + "createdAt": 1629524099000, + "text": "What does ... (3 dot) represents?", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90930", + "creator": "mitch3ls", + "createdAt": 1486037700000, + "text": "

You can do the following in EcmaScript2016

\n\n

Correction: it's a stage 3 proposal, still it has always worked for me

\n\n
const objA = {\n  attrA: 'hello',\n  attrB: true\n}\n\nconst objB = {\n  attrC: 2\n}\n\nconst mergedObj = {...objA, ...objB}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90931", + "creator": "toster-cx", + "createdAt": 1493058574000, + "text": "

ES5 compatible native one-liner:

\n\n
var merged = [obj1, obj2].reduce(function(a, o) { for(k in o) a[k] = o[k]; return a; }, {})\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90932", + "creator": "Legends", + "createdAt": 1521582104000, + "text": "

**Merging objects is simple using Object.assign or the spread ... operator **

\n\n

\r\n
\r\n
var obj1 = { food: 'pizza', car: 'ford' }\r\nvar obj2 = { animal: 'dog', car: 'BMW' }\r\nvar obj3 = {a: \"A\"}\r\n\r\n\r\nvar mergedObj = Object.assign(obj1,obj2,obj3)\r\n // or using the Spread operator (...)\r\nvar mergedObj = {...obj1,...obj2,...obj3}\r\n\r\nconsole.log(mergedObj);
\r\n
\r\n
\r\n

\n\n

The objects are merged from right to left, this means that objects which have identical properties as the objects to their right will be overriden.

\n\n

In this example obj2.car overrides obj1.car

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90934", + "creator": "aircraft", + "createdAt": 1527772292000, + "text": "

We can crate a empty object, and combine them by for-loop:

\n\n

\r\n
\r\n
var obj1 = {\r\n  id: '1',\r\n  name: 'name'\r\n}\r\n\r\nvar obj2 = {\r\n  c: 'c',\r\n  d: 'd'\r\n}\r\n\r\nvar obj3 = {}\r\n\r\nfor (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }\r\nfor (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }\r\n\r\n\r\nconsole.log( obj1, obj2, obj3)
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90933", + "creator": "Logan", + "createdAt": 1525200254000, + "text": "

Wow.. this is the first StackOverflow post I've seen with multiple pages. Apologies for adding another "answer"

\n
\n

ES5 & Earlier

\n

This method is for ES5 & Earlier - there are plenty of other answers addressing ES6.

\n

I did not see any "deep" object merging utilizing the arguments property. Here is my answer - compact & recursive, allowing unlimited object arguments to be passed:

\n
function extend() {\n    for (var o = {}, i = 0; i < arguments.length; i++) {\n        // Uncomment to skip arguments that are not objects (to prevent errors)\n        // if (arguments[i].constructor !== Object) continue;\n        for (var k in arguments[i]) {\n            if (arguments[i].hasOwnProperty(k)) {\n                o[k] = arguments[i][k].constructor === Object\n                    ? extend(o[k] || {}, arguments[i][k])\n                    : arguments[i][k];\n            }\n        }\n    }\n    return o;\n}\n
\n
\n

Example

\n

\r\n
\r\n
/**\n * Extend objects\n */\nfunction extend() {\n    for (var o = {}, i = 0; i < arguments.length; i++) {\n        for (var k in arguments[i]) {\n            if (arguments[i].hasOwnProperty(k)) {\n                o[k] = arguments[i][k].constructor === Object\n                    ? extend(o[k] || {}, arguments[i][k])\n                    : arguments[i][k];\n            }\n        }\n    }\n    return o;\n}\n\n/**\n * Example\n */\ndocument.write(JSON.stringify(extend({\n    api: 1,\n    params: {\n        query: 'hello'\n    }\n}, {\n    params: {\n        query: 'there'\n    }\n})));\n// outputs {\"api\": 1, \"params\": {\"query\": \"there\"}}
\r\n
\r\n
\r\n

\n
\n

This answer is now but a drop in the ocean ...

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90935", + "creator": "Tính Ngô Quang", + "createdAt": 1537235743000, + "text": "
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog' }\n\n// result\nresult: {food: \"pizza\", car: \"ford\", animal: \"dog\"}\n
\n\n

Using jQuery.extend() - Link

\n\n
// Merge obj1 & obj2 to result\nvar result1 = $.extend( {}, obj1, obj2 );\n
\n\n

Using _.merge() - Link

\n\n
// Merge obj1 & obj2 to result\nvar result2 = _.merge( {}, obj1, obj2 );\n
\n\n

Using _.extend() - Link

\n\n
// Merge obj1 & obj2 to result\nvar result3 = _.extend( {}, obj1, obj2 );\n
\n\n

Using Object.assign() ECMAScript 2015 (ES6) - Link

\n\n
// Merge obj1 & obj2 to result\nvar result4 = Object.assign( {}, obj1, obj2 );\n
\n\n

Output of all

\n\n
obj1: { animal: 'dog' }\nobj2: { food: 'pizza', car: 'ford' }\nresult1: {food: \"pizza\", car: \"ford\", animal: \"dog\"}\nresult2: {food: \"pizza\", car: \"ford\", animal: \"dog\"}\nresult3: {food: \"pizza\", car: \"ford\", animal: \"dog\"}\nresult4: {food: \"pizza\", car: \"ford\", animal: \"dog\"}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32680082fcc3049e92103", + "creator": "Shilpe Saxena", + "createdAt": 1658158300000, + "text": "The result4 can also be written without the {} var result4 = Object.assign(obj1,obj2);", + "upvotes": 464, + "upvoterUsernames": [], + "downvotes": 464, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90936", + "creator": "gildniy", + "createdAt": 1541596566000, + "text": "

With the following helper, you can merge two objects into one new object:

\n\n
function extend(obj, src) {\n    for (var key in src) {\n        if (src.hasOwnProperty(key)) obj[key] = src[key];\n    }\n    return obj;\n}\n\n// example\nvar a = { foo: true }, b = { bar: false };\nvar c = extend(a, b);\n\nconsole.log(c);\n// { foo: true, bar: false }\n
\n\n

This is typically useful when merging an options dict with the default settings in a function or a plugin.

\n\n

If support for IE 8 is not required, you may use Object.keys for the same functionality instead:

\n\n
function extend(obj, src) {\n    Object.keys(src).forEach(function(key) { obj[key] = src[key]; });\n    return obj;\n}\n
\n\n

This involves slightly less code and is a bit faster.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90937", + "creator": "Tejas Savaliya", + "createdAt": 1547724602000, + "text": "

\r\n
\r\n
let obj1 = {a:1, b:2};\r\nlet obj2 = {c:3, d:4};\r\nlet merged = {...obj1, ...obj2};\r\nconsole.log(merged);
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90938", + "creator": "Paweł Otto", + "createdAt": 1552546509000, + "text": "

ES2018/TypeScript: Many answers are OK but I've come up with a more elegant solution to this problem when you need to merge two objects without overwriting overlapping object keys.

\n\n

My function also accepts unlimited number of objects to merge as function arguments:

\n\n

(I'm using TypeScript notation here, feel free to delete the :object[] type in the function argument if you're using plain JavaScript).

\n\n
const merge = (...objects: object[]) => {\n  return objects.reduce((prev, next) => {\n    Object.keys(prev).forEach(key => {\n      next[key] = { ...next[key], ...prev[key] }\n    })\n    return next\n  })\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90939", + "creator": "akhtarvahid", + "createdAt": 1571030034000, + "text": "

merge two object using Object.assign and spread operator.

\n\n

Wrong way(Modify original object because targeting o1)

\n\n
var o1 = { X: 10 };\nvar o2 = { Y: 20 };\nvar o3 = { Z: 30 };\nvar merge = Object.assign(o1, o2, o3);\nconsole.log(merge)  // {X:10, Y:20, Z:30}\nconsole.log(o1)     // {X:10, Y:20, Z:30}\n
\n\n

Right ways

\n\n\n\n

\r\n
\r\n
var o1 = { X: 10 };\r\nvar o2 = { Y: 20 };\r\nvar o3 = { Z: 30 };\r\n\r\nconsole.log('Does not modify original objects because target {}');\r\nvar merge = Object.assign({}, o1, o2, o3);\r\nconsole.log(merge); // { X: 10, Y: 20, Z: 30 }\r\nconsole.log(o1)\r\n\r\nconsole.log('Does not modify original objects')\r\nvar spreadMerge = {...o1, ...o2, ...o3};\r\nconsole.log(spreadMerge);\r\nconsole.log(o1);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9093a", + "creator": "space97", + "createdAt": 1573683870000, + "text": "

It seems like this should be all you need:

\n
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog' }\n\nvar obj3 = { ...obj1, ...obj2 }\n
\n

After that obj3 should now have the following value:

\n
{food: "pizza", car: "ford", animal: "dog"}\n
\n

Try it out here:

\n

\r\n
\r\n
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog' }\n\nvar obj3 = { ...obj1, ...obj2 }\n\nconsole.log(obj3);
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9093b", + "creator": "Deen John", + "createdAt": 1577088213000, + "text": "

shallow

\n\n
var obj = { name : \"Jacob\" , address : [\"America\"] }\nvar obj2 = { name : \"Shaun\" , address : [\"Honk Kong\"] }\n\nvar merged = Object.assign({} , obj,obj2 ); //shallow merge \nobj2.address[0] = \"new city\"\n
\n\n

result.address[0] is changed to \"new city\" , i.e merged object is also changed. This is the problem with shallow merge.

\n\n

deep

\n\n
var obj = { name : \"Jacob\" , address : [\"America\"] }\nvar obj2 = { name : \"Shaun\" , address : [\"Honk Kong\"] }\n\nvar result = Object.assign({} , JSON.parse(JSON.stringify(obj)),JSON.parse(JSON.stringify(obj2)) )\n\nobj2.address[0] = \"new city\"\n
\n\n

result.address[0] is not changed

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32680082fcc3049e92109", + "creator": "GroovyDotCom", + "createdAt": 1605558740000, + "text": "var merged = Object.assign({} , obj,obj2 ) does not merge anything?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9093c", + "creator": "Kavale arun", + "createdAt": 1577341966000, + "text": "

You can use Object.assign method. For example:

\n
var result = Object.assign(obj1, obj2);\n
\n

Also, note that it creates a shallow copy of the object.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9093e", + "creator": "Piyush Rana", + "createdAt": 1601977370000, + "text": "

There are different ways to achieve this:

\n
Object.assign(targetObj, sourceObj);\n\ntargetObj = {...targetObj, ...sourceObj};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9093d", + "creator": "Soura Ghosh", + "createdAt": 1578747541000, + "text": "
\n

Use Spread operator which follows the ES6 version

\n
\n\n
var obj1 = { food: 'pizza', car: 'ford' }\nvar obj2 = { animal: 'dog' }\nlet result = {...obj1,...obj2};\nconsole.log(result)\n\noutput { food: 'pizza', car: 'ford', animal: 'dog' }\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9093f", + "creator": "kokhta shukvani", + "createdAt": 1603170832000, + "text": "
Object.assign(TargetObject, Obj1, Obj2, ...);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90940", + "creator": "Mehadi Hassan", + "createdAt": 1613042411000, + "text": "

Three ways you can do that:-

\n

Approach 1:-

\n
// using spread ...\n    let obj1 = {\n        ...obj2\n    };\n
\n

Approach2:-

\n
// using  Object.assign() method\nlet obj1 = Object.assign({}, obj2);\n
\n

Approach3:-

\n
// using JSON\nlet obj1 = JSON.parse(JSON.stringify(obj2));\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90941", + "creator": "Force Bolt", + "createdAt": 1621953093000, + "text": "

Try this way using jQuery library

\n
let obj1 = { food: 'pizza', car: 'ford' }\nlet obj2 = { animal: 'dog' }\n\nconsole.log(jQuery.extend(obj1, obj2))\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90942", + "creator": "dazzafact", + "createdAt": 1650776499000, + "text": "

Use this

\n

\r\n
\r\n
var t=merge({test:123},{mysecondTest:{blub:{test2:'string'},args:{'test':2}}})\nconsole.log(t);\n\nfunction merge(...args) {\n  return Object.assign({}, ...args);\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90943", + "creator": "dunubh", + "createdAt": 1656728761000, + "text": "
var firstObject = {\n    key1 : 'value1',\n    key2 : 'value2'\n};\n\nvar secondObject={\n  ...firstObject,\n  key3 : 'value3',\n  key4 : 'value4',\n  key5 : 'value5'\n}\nconsole.log(firstObject);\nconsole.log(secondObject);\n
\n", + "upvotes": 1200, + "upvoterUsernames": [], + "downvotes": 1200, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1745914, + "uvac": 1745979 + } + }, + { + "_id": "62f321bb082fcc3049e8fead", + "title": "How do I check if an element is hidden in jQuery?", + "title-lowercase": "how do i check if an element is hidden in jquery?", + "creator": "Philip Morton", + "createdAt": 1223384598000, + "status": "open", + "text": "

How do I toggle the visibility of an element using .hide(), .show(), or .toggle()?

\n

How do I test if an element is visible or hidden?

\n", + "upvotes": 11679, + "upvoterUsernames": [], + "downvotes": 3258, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3030285, + "answers": 64, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e8ff0f", + "creator": "Mote", + "createdAt": 1223384944000, + "text": "
if ( $(element).css('display') == 'none' || $(element).css(\"visibility\") == \"hidden\"){\n    // 'element' is hidden\n}\n
\n\n

The above method does not consider the visibility of the parent. To consider the parent as well, you should use .is(\":hidden\") or .is(\":visible\").

\n\n

For example,

\n\n
<div id=\"div1\" style=\"display:none\">\n  <div id=\"div2\" style=\"display:block\">Div2</div>\n</div>\n
\n\n
\n

The above method will consider div2 visible while :visible not. But the above might be useful in many cases, especially when you need to find if there is any error divs visible in the hidden parent because in such conditions :visible will not work.

\n
\n", + "upvotes": 1406, + "upvoterUsernames": [], + "downvotes": 349, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f59", + "creator": "chiborg", + "createdAt": 1267611048000, + "text": "This only checks for the display property of a single element. The :visible attribute checks also the visibility of the parent elements.", + "upvotes": 170, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f5b", + "creator": "evanmcd", + "createdAt": 1326480701000, + "text": "This is the only solution that worked for me when testing with IE 8.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f5d", + "creator": "Casey", + "createdAt": 1394819774000, + "text": "@chiborg Yes, but sometimes that's what you want and I had to learn the hard way how "clever" jQuery was...", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f5f", + "creator": "atheaos", + "createdAt": 1493136339000, + "text": "This answer can be used when an element exists but is not currently on the page, such as after detach().", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff10", + "creator": "twernt", + "createdAt": 1223385375000, + "text": "

You can use the hidden selector:

\n\n
// Matches all elements that are hidden\n$('element:hidden')\n
\n\n

And the visible selector:

\n\n
// Matches all elements that are visible\n$('element:visible')\n
\n", + "upvotes": 2037, + "upvoterUsernames": [], + "downvotes": 455, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f62", + "creator": "Etienne Dupuis", + "createdAt": 1341432724000, + "text": "On pages 21 to 28 it shows how slow :hidden or :visible is compared to other selectors. Thanks for pointing this.", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f64", + "creator": "ZoomIn", + "createdAt": 1376032713000, + "text": "I am toggling the element mamually using this selector. $('element:hidden') is always true for me!", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f66", + "creator": "cwingrav", + "createdAt": 1447862268000, + "text": ":hidden is for form elements, not display:none. This is not the answer people are most likely expecting.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff12", + "creator": "Simon_Weaver", + "createdAt": 1248517300000, + "text": "

Often when checking if something is visible or not, you are going to go right ahead immediately and do something else with it. jQuery chaining makes this easy.

\n\n

So if you have a selector and you want to perform some action on it only if is visible or hidden, you can use filter(\":visible\") or filter(\":hidden\") followed by chaining it with the action you want to take.

\n\n

So instead of an if statement, like this:

\n\n
if ($('#btnUpdate').is(\":visible\"))\n{\n     $('#btnUpdate').animate({ width: \"toggle\" });   // Hide button\n}\n
\n\n

Or more efficient, but even uglier:

\n\n
var button = $('#btnUpdate');\nif (button.is(\":visible\"))\n{\n     button.animate({ width: \"toggle\" });   // Hide button\n}\n
\n\n

You can do it all in one line:

\n\n
$('#btnUpdate').filter(\":visible\").animate({ width: \"toggle\" });\n
\n", + "upvotes": 442, + "upvoterUsernames": [], + "downvotes": 124, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff11", + "creator": "Tsvetomir Tsonev", + "createdAt": 1223386222000, + "text": "

Since the question refers to a single element, this code might be more suitable:

\n
// Checks CSS content for display:[none|block], ignores visibility:[true|false]\n$(element).is(":visible");\n\n// The same works with hidden\n$(element).is(":hidden");\n
\n

It is the same as twernt's suggestion, but applied to a single element; and it matches the algorithm recommended in the jQuery FAQ.

\n

We use jQuery's is() to check the selected element with another element, selector or any jQuery object. This method traverses along the DOM elements to find a match, which satisfies the passed parameter. It will return true if there is a match, otherwise return false.

\n", + "upvotes": 13706, + "upvoterUsernames": [], + "downvotes": 3659, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f6a", + "creator": "Tsvetomir Tsonev", + "createdAt": 1294317027000, + "text": "That is correct, but :visible will also check if the parent elements are visible, as chiborg pointed out.", + "upvotes": 189, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f6b", + "creator": "Kzqai", + "createdAt": 1325777775000, + "text": "I actually found that the reverse logic words better: !$('selector').is(':hidden'); for some reason. Worth a try.", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f6d", + "creator": "Robert Johnstone", + "createdAt": 1377863169000, + "text": "How do you do the inverse of this?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f6f", + "creator": "Wolfpack'08", + "createdAt": 1386122292000, + "text": "So, should we use .is(":invisible") for false? Or !$(element).is(":visible"), or just what?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f71", + "creator": "Izkata", + "createdAt": 1391467432000, + "text": "@Kzqai Also, I believe this works: $(selector).is('not(:hidden)')", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f73", + "creator": "carl", + "createdAt": 1392101185000, + "text": "This doesn't work in the latest jquery. It's been deprecated. What's the best work around? (Why did they remove this...?)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f75", + "creator": "Yana Agun Siswanto", + "createdAt": 1423037037000, + "text": "Not working for element that have parent overflow: hidden;", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3220a082fcc3049e90f77", + "creator": "Black", + "createdAt": 1558362056000, + "text": "Does not work with jQuery UI Selectmenu, It says my option is hidden even though it is visible.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff13", + "creator": "user574889", + "createdAt": 1294953198000, + "text": "

From How do I determine the state of a toggled element?

\n\n
\n\n

You can determine whether an element is collapsed or not by using the :visible and :hidden selectors.

\n\n
var isVisible = $('#myDiv').is(':visible');\nvar isHidden = $('#myDiv').is(':hidden');\n
\n\n

If you're simply acting on an element based on its visibility, you can just include :visible or :hidden in the selector expression. For example:

\n\n
 $('#myDiv:visible').animate({left: '+=200px'}, 'slow');\n
\n", + "upvotes": 535, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff14", + "creator": "aaronLile", + "createdAt": 1300992250000, + "text": "

None of these answers address what I understand to be the question, which is what I was searching for, \"How do I handle items that have visibility: hidden?\". Neither :visible nor :hidden will handle this, as they are both looking for display per the documentation. As far as I could determine, there is no selector to handle CSS visibility. Here is how I resolved it (standard jQuery selectors, there may be a more condensed syntax):

\n\n
$(\".item\").each(function() {\n    if ($(this).css(\"visibility\") == \"hidden\") {\n        // handle non visible state\n    } else {\n        // handle visible state\n    }\n});\n
\n", + "upvotes": 1115, + "upvoterUsernames": [], + "downvotes": 543, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f7a", + "creator": "vsync", + "createdAt": 1398194411000, + "text": "you need to traverse up the DOM to check the node's parents, or else ,this is useless.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff16", + "creator": "Pedro Rainho", + "createdAt": 1322212570000, + "text": "

The :visible selector according to the jQuery documentation:

\n\n
\n \n \n

Elements with visibility: hidden or opacity: 0 are considered to be visible, since they still consume space in the layout.

\n
\n\n

This is useful in some cases and useless in others, because if you want to check if the element is visible (display != none), ignoring the parents visibility, you will find that doing .css(\"display\") == 'none' is not only faster, but will also return the visibility check correctly.

\n\n

If you want to check visibility instead of display, you should use: .css(\"visibility\") == \"hidden\".

\n\n

Also take into consideration the additional jQuery notes:

\n\n
\n

Because :visible is a jQuery extension and not part of the CSS specification, queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(\":visible\").

\n
\n\n

Also, if you are concerned about performance, you should check Now you see me… show/hide performance (2010-05-04). And use other methods to show and hide elements.

\n", + "upvotes": 398, + "upvoterUsernames": [], + "downvotes": 136, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff15", + "creator": "Abiy", + "createdAt": 1309983540000, + "text": "

This works for me, and I am using show() and hide() to make my div hidden/visible:

\n\n
if( $(this).css('display') == 'none' ){\n    /* your code goes here */\n} else {\n    /* alternate logic   */\n}\n
\n", + "upvotes": 323, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff18", + "creator": "Lucas", + "createdAt": 1335051630000, + "text": "

Another answer you should put into consideration is if you are hiding an element, you should use jQuery, but instead of actually hiding it, you remove the whole element, but you copy its HTML content and the tag itself into a jQuery variable, and then all you need to do is test if there is such a tag on the screen, using the normal if (!$('#thetagname').length).

\n", + "upvotes": 233, + "upvoterUsernames": [], + "downvotes": 105, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff17", + "creator": "David Levin", + "createdAt": 1328285071000, + "text": "

I would use CSS class .hide { display: none!important; }.

\n\n

For hiding/showing, I call .addClass(\"hide\")/.removeClass(\"hide\"). For checking visibility, I use .hasClass(\"hide\").

\n\n

It's a simple and clear way to check/hide/show elements, if you don't plan to use .toggle() or .animate() methods.

\n", + "upvotes": 331, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff19", + "creator": "webvitaly", + "createdAt": 1335301451000, + "text": "

How element visibility and jQuery works;

\n\n

An element could be hidden with display:none, visibility:hidden or opacity:0. The difference between those methods:

\n\n\n", + "upvotes": 379, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220c082fcc3049e90fb0", + "creator": "YangombiUmpakati", + "createdAt": 1513076916000, + "text": "also if you hide input with opacity:0, it still gets selected with tab key", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff1a", + "creator": "ScoRpion", + "createdAt": 1337777948000, + "text": "

One can simply use the hidden or visible attribute, like:

\n\n
$('element:hidden')\n$('element:visible')\n
\n\n

Or you can simplify the same with is as follows.

\n\n
$(element).is(\":visible\")\n
\n", + "upvotes": 205, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff1c", + "creator": "Matt Brock", + "createdAt": 1342466299000, + "text": "

You can also do this using plain JavaScript:

\n\n
function isRendered(domObj) {\n    if ((domObj.nodeType != 1) || (domObj == document.body)) {\n        return true;\n    }\n    if (domObj.currentStyle && domObj.currentStyle[\"display\"] != \"none\" && domObj.currentStyle[\"visibility\"] != \"hidden\") {\n        return isRendered(domObj.parentNode);\n    } else if (window.getComputedStyle) {\n        var cs = document.defaultView.getComputedStyle(domObj, null);\n        if (cs.getPropertyValue(\"display\") != \"none\" && cs.getPropertyValue(\"visibility\") != \"hidden\") {\n            return isRendered(domObj.parentNode);\n        }\n    }\n    return false;\n}\n
\n\n

Notes:

\n\n
    \n
  1. Works everywhere

  2. \n
  3. Works for nested elements

  4. \n
  5. Works for CSS and inline styles

  6. \n
  7. Doesn't require a framework

  8. \n
\n", + "upvotes": 291, + "upvoterUsernames": [], + "downvotes": 115, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220c082fcc3049e90fb4", + "creator": "alex", + "createdAt": 1348116323000, + "text": "Works slightly differently to jQuery's; it considers visibility: hidden to be visible.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff1b", + "creator": "Vaishu", + "createdAt": 1339593645000, + "text": "

ebdiv should be set to style=\"display:none;\". It works for both show and hide:

\n\n
$(document).ready(function(){\n    $(\"#eb\").click(function(){\n        $(\"#ebdiv\").toggle();\n    });    \n});\n
\n", + "upvotes": 249, + "upvoterUsernames": [], + "downvotes": 108, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff1d", + "creator": "Maneesh Kumar", + "createdAt": 1342788264000, + "text": "
expect($("#message_div").css("display")).toBe("none");\n
\n", + "upvotes": 190, + "upvoterUsernames": [], + "downvotes": 89, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff1e", + "creator": "Code Spy", + "createdAt": 1359092051000, + "text": "

Demo Link

\n

\r\n
\r\n
$('#clickme').click(function() {\n  $('#book').toggle('slow', function() {\n    // Animation complete.\n    alert($('#book').is(\":visible\")); //<--- TRUE if Visible False if Hidden\n  });\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<div id=\"clickme\">\n  Click here\n</div>\n<img id=\"book\" src=\"https://upload.wikimedia.org/wikipedia/commons/8/87/Google_Chrome_icon_%282011%29.png\" alt=\"\" width=\"300\"/>
\r\n
\r\n
\r\n

\n

Source (from my blog):

\n

Blogger Plug n Play - jQuery Tools and Widgets: How to See if Element is hidden or Visible Using jQuery

\n", + "upvotes": 182, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff1f", + "creator": "Matthias Wegtun", + "createdAt": 1370353373000, + "text": "

To check if it is not visible I use !:

\n\n
if ( !$('#book').is(':visible')) {\n    alert('#book is not visible')\n}\n
\n\n

Or the following is also the sam, saving the jQuery selector in a variable to have better performance when you need it multiple times:

\n\n
var $book = $('#book')\n\nif(!$book.is(':visible')) {\n    alert('#book is not visible')\n}\n
\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220c082fcc3049e91049", + "creator": "Ilia", + "createdAt": 1371763950000, + "text": "How did you determined that saving a selector in variable is really faster?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3220c082fcc3049e9104b", + "creator": "Kenma", + "createdAt": 1377449829000, + "text": "This is suitable if you want to use a single variable through out the process instead of calling and calling the same object.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff21", + "creator": "Irfan DANISH", + "createdAt": 1382942594000, + "text": "

\r\n
\r\n
$(document).ready(function() {\n  if ($(\"#checkme:hidden\").length) {\n    console.log('Hidden');\n  }\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<div id=\"checkme\" class=\"product\" style=\"display:none\">\n  <span class=\"itemlist\"><!-- Shows Results for Fish --></span> Category:Fish\n  <br>Product: Salmon Atlantic\n  <br>Specie: Salmo salar\n  <br>Form: Steaks\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 147, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff22", + "creator": "cssimsek", + "createdAt": 1383694322000, + "text": "

Also here's a ternary conditional expression to check the state of the element and then to toggle it:

\n\n
$('someElement').on('click', function(){ $('elementToToggle').is(':visible') ? $('elementToToggle').hide('slow') : $('elementToToggle').show('slow'); });\n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220c082fcc3049e9104f", + "creator": "nbrooks", + "createdAt": 1387961601000, + "text": "Or, y'kno, just get rid of the entire conditional and say $('elementToToggle').toggle('slow');... :)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff25", + "creator": "Andron", + "createdAt": 1395232937000, + "text": "

Because Elements with visibility: hidden or opacity: 0 are considered visible, since they still consume space in the layout (as described for jQuery :visible Selector) - we can check if element is really visible in this way:

\n\n
function isElementReallyHidden (el) {\n    return $(el).is(\":hidden\") || $(el).css(\"visibility\") == \"hidden\" || $(el).css('opacity') == 0;\n}\n\nvar booElementReallyShowed = !isElementReallyHidden(someEl);\n$(someEl).parents().each(function () {\n    if (isElementReallyHidden(this)) {\n        booElementReallyShowed = false;\n    }\n});\n
\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff23", + "creator": "Gaurav", + "createdAt": 1384512074000, + "text": "
if($('#postcode_div').is(':visible')) {\n    if($('#postcode_text').val()=='') {\n        $('#spanPost').text('\\u00a0');\n    } else {\n        $('#spanPost').text($('#postcode_text').val());\n}\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff24", + "creator": "Premshankar Tiwari", + "createdAt": 1391149444000, + "text": "

You need to check both... Display as well as visibility:

\n\n
if ($(this).css(\"display\") == \"none\" || $(this).css(\"visibility\") == \"hidden\") {\n    // The element is not visible\n} else {\n    // The element is visible\n}\n
\n\n

If we check for $(this).is(\":visible\"), jQuery checks for both the things automatically.

\n", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff26", + "creator": "Kareem", + "createdAt": 1397288320000, + "text": "
.is(\":not(':hidden')\") /*if shown*/\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff27", + "creator": "conceptdeluxe", + "createdAt": 1399373431000, + "text": "

When testing an element against :hidden selector in jQuery it should be considered that an absolute positioned element may be recognized as hidden although their child elements are visible.

\n\n

This seems somewhat counter-intuitive in the first place – though having a closer look at the jQuery documentation gives the relevant information:

\n\n
\n

Elements can be considered hidden for several reasons: [...] Their width and height are explicitly set to 0. [...]

\n
\n\n

So this actually makes sense in regards to the box-model and the computed style for the element. Even if width and height are not set explicitly to 0 they may be set implicitly.

\n\n

Have a look at the following example:

\n\n

\r\n
\r\n
console.log($('.foo').is(':hidden')); // true\r\nconsole.log($('.bar').is(':hidden')); // false
\r\n
.foo {\r\n  position: absolute;\r\n  left: 10px;\r\n  top: 10px;\r\n  background: #ff0000;\r\n}\r\n\r\n.bar {\r\n  position: absolute;\r\n  left: 10px;\r\n  top: 10px;\r\n  width: 20px;\r\n  height: 20px;\r\n  background: #0000ff;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div class=\"foo\">\r\n  <div class=\"bar\"></div>\r\n</div>
\r\n
\r\n
\r\n

\n\n
\n\n

Update for jQuery 3.x:

\n\n

With jQuery 3 the described behavior will change! Elements will be considered visible if they have any layout boxes, including those of zero width and/or height.

\n\n

JSFiddle with jQuery 3.0.0-alpha1:

\n\n

http://jsfiddle.net/pM2q3/7/

\n\n

The same JavaScript code will then have this output:

\n\n
console.log($('.foo').is(':hidden')); // false\nconsole.log($('.bar').is(':hidden')); // false\n
\n", + "upvotes": 208, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff28", + "creator": "pixellabme", + "createdAt": 1407734917000, + "text": "

Simply check visibility by checking for a boolean value, like:

\n\n
if (this.hidden === false) {\n    // Your code\n}\n
\n\n

I used this code for each function. Otherwise you can use is(':visible') for checking the visibility of an element.

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff20", + "creator": "Ross Brasseaux", + "createdAt": 1374182272000, + "text": "

Use class toggling, not style editing . . .

\n

Using classes designated for "hiding" elements is easy and also one of the most efficient methods. Toggling a class 'hidden' with a Display style of 'none' will perform faster than editing that style directly. I explained some of this pretty thoroughly in Stack Overflow question Turning two elements visible/hidden in the same div.

\n
\n

JavaScript Best Practices and Optimization

\n

Here is a truly enlightening video of a Google Tech Talk by Google front-end engineer Nicholas Zakas:

\n\n", + "upvotes": 121, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff29", + "creator": "RN Kushwaha", + "createdAt": 1408827204000, + "text": "

But what if the element's CSS is like the following?

\n\n
.element{\n    position: absolute;left:-9999;    \n}\n
\n\n

So this answer to Stack Overflow question How to check if an element is off-screen should also be considered.

\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2c", + "creator": "Roman Losev", + "createdAt": 1430121447000, + "text": "

Example of using the visible check for adblocker is activated:

\n\n

\r\n
\r\n
$(document).ready(function(){\r\n  if(!$(\"#ablockercheck\").is(\":visible\"))\r\n    $(\"#ablockermsg\").text(\"Please disable adblocker.\").show();\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div class=\"ad-placement\" id=\"ablockercheck\"></div>\r\n<div id=\"ablockermsg\" style=\"display: none\"></div>
\r\n
\r\n
\r\n

\n\n

\"ablockercheck\" is a ID which adblocker blocks. So checking it if it is visible you are able to detect if adblocker is turned On.

\n", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2a", + "creator": "V31", + "createdAt": 1409343635000, + "text": "

A function can be created in order to check for visibility/display attributes in order to gauge whether the element is shown in the UI or not.

\n\n
function checkUIElementVisible(element) {\n    return ((element.css('display') !== 'none') && (element.css('visibility') !== 'hidden'));\n}\n
\n\n

Working Fiddle

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2b", + "creator": "Mathias Stavrou", + "createdAt": 1428409598000, + "text": "

\r\n
\r\n
$(document).ready(function() {\n   var visible = $('#tElement').is(':visible');\n\n   if(visible) {\n      alert(\"visible\");\n                    // Code\n   }\n   else\n   {\n      alert(\"hidden\");\n   }\n});
\r\n
<script src=\"https://code.jquery.com/jquery-1.10.2.js\"></script>\n\n<input type=\"text\" id=\"tElement\" style=\"display:block;\">Firstname</input>
\r\n
\r\n
\r\n

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2e", + "creator": "Sangeet Shah", + "createdAt": 1440418235000, + "text": "

This is some option to check that tag is visible or not

\n\n

\r\n
\r\n
 // using a pure CSS selector  \r\n   if ($('p:visible')) {  \r\n      alert('Paragraphs are visible (checked using a CSS selector) !');  \r\n   };  \r\n  \r\n   // using jQuery's is() method  \r\n   if ($('p').is(':visible')) {  \r\n      alert('Paragraphs are visible (checked using is() method)!');  \r\n   };  \r\n  \r\n   // using jQuery's filter() method  \r\n   if ($('p').filter(':visible')) {  \r\n      alert('Paragraphs are visible (checked using filter() method)!');  \r\n   };  \r\n  \r\n   // you can use :hidden instead of :visible to reverse the logic and check if an element is hidden  \r\n   // if ($('p:hidden')) {  \r\n   //    do something  \r\n   // };  
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2d", + "creator": "Prabhagaran", + "createdAt": 1439888655000, + "text": "
if($('#id_element').is(\":visible\")){\n   alert('shown');\n}else{\n   alert('hidden');\n}\n
\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff2f", + "creator": "cbertelegni", + "createdAt": 1458739302000, + "text": "
if($(\"h1\").is(\":hidden\")){\n    // your code..\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff30", + "creator": "Disapamok", + "createdAt": 1459758065000, + "text": "

You can just add a class when it is visible. Add a class, show. Then check for it have a class:

\n\n
$('#elementId').hasClass('show');\n
\n\n

It returns true if you have the show class.

\n\n

Add CSS like this:

\n\n
.show{ display: block; }\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff32", + "creator": "Abrar Jahin", + "createdAt": 1462192268000, + "text": "

You can use this:

\n\n
$(element).is(':visible');\n
\n\n

Example code

\n\n

\r\n
\r\n
$(document).ready(function()\r\n{\r\n    $(\"#toggle\").click(function()\r\n    {\r\n        $(\"#content\").toggle();\r\n    });\r\n\r\n    $(\"#visiblity\").click(function()\r\n    {\r\n       if( $('#content').is(':visible') )\r\n       {\r\n          alert(\"visible\"); // Put your code for visibility\r\n       }\r\n       else\r\n       {\r\n          alert(\"hidden\");\r\n       }\r\n    });\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js\"></script>\r\n\r\n<p id=\"content\">This is a Content</p>\r\n\r\n<button id=\"toggle\">Toggle Content Visibility</button>\r\n<button id=\"visibility\">Check Visibility</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff31", + "creator": "Oriol", + "createdAt": 1460423511000, + "text": "

This is how jQuery internally solves this problem:

\n\n
jQuery.expr.pseudos.visible = function( elem ) {\n    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n
\n\n

If you don't use jQuery, you can just leverage this code and turn it into your own function:

\n\n
function isVisible(elem) {\n    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n
\n\n

Which isVisible will return true as long as the element is visible.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff34", + "creator": "No one", + "createdAt": 1478348787000, + "text": "

As hide(), show() and toggle() attaches inline css (display:none or display:block) to element.\nSimilarly, we can easily use the ternary operator to check whether the element is hidden or visible by checking display CSS.

\n

UPDATE:

\n\n

So we can check for the property of an element that makes it invisible. So they are display: none and visibility: "hidden";

\n

We can create an object for checking property responsible for hiding element:

\n
var hiddenCssProps = {\ndisplay: "none",\nvisibility: "hidden"\n}\n
\n

We can check by looping through each key value in object matching if element property for key matches with hidden property value.

\n
var isHidden = false;\nfor(key in hiddenCssProps) {\n  if($('#element').css(key) == hiddenCssProps[key]) {\n     isHidden = true;\n   }\n}\n
\n

If you want to check property like element height: 0 or width: 0 or more, you can extend this object and add more property to it and can check.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff33", + "creator": "lmcDevloper", + "createdAt": 1464763009000, + "text": "

I searched for this, and none of the answers are correct for my case, so I've created a function that will return false if one's eyes can't see the element

\n\n
jQuery.fn.extend({\n  isvisible: function() {\n    //\n    //  This function call this: $(\"div\").isvisible()\n    //  Return true if the element is visible\n    //  Return false if the element is not visible for our eyes\n    //\n    if ( $(this).css('display') == 'none' ){\n        console.log(\"this = \" + \"display:none\");\n        return false;\n    }\n    else if( $(this).css('visibility') == 'hidden' ){\n        console.log(\"this = \" + \"visibility:hidden\");   \n        return false;\n    }\n    else if( $(this).css('opacity') == '0' ){\n        console.log(\"this = \" + \"opacity:0\");\n        return false;\n    }   \n    else{\n        console.log(\"this = \" + \"Is Visible\");\n        return true;\n    }\n  }  \n});\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3220e082fcc3049e91064", + "creator": "Questionnaire", + "createdAt": 1582531107000, + "text": "What about elements which are hidden beneath other items?", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff35", + "creator": "Wolfack", + "createdAt": 1480574765000, + "text": "
$( "div:visible" ).click(function() {\n  $( this ).css( "background", "yellow" );\n});\n$( "button" ).click(function() {\n  $( "div:hidden" ).show( "fast" );\n});\n
\n

API Documentation: visible Selector

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff36", + "creator": "Sky Yip", + "createdAt": 1481013851000, + "text": "

I just want to clarify that, in jQuery,

\n\n
\n

Elements can be considered hidden for several reasons:

\n \n \n \n

Elements with visibility: hidden or opacity: 0 are considered to be visible, since they still consume space in the layout. During animations that hide an element, the element is considered to be visible until the end of the animation.

\n \n

Source: :hidden Selector | jQuery API Documentation

\n
\n\n
if($('.element').is(':hidden')) {\n  // Do something\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff38", + "creator": "Abdul Aziz Al Basyir", + "createdAt": 1489986448000, + "text": "

There are too many methods to check for hidden elements. This is the best choice (I just recommended you):

\n\n
\n

Using jQuery, make an element, \"display:none\", in CSS for hidden.

\n
\n\n

The point is:

\n\n
$('element:visible')\n
\n\n

And an example for use:

\n\n
$('element:visible').show();\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff37", + "creator": "Arun Karnawat", + "createdAt": 1481621617000, + "text": "

There are quite a few ways to check if an element is visible or hidden in jQuery.

\n\n

Demo HTML for example reference

\n\n
<div id=\"content\">Content</div>\n<div id=\"content2\" style=\"display:none\">Content2</div>\n
\n\n

Use Visibility Filter Selector $('element:hidden') or $('element:visible')

\n\n\n\n
\n

Read more at http://api.jquery.com/category/selectors/visibility-filter-selectors/

\n
\n\n

Use is() Filtering

\n\n
    Example:\n       $('#content').is(\":visible\").css('color', '#EEE');\n\n    Or checking condition\n    if ($('#content').is(\":visible\")) {\n         // Perform action\n    }\n
\n\n
\n

Read more at http://api.jquery.com/is/

\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff39", + "creator": "Peter Wone", + "createdAt": 1490431770000, + "text": "

To be fair the question pre-dates this answer.

\n\n

I add it not to criticise the OP, but to help anyone still asking this question.

\n\n

The correct way to determine whether something is visible is to consult your view-model;

\n\n

If you don't know what that means then you are about to embark on a journey of discovery that will make your work a great deal less difficult.

\n\n

Here's an overview of the model-view-view-model architecture (MVVM).

\n\n

KnockoutJS is a binding library that will let you try this stuff out without learning an entire framework.

\n\n

And here's some JavaScript code and a DIV that may or may not be visible.

\n\n
<html>\n<body>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js\"></script>\n<script>\n    var vm = {\n        IsDivVisible: ko.observable(true);\n    }\n    vm.toggle = function(data, event) {\n      // Get current visibility state for the div\n      var x = IsDivVisible();\n      // Set it to the opposite\n      IsDivVisible(!x);\n    }\n    ko.applyBinding(vm);\n</script>\n<div data-bind=\"visible: IsDivVisible\">Peekaboo!</div>\n<button data-bind=\"click: toggle\">Toggle the div's visibility</button>\n</body>\n</html>\n
\n\n

Notice that the toggle function does not consult the DOM to determine the visibility of the div; it consults the view-model.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3a", + "creator": "user6119825", + "createdAt": 1493461522000, + "text": "
$('someElement').on('click', function(){ $('elementToToggle').is(':visible')\n
\n", + "upvotes": 1567, + "upvoterUsernames": [], + "downvotes": 1567, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3c", + "creator": "Antoine Auffray", + "createdAt": 1502214456000, + "text": "

Simply check for the display attribute (or visibility depending on what kind of invisibility you prefer). Example:

\n\n
if ($('#invisible').css('display') == 'none') {\n    // This means the HTML element with ID 'invisible' has its 'display' attribute set to 'none'\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3b", + "creator": "Alireza", + "createdAt": 1494052687000, + "text": "

Just simply check if that element is visible and it will return a boolean. jQuery hides the elements by adding display none to the element, so if you want to use pure JavaScript, you can still do that, for example:

\n\n
if (document.getElementById(\"element\").style.display === 'block') {\n  // Your element is visible; do whatever you'd like\n}\n
\n\n

Also, you can use jQuery as it seems the rest of your code is using that and you have smaller block of code. Something like the below in jQuery does the same trick for you:

\n\n
if ($(element).is(\":visible\")) {\n    // Your element is visible, do whatever you'd like\n};\n
\n\n

Also using the css method in jQuery can result in the same thing:

\n\n
if ($(element).css('display') === 'block') {\n    // Your element is visible, do whatever you'd like\n}\n
\n\n

Also in case of checking for visibility and display, you can do the below:

\n\n
if ($(this).css(\"display\") === \"block\" || $(this).css(\"visibility\") === \"visible\") {\n   // Your element is visible, do whatever you'd like\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3d", + "creator": "Disapamok", + "createdAt": 1506417062000, + "text": "

You can use a CSS class when it visible or hidden by toggling the class:

\n\n
.show{ display :block; }\n
\n\n

Set your jQuery toggleClass() or addClass() or removeClass();.

\n\n

As an example,

\n\n

jQuery('#myID').toggleClass('show')

\n\n

The above code will add show css class when the element don't have show and will remove when it has show class.

\n\n

And when you are checking if it visible or not, You can follow this jQuery code,

\n\n

jQuery('#myID').hasClass('show');

\n\n

Above code will return a boolean (true) when #myID element has our class (show) and false when it don't have the (show) class.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3e", + "creator": "user8903269", + "createdAt": 1513973545000, + "text": "
isHidden = function(element){\n    return (element.style.display === "none");\n};\n\nif(isHidden($("element")) == true){\n    // Something\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff40", + "creator": "Profesor08", + "createdAt": 1534297714000, + "text": "

If you want to check if an element is visible on the page, depending on the visibility of its parent, you can check if width and height of the element are both equal to 0.

\n\n

jQuery

\n\n

$element.width() === 0 && $element.height() === 0

\n\n

Vanilla

\n\n

element.clientWidth === 0 && element.clientHeight === 0

\n\n

Or

\n\n

element.offsetWidth === 0 && element.offsetHeight === 0

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff3f", + "creator": "user10145552", + "createdAt": 1532898442000, + "text": "

Instead of writing an event for every single element, do this:

\n\n
$('div').each(function(){\n  if($(this).css('display') === 'none'){\n    $(this).css({'display':'block'});\n  }\n});\n
\n\n

Also you can use it on the inputs:

\n\n
$('input').each(function(){\n  if($(this).attr('type') === 'hidden'){\n    $(this).attr('type', 'text');\n  }\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff41", + "creator": "Muhammad", + "createdAt": 1536744883000, + "text": "

A jQuery solution, but it is still a bit better for those who want to change the button text as well:

\n\n

\r\n
\r\n
$(function(){\r\n  $(\"#showHide\").click(function(){\r\n    var btn = $(this);\r\n    $(\"#content\").toggle(function () {\r\n      btn.text($(this).css(\"display\") === 'none' ? \"Show\" : \"Hide\");\r\n    });\r\n   });\r\n });
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n<button id=\"showHide\">Hide</button>\r\n<div id=\"content\">\r\n  <h2>Some content</h2>\r\n  <p>\r\n  What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.\r\n  </p>\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff42", + "creator": "L Y E S - C H I O U K H", + "createdAt": 1539183669000, + "text": "

1 • jQuery solution

\n\n

Methods to determine if an element is visible in jQuery

\n\n
<script>\nif ($(\"#myelement\").is(\":visible\")){alert (\"#myelement is visible\");}\nif ($(\"#myelement\").is(\":hidden\")){alert (\"#myelement is hidden\"); }\n</script>\n
\n\n

Loop on all visible div children of the element of id 'myelement':

\n\n
$(\"#myelement div:visible\").each( function() {\n //Do something\n});\n
\n\n

Peeked at source of jQuery

\n\n

This is how jQuery implements this feature:

\n\n
jQuery.expr.filters.visible = function( elem ) {\n    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n
\n\n

2 • How to check if an element is off-screen - CSS

\n\n

Using Element.getBoundingClientRect() you can easily detect whether or not your element is within the boundaries of your viewport (i.e. onscreen or offscreen):

\n\n
jQuery.expr.filters.offscreen = function(el) {\n  var rect = el.getBoundingClientRect();\n  return (\n           (rect.x + rect.width) < 0 \n             || (rect.y + rect.height) < 0\n             || (rect.x > window.innerWidth || rect.y > window.innerHeight)\n         );\n};\n
\n\n

You could then use that in several ways:

\n\n
// Returns all elements that are offscreen\n$(':offscreen');\n\n// Boolean returned if element is offscreen\n$('div').is(':offscreen');\n
\n\n

If you use Angular, check: Don’t use hidden attribute with Angular

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff44", + "creator": "Aravinda Meewalaarachchi", + "createdAt": 1563446222000, + "text": "

You can use jQuery's is() function to check the selected element visible or hidden. This method traverses along the DOM elements to find a match, which satisfies the passed parameter. It will return true if there is a match otherwise returns false.

\n\n
<script>\n    ($(\"#myelement\").is(\":visible\"))? alert(\"#myelement is visible\") : alert(\"#myelement is hidden\");\n</script>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff43", + "creator": "Kamil Kiełczewski", + "createdAt": 1560322004000, + "text": "
content.style.display != 'none'\n
\n

\r\n
\r\n
function toggle() {\n  $(content).toggle();\n  let visible= content.style.display != 'none'\n  console.log('visible:', visible);\n}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n\n<button onclick=\"toggle()\">Show/hide</button>\n<div id=\"content\">ABC</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff46", + "creator": "Brane", + "createdAt": 1581796451000, + "text": "

Extended function for checking if element is visible, display none, or even the opacity level

\n

It returns false if the element is not visible.

\n
function checkVisible(e) {\n    if (!(e instanceof Element)) throw Error('not an Element');\n    const elementStyle = getComputedStyle(e);\n    if (elementStyle.display === 'none' || elementStyle.visibility !== 'visible' || elementStyle.opacity < 0.1) return false;\n    if (e.offsetWidth + e.offsetHeight + e.getBoundingClientRect().height +\n        e.getBoundingClientRect().width === 0) {\n        return false;\n    }\n    const elemCenter   = {\n        x: e.getBoundingClientRect().left + e.offsetWidth / 2,\n        y: e.getBoundingClientRect().top + e.offsetHeight / 2\n    };\n    if (elemCenter.x < 0 || elemCenter.y < 0) return false;\n    if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;\n    if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;\n    let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);\n    do {\n        if (pointContainer === e) return true;\n    } while (pointContainer = pointContainer.parentNode);\n    return false;\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff45", + "creator": "Ankush Kumar", + "createdAt": 1575961547000, + "text": "

The below code checks if an element is hidden in jQuery or visible

\n\n
// You can also do this...\n\n        $(\"button\").click(function(){\n            // show hide paragraph on button click\n            $(\"p\").toggle(\"slow\", function(){\n                // check paragraph once toggle effect is completed\n                if($(\"p\").is(\":visible\")){\n                    alert(\"The paragraph  is visible.\");\n                } else{\n                    alert(\"The paragraph  is hidden.\");\n                }\n            });\n        });\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff48", + "creator": "Hasee Amarathunga", + "createdAt": 1600837545000, + "text": "

Using hidden selection you can match all hidden elements

\n
$('element:hidden')\n
\n

Using Visible selection you can match all visible elements

\n
$('element:visible')\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff47", + "creator": "Mojtaba Nava", + "createdAt": 1599032171000, + "text": "
   hideShow(){\n  $("#accordionZiarat").hide();\n  // Checks CSS content for display:[none|block], ignores visibility:[true|false]\n  if ($("#accordionZiarat").is(":visible")) {\n    $("#accordionZiarat").hide();\n  }\n\n  \n  else if ($("#accordionZiarat").is(":hidden")) {\n    $("#accordionZiarat").show();\n  }\n\n  else{\n\n  }\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff4a", + "creator": "Vicky P", + "createdAt": 1640079982000, + "text": "

There are two ways to check visibility of element.

\n

Solution #1

\n
if($('.selector').is(':visible')){\n    // element is visible\n}else{\n    // element is hidden\n}\n
\n

Solution #2

\n
if($('.selector:visible')){\n    // element is visible\n}else{\n    // element is hidden\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff49", + "creator": "centralhubb.com", + "createdAt": 1611315133000, + "text": "
if($(element).is(":visible")) {\n  console.log('element is visible');\n} else {\n  console.log('element is not visible');\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff4c", + "creator": "Enrico König", + "createdAt": 1641397410000, + "text": "

The easiest answer to this question is this:

\n
function checkUIElementVisible(element) {\n    return ((element.css('display') !== 'none') && (element.css('visibility') !== 'hidden'));\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff4b", + "creator": "udorb b", + "createdAt": 1640332852000, + "text": "

if you hide with class - d-none

\n
if (!$('#ele').hasClass('d-none')) {\n        $('#ele').addClass('d-none'); //hide \n\n    }\n\n\n  \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32210082fcc3049e9107b", + "creator": "JJ Shaw", + "createdAt": 1658935015000, + "text": "This is only if you are using Bootsrap", + "upvotes": 3296, + "upvoterUsernames": [], + "downvotes": 3296, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff4d", + "creator": "Sahil Thummar", + "createdAt": 1651663332000, + "text": "

Use any of visible Selector or hidden Selector to check visiblity:

\n
    \n
  1. Use :visible Selector - jQuery( ":visible" )
  2. \n
  3. Use :hidden Selector - jQuery( ":hidden" )
  4. \n
\n

use .toggle() - Display and hide element

\n

\r\n
\r\n
function checkVisibility() {\n    // check if element is hidden or not and return true false\n    console.log($('#element').is(':hidden'));\n\n    // check if element is visible or not and return true false\n    console.log($('#element').is(':visible'));\n\n    if ( $('#element').css('display') == 'none' || $('#element').css(\"visibility\") == \"hidden\"){\n        console.log('element is hidden');\n    } else {\n        console.log('element is visibile');\n    }\n}\n\ncheckVisibility()\n$('#toggle').click(function() {\n    $('#element').toggle()\n    checkVisibility()\n})
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n<button id='toggle'>Toggle</button>\n<div style='display:none' id='element'>\n    <h1>Hello</h1>\n    <p>it's visible</p>\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff4e", + "creator": "user17087887", + "createdAt": 1656335139000, + "text": "

You can try this

\n
$(document).ready(function() {\n   var view = $(this).is(':visible');\n\n   if(view) {\n      alert("view");\n                    // Code\n   }\n   else\n   {\n      alert("hidden");\n   }\n});\n
\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3041964, + "uvac": 3042028 + } + }, + { + "_id": "62f321bb082fcc3049e8fed4", + "title": "How can I change an element's class with JavaScript?", + "title-lowercase": "how can i change an element's class with javascript?", + "creator": "Nathan Smith", + "createdAt": 1223842003000, + "status": "open", + "text": "

How can I change the class of an HTML element in response to an onclick or any other events using JavaScript?

\n", + "upvotes": 5528, + "upvoterUsernames": [], + "downvotes": 2297, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 3174202, + "answers": 31, + "answerItems": [ + { + "_id": "62f321c3082fcc3049e907a7", + "creator": "Jon Galloway", + "createdAt": 1223842336000, + "text": "

This is easiest with a library like jQuery:

\n\n
<input type=\"button\" onClick=\"javascript:test_byid();\" value=\"id='second'\" />\n\n<script>\nfunction test_byid()\n{\n    $(\"#second\").toggleClass(\"highlight\");\n}\n</script>\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325df082fcc3049e91e96", + "creator": "Quentin", + "createdAt": 1223885967000, + "text": "In that context, it isn't the pseudo-protocol - it's a loop label ... only there is no loop for it TO label.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907a9", + "creator": "Eric Wendelin", + "createdAt": 1223843586000, + "text": "

You can use node.className like so:

\n
document.getElementById('foo').className = 'bar';\n
\n

This should work in Internet Explorer 5.5 and up according to PPK.

\n", + "upvotes": 158, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325df082fcc3049e91e98", + "creator": "Eric Sebasta", + "createdAt": 1444422795000, + "text": "this would overwrite any and all other classes on the object... so it is not that simple.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907aa", + "creator": "Eric Bailey", + "createdAt": 1260310512000, + "text": "

The line

\n\n
document.getElementById(\"MyElement\").className = document.getElementById(\"MyElement\").className.replace(/\\bMyClass\\b/','')\n
\n\n

should be:

\n\n
document.getElementById(\"MyElement\").className = document.getElementById(\"MyElement\").className.replace('/\\bMyClass\\b/','');\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907a8", + "creator": "roenving", + "createdAt": 1223842593000, + "text": "

No offense, but it's unclever to change class on-the-fly as it forces the CSS interpreter to recalculate the visual presentation of the entire web page.

\n\n

The reason is that it is nearly impossible for the CSS interpreter to know if any inheritance or cascading could be changed, so the short answer is:

\n\n

Never ever change className on-the-fly !-)

\n\n

But usually you'll only need to change a property or two, and that is easily implemented:

\n\n
function highlight(elm){\n    elm.style.backgroundColor =\"#345\";\n    elm.style.color = \"#fff\";\n}\n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 130, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325df082fcc3049e91e9c", + "creator": "Peter Boughton", + "createdAt": 1223848176000, + "text": "Why would you even want thousands of rows nested with other elements? Also, what operating system & browser was this delay with?", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91e9e", + "creator": "Jason", + "createdAt": 1260310744000, + "text": "this is the worst idea ever. changing classes is by far and away the easiest and cleanest way to update your CSS on the fly.", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91e9f", + "creator": "Erik Olson", + "createdAt": 1308929983000, + "text": "To the “thousands of rows” unbelievers: doxygen. Hierarchical, collapsible menus in a side frame. IE7 had fatal issues with this so I used Firefox.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91ea1", + "creator": "Alex Jolig", + "createdAt": 1454266814000, + "text": "I'm wondering why you haven't deleted your answer yet! (it's 2016)", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91ea3", + "creator": "Krease", + "createdAt": 1465577269000, + "text": "@AlexJolig - the user hasn't logged in since 2009", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91ea5", + "creator": "Mr Anderson", + "createdAt": 1472760116000, + "text": "This is the second-most downvoted answer on the site.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91ea7", + "creator": "Lajos Mészáros", + "createdAt": 1492514729000, + "text": "I think "don't do it in the first place" is not an acceptable answer for "how to do it".", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907ab", + "creator": "Hiren Kansara", + "createdAt": 1306567165000, + "text": "

Change an element's CSS class with JavaScript in ASP.NET:

\n\n
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load\n    If Not Page.IsPostBack Then\n        lbSave.Attributes.Add(\"onmouseover\", \"this.className = 'LinkButtonStyle1'\")\n        lbSave.Attributes.Add(\"onmouseout\", \"this.className = 'LinkButtonStyle'\")\n        lbCancel.Attributes.Add(\"onmouseover\", \"this.className = 'LinkButtonStyle1'\")\n        lbCancel.Attributes.Add(\"onmouseout\", \"this.className = 'LinkButtonStyle'\")\n    End If\nEnd Sub\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907ac", + "creator": "Andrew Orsich", + "createdAt": 1306567973000, + "text": "

In one of my old projects that did not use jQuery, I built the following functions for adding, removing and checking if an element has a class:

\n
function hasClass(ele, cls) {\n    return ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'));\n}\n\nfunction addClass(ele, cls) {\n    if (!hasClass(ele, cls))\n        ele.className += " " + cls;\n}\n\nfunction removeClass(ele, cls) {\n    if (hasClass(ele, cls)) {\n        var reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)');\n        ele.className = ele.className.replace(reg, ' ');\n    }\n}\n
\n

So, for example, if I want onclick to add some class to the button I can use this:

\n
<script type="text/javascript">\n    function changeClass(btn, cls) {\n        if(!hasClass(btn, cls)) {\n            addClass(btn, cls);\n        }\n    }\n</script>\n...\n<button onclick="changeClass(this, "someClass")">My Button</button>\n
\n

By now for sure it would just be better to use jQuery.

\n", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e0082fcc3049e91eab", + "creator": "Mike", + "createdAt": 1374642969000, + "text": "This is great for when your client doesn't let you use jQuery. (Cause you end up almost building your own library.)", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91eac", + "creator": "kfrncs", + "createdAt": 1384751077000, + "text": "@Mike If the client doesn't let you use jQuery, could you not just go through and rebuild only the features you needed into your own library?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907ad", + "creator": "Tyilo", + "createdAt": 1312566252000, + "text": "

You could also just do:

\n\n
document.getElementById('id').classList.add('class');\ndocument.getElementById('id').classList.remove('class');\n
\n\n

And to toggle a class (remove if exists else add it):

\n\n
document.getElementById('id').classList.toggle('class');\n
\n", + "upvotes": 689, + "upvoterUsernames": [], + "downvotes": 222, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e0082fcc3049e91eaf", + "creator": "John", + "createdAt": 1319735819000, + "text": "I believe this is HTML5 dependent.", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91eb0", + "creator": "ELLIOTTCABLE", + "createdAt": 1321281254000, + "text": "You’ll need Eli Grey’s classList shim.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91eb2", + "creator": "andreszs", + "createdAt": 1407021235000, + "text": "Feature not supported until a few hours ago (added on IE10 and Android 3)", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91eb3", + "creator": "Wilf", + "createdAt": 1441558493000, + "text": "I think the Eli Grey shim can be found here...", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907ae", + "creator": "PseudoNinja", + "createdAt": 1316532368000, + "text": "

Using pure JavaScript code:

\n\n
function hasClass(ele, cls) {\n    return ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'));\n}\n\nfunction addClass(ele, cls) {\n    if (!this.hasClass(ele, cls)) ele.className += \" \" + cls;\n}\n\nfunction removeClass(ele, cls) {\n    if (hasClass(ele, cls)) {\n        var reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)');\n        ele.className = ele.className.replace(reg, ' ');\n    }\n}\n\nfunction replaceClass(ele, oldClass, newClass){\n    if(hasClass(ele, oldClass)){\n        removeClass(ele, oldClass);\n        addClass(ele, newClass);\n    }\n    return;\n}\n\nfunction toggleClass(ele, cls1, cls2){\n    if(hasClass(ele, cls1)){\n        replaceClass(ele, cls1, cls2);\n    }else if(hasClass(ele, cls2)){\n        replaceClass(ele, cls2, cls1);\n    }else{\n        addClass(ele, cls1);\n    }\n}\n
\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907af", + "creator": "Ben Flynn", + "createdAt": 1322341452000, + "text": "

Just to add on information from another popular framework, Google Closures, see their dom/classes class:

\n
goog.dom.classes.add(element, var_args)\n\ngoog.dom.classes.addRemove(element, classesToRemove, classesToAdd)\n\ngoog.dom.classes.remove(element, var_args)\n
\n

One option for selecting the element is using goog.dom.query with a CSS 3 selector:

\n
var myElement = goog.dom.query("#MyElement")[0];\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b0", + "creator": "Gopal Krishna Ranjan", + "createdAt": 1323337017000, + "text": "

This is working for me:

\n\n
function setCSS(eleID) {\n    var currTabElem = document.getElementById(eleID);\n\n    currTabElem.setAttribute(\"class\", \"some_class_name\");\n    currTabElem.setAttribute(\"className\", \"some_class_name\");\n}\n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e0082fcc3049e91eb7", + "creator": "Roman Polen.", + "createdAt": 1336475998000, + "text": "Excellent answer! Just left to add : Set for each CSS class name for selector to specify a style for a group of class elements", + "upvotes": 124, + "upvoterUsernames": [], + "downvotes": 124, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91eb9", + "creator": "Lukasz 'Severiaan' Grela", + "createdAt": 1342700965000, + "text": "This works for me on FF, but when I've tried to use el.className = "newStyle"; it didn't worked, why?", + "upvotes": 4789, + "upvoterUsernames": [], + "downvotes": 4789, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907b2", + "creator": "Alex Robinson", + "createdAt": 1335934757000, + "text": "

A couple of minor notes and tweaks on the previous regexes:

\n\n

You'll want to do it globally in case the class list has the class name more than once. And, you'll probably want to strip spaces from the ends of the class list and convert multiple spaces to one space to keep from getting runs of spaces. None of these things should be a problem if the only code dinking with the class names uses the regex below and removes a name before adding it. But. Well, who knows who might be dinking with the class name list.

\n\n

This regex is case insensitive so that bugs in class names may show up before the code is used on a browser that doesn't care about case in class names.

\n\n
var s = \"testing   one   four  one  two\";\nvar cls = \"one\";\nvar rg          = new RegExp(\"(^|\\\\s+)\" + cls + \"(\\\\s+|$)\", 'ig');\nalert(\"[\" + s.replace(rg, ' ') + \"]\");\nvar cls = \"test\";\nvar rg          = new RegExp(\"(^|\\\\s+)\" + cls + \"(\\\\s+|$)\", 'ig');\nalert(\"[\" + s.replace(rg, ' ') + \"]\");\nvar cls = \"testing\";\nvar rg          = new RegExp(\"(^|\\\\s+)\" + cls + \"(\\\\s+|$)\", 'ig');\nalert(\"[\" + s.replace(rg, ' ') + \"]\");\nvar cls = \"tWo\";\nvar rg          = new RegExp(\"(^|\\\\s+)\" + cls + \"(\\\\s+|$)\", 'ig');\nalert(\"[\" + s.replace(rg, ' ') + \"]\");\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b1", + "creator": "Travis J", + "createdAt": 1325790841000, + "text": "

Wow, surprised there are so many overkill answers here...

\n\n
<div class=\"firstClass\" onclick=\"this.className='secondClass'\">\n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e0082fcc3049e91ebd", + "creator": "Gabe", + "createdAt": 1334328641000, + "text": "I would say unobtrusive javascript is terrible practice for writing example code...", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91ebf", + "creator": "thomasrutter", + "createdAt": 1427672193000, + "text": "I would disagree, because I think example code should set a good example.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91ec0", + "creator": "Anthony Rutledge", + "createdAt": 1446264115000, + "text": "A good example should instruct and spark the imagination at the same time. It should not replace thought, but inspire it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e0082fcc3049e91ec2", + "creator": "gcampbell", + "createdAt": 1465052683000, + "text": "The other answers aren't overkill, they also keep existing classes on the element.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907b3", + "creator": "shingokko", + "createdAt": 1336185982000, + "text": "

I would use jQuery and write something like this:

\n
jQuery(function($) {\n    $("#some-element").click(function() {\n        $(this).toggleClass("clicked");\n    });\n});\n
\n

This code adds a function to be called when an element of the id some-element is clicked. The function appends clicked to the element's class attribute if it's not already part of it, and removes it if it's there.

\n

Yes, you do need to add a reference to the jQuery library in your page to use this code, but at least you can feel confident the most functions in the library would work on pretty much all the modern browsers, and it will save you time implementing your own code to do the same.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b4", + "creator": "alfred", + "createdAt": 1350476480000, + "text": "

Here's my version, fully working:

\n\n
function addHTMLClass(item, classname) {\n    var obj = item\n    if (typeof item==\"string\") {\n        obj = document.getElementById(item)\n    }\n    obj.className += \" \" + classname\n}\n\nfunction removeHTMLClass(item, classname) {\n    var obj = item\n    if (typeof item==\"string\") {\n        obj = document.getElementById(item)\n    }\n    var classes = \"\"+obj.className\n    while (classes.indexOf(classname)>-1) {\n        classes = classes.replace (classname, \"\")\n    }\n    obj.className = classes\n}\n
\n\n

Usage:

\n\n
<tr onmouseover='addHTMLClass(this,\"clsSelected\")'\nonmouseout='removeHTMLClass(this,\"clsSelected\")' >\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e0082fcc3049e91ec6", + "creator": "alfred", + "createdAt": 1350476862000, + "text": "thanks, i fixed it (not the prefix problem). it's the old accepted answer that have a bug with the regexp.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907b5", + "creator": "moka", + "createdAt": 1365675216000, + "text": "

As well you could extend HTMLElement object, in order to add methods to add, remove, toggle and check classes (gist):

\n\n
HTMLElement = typeof(HTMLElement) != 'undefiend' ? HTMLElement : Element;\n\nHTMLElement.prototype.addClass = function(string) {\n  if (!(string instanceof Array)) {\n    string = string.split(' ');\n  }\n  for(var i = 0, len = string.length; i < len; ++i) {\n    if (string[i] && !new RegExp('(\\\\s+|^)' + string[i] + '(\\\\s+|$)').test(this.className)) {\n      this.className = this.className.trim() + ' ' + string[i];\n    }\n  }\n}\n\nHTMLElement.prototype.removeClass = function(string) {\n  if (!(string instanceof Array)) {\n    string = string.split(' ');\n  }\n  for(var i = 0, len = string.length; i < len; ++i) {\n    this.className = this.className.replace(new RegExp('(\\\\s+|^)' + string[i] + '(\\\\s+|$)'), ' ').trim();\n  }\n}\n\nHTMLElement.prototype.toggleClass = function(string) {\n  if (string) {\n    if (new RegExp('(\\\\s+|^)' + string + '(\\\\s+|$)').test(this.className)) {\n      this.className = this.className.replace(new RegExp('(\\\\s+|^)' + string + '(\\\\s+|$)'), ' ').trim();\n    } else {\n      this.className = this.className.trim() + ' ' + string;\n    }\n  }\n}\n\nHTMLElement.prototype.hasClass = function(string) {\n  return string && new RegExp('(\\\\s+|^)' + string + '(\\\\s+|$)').test(this.className);\n}\n
\n\n

And then just use like this (on click will add or remove class):

\n\n
document.getElementById('yourElementId').onclick = function() {\n  this.toggleClass('active');\n}\n
\n\n

Here is demo.

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e1082fcc3049e91ec8", + "creator": "moka", + "createdAt": 1408610216000, + "text": "Sorry @Jackson_Sandland but you've totally missed the point, which is not to use jQuery at all.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e907b6", + "creator": "Salman A", + "createdAt": 1390037285000, + "text": "

I use the following vanilla JavaScript functions in my code. They use regular expressions and indexOf but do not require quoting special characters in regular expressions.

\n\n
function addClass(el, cn) {\n    var c0 = (\" \" + el.className + \" \").replace(/\\s+/g, \" \"),\n        c1 = (\" \" + cn + \" \").replace(/\\s+/g, \" \");\n    if (c0.indexOf(c1) < 0) {\n        el.className = (c0 + c1).replace(/\\s+/g, \" \").replace(/^ | $/g, \"\");\n    }\n}\n\nfunction delClass(el, cn) {\n    var c0 = (\" \" + el.className + \" \").replace(/\\s+/g, \" \"),\n        c1 = (\" \" + cn + \" \").replace(/\\s+/g, \" \");\n    if (c0.indexOf(c1) >= 0) {\n        el.className = c0.replace(c1, \" \").replace(/\\s+/g, \" \").replace(/^ | $/g, \"\");\n    }\n}\n
\n\n

You can also use element.classList in modern browsers.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b7", + "creator": "uttamcafedeweb", + "createdAt": 1396764614000, + "text": "

Here is a simple jQuery code to do that:

\n
$(".class1").click(function(argument) {\n    $(".parentclass").removeClass("classtoremove");\n    setTimeout(function (argument) {\n        $(".parentclass").addClass("classtoadd");\n    }, 100);\n});\n
\n

Here,

\n\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b8", + "creator": "StackSlave", + "createdAt": 1426818901000, + "text": "

Just thought I'd throw this in:

\n\n
function inArray(val, ary){\n  for(var i=0,l=ary.length; i<l; i++){\n    if(ary[i] === val){\n      return true;\n    }\n  }\n  return false;\n}\nfunction removeClassName(classNameS, fromElement){\n  var x = classNameS.split(/\\s/), s = fromElement.className.split(/\\s/), r = [];\n  for(var i=0,l=s.length; i<l; i++){\n    if(!iA(s[i], x))r.push(s[i]);\n  }\n  fromElement.className = r.join(' ');\n}\nfunction addClassName(classNameS, toElement){\n  var s = toElement.className.split(/\\s/);\n  s.push(c); toElement.className = s.join(' ');\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907ba", + "creator": "Eugene Tiurin", + "createdAt": 1446659827000, + "text": "

Change an element's class in vanilla JavaScript with Internet Explorer 6 support

\n

You may try to use the node attributes property to keep compatibility with old browsers, even Internet Explorer 6:

\n

\r\n
\r\n
function getClassNode(element) {\n  for (var i = element.attributes.length; i--;)\n    if (element.attributes[i].nodeName === 'class')\n      return element.attributes[i];\n}\n\nfunction removeClass(classNode, className) {\n  var index, classList = classNode.value.split(' ');\n  if ((index = classList.indexOf(className)) > -1) {\n    classList.splice(index, 1);\n    classNode.value = classList.join(' ');\n  }\n}\n\nfunction hasClass(classNode, className) {\n  return classNode.value.indexOf(className) > -1;\n}\n\nfunction addClass(classNode, className) {\n  if (!hasClass(classNode, className))\n    classNode.value += ' ' + className;\n}\n\ndocument.getElementById('message').addEventListener('click', function() {\n  var classNode = getClassNode(this);\n  var className = hasClass(classNode, 'red') && 'blue' || 'red';\n\n  removeClass(classNode, 'red');\n  removeClass(classNode, 'blue');\n\n  addClass(classNode, className);\n})
\r\n
.red {\n  color: red;\n}\n.red:before {\n  content: 'I am red! ';\n}\n.red:after {\n  content: ' again';\n}\n.blue {\n  color: blue;\n}\n.blue:before {\n  content: 'I am blue! '\n}
\r\n
<span id=\"message\" class=\"\">Click me</span>
\r\n
\r\n
\r\n

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907b9", + "creator": "kofifus", + "createdAt": 1446015621000, + "text": "

Here's a toggleClass to toggle/add/remove a class on an element:

\n
// If newState is provided add/remove theClass accordingly, otherwise toggle theClass\nfunction toggleClass(elem, theClass, newState) {\n    var matchRegExp = new RegExp('(?:^|\\\\s)' + theClass + '(?!\\\\S)', 'g');\n    var add=(arguments.length>2 ? newState : (elem.className.match(matchRegExp) == null));\n\n    elem.className=elem.className.replace(matchRegExp, ''); // clear all\n    if (add) elem.className += ' ' + theClass;\n}\n
\n

See the JSFiddle.

\n

Also see my answer here for creating a new class dynamically.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907bb", + "creator": "Ronnie Royston", + "createdAt": 1492743051000, + "text": "

Just use myElement.classList="new-class" unless you need to maintain other existing classes in which case you can use the classList.add, .remove methods.

\n

\r\n
\r\n
var doc = document;\nvar divOne = doc.getElementById(\"one\");\nvar goButton = doc.getElementById(\"go\");\n\ngoButton.addEventListener(\"click\", function() {\n  divOne.classList=\"blue\";\n});
\r\n
div{\n  min-height: 48px;\n  min-width: 48px;\n}\n.bordered{\n  border: 1px solid black;\n}\n.green{\n  background: green;\n}\n.blue{\n  background: blue;\n}
\r\n
<button id=\"go\">Change Class</button>\n\n<div id=\"one\" class=\"bordered green\">\n\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907bc", + "creator": "Alireza", + "createdAt": 1496064593000, + "text": "

OK, I think in this case you should use jQuery or write your own Methods in pure JavaScript. My preference is adding my own methods rather than injecting all jQuery to my application if I don't need that for other reasons.

\n

I'd like to do something like below as methods to my JavaScript framework to add few functionalities which handle adding classes, deleting classes, etc. Similar to jQuery, this is fully supported in IE9+. Also my code is written in ES6, so you need to make sure your browser support it or you using something like Babel, otherwise need to use ES5 syntax in your code. Also in this way, we finding element via ID, which means the element needs to have an ID to be selected:

\n
// Simple JavaScript utilities for class management in ES6\nvar classUtil = {\n\n  addClass: (id, cl) => {\n    document.getElementById(id).classList.add(cl);\n  },\n\n  removeClass: (id, cl) => {\n    document.getElementById(id).classList.remove(cl);\n  },\n\n  hasClass: (id, cl) => {\n    return document.getElementById(id).classList.contains(cl);\n  },\n\n  toggleClass: (id, cl) => {\n    document.getElementById(id).classList.toggle(cl);\n  }\n\n}\n
\n

And you can simply use them as below. Imagine your element has id of 'id' and class of 'class'. Make sure you pass them as a string. You can use the utility as below:

\n
classUtil.addClass('myId', 'myClass');\nclassUtil.removeClass('myId', 'myClass');\nclassUtil.hasClass('myId', 'myClass');\nclassUtil.toggleClass('myId', 'myClass');\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907bd", + "creator": "Willem van der Veen", + "createdAt": 1537521792000, + "text": "

classList DOM API:

\n

A very convenient manner of adding and removing classes is the classList DOM API. This API allows us to select all classes of a specific DOM element in order to modify the list using JavaScript. For example:

\n

\r\n
\r\n
const el = document.getElementById(\"main\");\nconsole.log(el.classList);
\r\n
<div class=\"content wrapper animated\" id=\"main\"></div>
\r\n
\r\n
\r\n

\n

We can observe in the log that we are getting back an object with not only the classes of the element, but also many auxiliary methods and properties. This object inherits from the interface DOMTokenList, an interface which is used in the DOM to represent a set of space separated tokens (like classes).

\n

Example:

\n

\r\n
\r\n
const el = document.getElementById('container');\n\nfunction addClass () {\n    el.classList.add('newclass');\n}\n\n\nfunction replaceClass () {\n    el.classList.replace('foo', 'newFoo');\n}\n\n\nfunction removeClass () {\n    el.classList.remove('bar');\n}
\r\n
button{\n  margin: 20px;\n}\n\n.foo{\n  color: red;\n}\n\n.newFoo {\n  color: blue;\n}\n\n.bar{\n  background-color: powderblue;\n}\n\n.newclass{\n  border: 2px solid green;\n}
\r\n
<div class=\"foo bar\" id=\"container\">\n  \"Sed ut perspiciatis unde omnis\n  iste natus error sit voluptatem accusantium doloremque laudantium,\n  totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et\n  quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam\n  voluptatem quia voluptas\n </div>\n\n<button onclick=\"addClass()\">AddClass</button>\n\n<button onclick=\"replaceClass()\">ReplaceClass</button>\n\n<button onclick=\"removeClass()\">removeClass</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907be", + "creator": "Danish Khan", + "createdAt": 1549461777000, + "text": "

Yes, there are many ways to do this. In ES6 syntax we can achieve easily. Use this code toggle add and remove class.

\n

\r\n
\r\n
const tabs=document.querySelectorAll('.menu li');\n\nfor(let tab of tabs){\n\n  tab.onclick = function(){\n\n    let activetab = document.querySelectorAll('li.active');\n\n    activetab[0].classList.remove('active')\n\n    tab.classList.add('active');\n  }\n\n}
\r\n
body {\n    padding: 20px;\n    font-family: sans-serif;\n}\n\nul {\n    margin: 20px 0;\n    list-style: none;\n}\n\nli {\n    background: #dfdfdf;\n    padding: 10px;\n    margin: 6px 0;\n    cursor: pointer;\n}\n\nli.active {\n    background: #2794c7;\n    font-weight: bold;\n    color: #ffffff;\n}
\r\n
<i>Please click an item:</i>\n\n<ul class=\"menu\">\n  <li class=\"active\"><span>Three</span></li>\n  <li><span>Two</span></li>\n  <li><span>One</span></li>\n</ul>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907bf", + "creator": "tfont", + "createdAt": 1550830955000, + "text": "

TL;DR:

\n
document.getElementById('id').className = 'class'\n
\n

OR

\n
document.getElementById('id').classList.add('class');\ndocument.getElementById('id').classList.remove('class');\n
\n

That's it.

\n

And, if you really want to know the why and educate yourself then I suggest reading Peter Boughton's answer. It's perfect.

\n

Note:

\n

This is possible with (document or event):

\n\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c0", + "creator": "Kamil Kiełczewski", + "createdAt": 1555239102000, + "text": "

Try:

\n
element.className='second'\n
\n

\r\n
\r\n
function change(box) { box.className='second' }
\r\n
.first  { width:  70px; height:  70px; background: #ff0                 }\n.second { width: 150px; height: 150px; background: #f00; transition: 1s }
\r\n
<div onclick=\"change(this)\" class=\"first\">Click me</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c2", + "creator": "donatso", + "createdAt": 1567611127000, + "text": "
function classed(el, class_name, add_class) {\n  const re = new RegExp("(?:^|\\\\s)" + class_name + "(?!\\\\S)", "g");\n  if (add_class && !el.className.match(re)) el.className += " " + class_name\n  else if (!add_class) el.className = el.className.replace(re, '');\n}\n
\n

Using Peter Boughton's answer, here is a simple cross-browser function to add and remove class.

\n

Add class:

\n
classed(document.getElementById("denis"), "active", true)\n
\n

Remove class:

\n
classed(document.getElementById("denis"), "active", false)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c1", + "creator": "Brian Nezhad", + "createdAt": 1566498509000, + "text": "
\n

THE OPTIONS.

\n

Here is a little style vs. classList examples to get you to see what are the options you have available and how to use classList to do what you want.

\n

style vs. classList

\n

The difference between style and classList is that with style you're adding to the style properties of the element, but classList is kinda controlling the class(es) of the element (add, remove, toggle, contain), I will show you how to use the add and remove method since those are the popular ones.

\n
\n

Style Example

\n

If you want to add margin-top property into an element, you would do in such:

\n
// Get the Element\nconst el = document.querySelector('#element');\n\n// Add CSS property \nel.style.margintop = "0px"\nel.style.margintop = "25px" // This would add a 25px to the top of the element.\n
\n
\n

classList Example

\n

Let say we have a <div class="class-a class-b">, in this case, we have 2 classes added to our div element already, class-a and class-b, and we want to control what classes remove and what class to add. This is where classList becomes handy.

\n

Remove class-b

\n
// Get the Element\nconst el = document.querySelector('#element');\n\n// Remove class-b style from the element\nel.classList.remove("class-b")\n\n
\n

Add class-c

\n
// Get the Element\nconst el = document.querySelector('#element');\n\n// Add class-b style from the element\nel.classList.add("class-c")\n\n
\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c3", + "creator": "Jai Prakash", + "createdAt": 1584707124000, + "text": "

There is a property, className, in JavaScript to change the name of the class of an HTML element. The existing class value will be replaced with the new one, that you have assigned in className.

\n
<!DOCTYPE html>\n<html>\n<head>\n<title>How can I change the class of an HTML element in JavaScript?</title>\n<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">\n</head>\n<body>\n<h1 align="center"><i class="fa fa-home" id="icon"></i></h1><br />\n\n<center><button id="change-class">Change Class</button></center>\n\n<script>\nvar change_class = document.getElementById("change-class");\nchange_class.onclick = function()\n{\n    var icon=document.getElementById("icon");\n    icon.className = "fa fa-gear";\n}\n</script>\n</body>\n</html>\n
\n

Credit - How To Change Class Name of an HTML Element in JavaScript

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c4", + "creator": "timbo", + "createdAt": 1588547138000, + "text": "

The OP question was How can I change an element's class with JavaScript?

\n

Modern browsers allow you to do this with one line of JavaScript:

\n

document.getElementById('id').classList.replace('span1', 'span2')

\n

The classList attribute provides a DOMTokenList which has a variety of methods. You can operate on an element's classList using simple manipulations like add(), remove() or replace(). Or get very sophisticated and manipulate classes like you would an object or Map with keys(), values(), and entries().

\n

Peter Boughton's answer is a great one, but it's now over a decade old. All modern browsers now support DOMTokenList - see https://caniuse.com/#search=classList and even Internet Explorer 11 supports some DOMTokenList methods.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e907c5", + "creator": "gomn", + "createdAt": 1631275048000, + "text": "

For IE v6-9 (in which classList is not supported and you don't want to use polyfills):

\n
var elem = document.getElementById('some-id');\n\n// don't forget the extra space before the class name\nvar classList = elem.getAttribute('class') + ' other-class-name';\n\nelem.setAttribute('class', classList);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c3082fcc3049e90784", + "creator": "Teepeemm", + "createdAt": 1631847230000, + "text": "@ImanBahrampour That will obliterate any existing classes.", + "upvotes": 218, + "upvoterUsernames": [], + "downvotes": 218, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3179731, + "uvac": 3179762 + } + }, + { + "_id": "62f321bb082fcc3049e8feb3", + "title": "How do I remove a property from a JavaScript object?", + "title-lowercase": "how do i remove a property from a javascript object?", + "creator": "johnstok", + "createdAt": 1224154665000, + "status": "open", + "text": "

Given an object:

\n
let myObject = {\n  "ircEvent": "PRIVMSG",\n  "method": "newURI",\n  "regex": "^http://.*"\n};\n
\n

How do I remove the property regex to end up with the following myObject?

\n
let myObject = {\n  "ircEvent": "PRIVMSG",\n  "method": "newURI"\n};\n
\n", + "upvotes": 10893, + "upvoterUsernames": [], + "downvotes": 3805, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2840945, + "answers": 31, + "answerItems": [ + { + "_id": "62f321bd082fcc3049e900af", + "creator": "redsquare", + "createdAt": 1224154981000, + "text": "

\r\n
\r\n
var myObject = {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\r\n    \r\ndelete myObject.regex;\r\n\r\nconsole.log ( myObject.regex); // logs: undefined
\r\n
\r\n
\r\n

\n\n

This works in Firefox and Internet Explorer, and I think it works in all others.

\n", + "upvotes": 599, + "upvoterUsernames": [], + "downvotes": 298, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ae", + "creator": "nickf", + "createdAt": 1224154733000, + "text": "

To remove a property from an object (mutating the object), you can do it like this:

\n
delete myObject.regex;\n// or,\ndelete myObject['regex'];\n// or,\nvar prop = "regex";\ndelete myObject[prop];\n
\n

Demo\n

\r\n
\r\n
var myObject = {\n    \"ircEvent\": \"PRIVMSG\",\n    \"method\": \"newURI\",\n    \"regex\": \"^http://.*\"\n};\ndelete myObject.regex;\n\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n

For anyone interested in reading more about it, Stack Overflow user kangax has written an incredibly in-depth blog post about the delete statement on their blog, Understanding delete. It is highly recommended.

\n

If you'd like a new object with all the keys of the original except some, you could use destructuring.

\n

Demo\n

\r\n
\r\n
let myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\"\n};\n\n// assign the key regex to the variable _ indicating it will be unused\nconst {regex: _, ...newObj} = myObject;\n\nconsole.log(newObj);   // has no 'regex' key\nconsole.log(myObject); // remains unchanged
\r\n
\r\n
\r\n

\n", + "upvotes": 16673, + "upvoterUsernames": [], + "downvotes": 7254, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f322a6082fcc3049e9126a", + "creator": "ankush981", + "createdAt": 1629842215000, + "text": "The problem I've had with this approach is that if the destructing is inside a conditional, it makes ESlint go bonkers.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f322a6082fcc3049e9126b", + "creator": "Leonardo Rick", + "createdAt": 1656431332000, + "text": "I used const {[dynamic]: regex, ...newObj} = myObject; for dynamic keys on the object. So cool! Thanks", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322a6082fcc3049e9126d", + "creator": "Paveloosha", + "createdAt": 1659954389000, + "text": "@nickf, "const {regex, ...newObj} = myObject" does the same, then what is the use of "regex: _"?", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900b1", + "creator": "Thaddeus Albers", + "createdAt": 1400957606000, + "text": "

Another alternative is to use the Underscore.js library.

\n\n

Note that _.pick() and _.omit() both return a copy of the object and don't directly modify the original object. Assigning the result to the original object should do the trick (not shown).

\n\n

Reference: link _.pick(object, *keys)

\n\n

Return a copy of the object, filtered to only have values for the \nwhitelisted keys (or array of valid keys).

\n\n
var myJSONObject = \n{\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\n\n_.pick(myJSONObject, \"ircEvent\", \"method\");\n=> {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\"};\n
\n\n

Reference: link _.omit(object, *keys)

\n\n

Return a copy of the object, filtered to omit the \nblacklisted keys (or array of keys).

\n\n
var myJSONObject = \n{\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\n\n_.omit(myJSONObject, \"regex\");\n=> {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\"};\n
\n\n

For arrays, _.filter() and _.reject() can be used in a similar manner.

\n", + "upvotes": 172, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b0", + "creator": "Dan", + "createdAt": 1392227285000, + "text": "

Objects in JavaScript can be thought of as maps between keys and values. The delete operator is used to remove these keys, more commonly known as object properties, one at a time.

\n\n

\r\n
\r\n
var obj = {\r\n  myProperty: 1    \r\n}\r\nconsole.log(obj.hasOwnProperty('myProperty')) // true\r\ndelete obj.myProperty\r\nconsole.log(obj.hasOwnProperty('myProperty')) // false
\r\n
\r\n
\r\n

\n\n

The delete operator does not directly free memory, and it differs from simply assigning the value of null or undefined to a property, in that the property itself is removed from the object. Note that if the value of a deleted property was a reference type (an object), and another part of your program still holds a reference to that object, then that object will, of course, not be garbage collected until all references to it have disappeared.

\n\n

delete will only work on properties whose descriptor marks them as configurable.

\n", + "upvotes": 1275, + "upvoterUsernames": [], + "downvotes": 201, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b2", + "creator": "Willem", + "createdAt": 1410742088000, + "text": "

There are a lot of good answers here but I just want to chime in that when using delete to remove a property in JavaScript, it is often wise to first check if that property exists to prevent errors.

\n\n

E.g

\n\n
var obj = {\"property\":\"value\", \"property2\":\"value\"};\n\nif (obj && obj.hasOwnProperty(\"property2\")) {\n  delete obj.property2;\n} else {\n  //error handling\n}\n
\n\n

Due to the dynamic nature of JavaScript there are often cases where you simply don't know if the property exists or not. Checking if obj exists before the && also makes sure you don't throw an error due to calling the hasOwnProperty() function on an undefined object.

\n\n

Sorry if this didn't add to your specific use case but I believe this to be a good design to adapt when managing objects and their properties.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b3", + "creator": "talsibony", + "createdAt": 1417419197000, + "text": "

This post is very old and I find it very helpful so I decided to share the unset function I wrote in case someone else see this post and think why it's not so simple as it in PHP unset function.

\n

The reason for writing this new unset function, is to keep the index of all other variables in this hash_map. Look at the following example, and see how the index of "test2" did not change after removing a value from the hash_map.

\n

\r\n
\r\n
function unset(unsetKey, unsetArr, resort) {\n  var tempArr = unsetArr;\n  var unsetArr = {};\n  delete tempArr[unsetKey];\n  if (resort) {\n    j = -1;\n  }\n  for (i in tempArr) {\n    if (typeof(tempArr[i]) !== 'undefined') {\n      if (resort) {\n        j++;\n      } else {\n        j = i;\n      }\n      unsetArr[j] = tempArr[i];\n    }\n  }\n  return unsetArr;\n}\n\nvar unsetArr = ['test', 'deletedString', 'test2'];\n\nconsole.log(unset('1', unsetArr, true)); // output Object {0: \"test\", 1: \"test2\"}\nconsole.log(unset('1', unsetArr, false)); // output Object {0: \"test\", 2: \"test2\"}
\r\n
\r\n
\r\n

\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b5", + "creator": "emil", + "createdAt": 1453429780000, + "text": "

I personally use Underscore.js or Lodash for object and array manipulation:

\n\n
myObject = _.omit(myObject, 'regex');\n
\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b4", + "creator": "madox2", + "createdAt": 1452443799000, + "text": "

ECMAScript 2015 (or ES6) came with built-in Reflect object. It is possible to delete object property by calling Reflect.deleteProperty() function with target object and property key as parameters:

\n\n
Reflect.deleteProperty(myJSONObject, 'regex');\n
\n\n

which is equivalent to:

\n\n
delete myJSONObject['regex'];\n
\n\n

But if the property of the object is not configurable it cannot be deleted neither with deleteProperty function nor delete operator:

\n\n
let obj = Object.freeze({ prop: \"value\" });\nlet success = Reflect.deleteProperty(obj, \"prop\");\nconsole.log(success); // false\nconsole.log(obj.prop); // value\n
\n\n

Object.freeze() makes all properties of object not configurable (besides other things). deleteProperty function (as well as delete operator) returns false when tries to delete any of it's properties. If property is configurable it returns true, even if property does not exist.

\n\n

The difference between delete and deleteProperty is when using strict mode:

\n\n
\"use strict\";\n\nlet obj = Object.freeze({ prop: \"value\" });\nReflect.deleteProperty(obj, \"prop\"); // false\ndelete obj[\"prop\"];\n// TypeError: property \"prop\" is non-configurable and can't be deleted\n
\n", + "upvotes": 99, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b6", + "creator": "John Slegers", + "createdAt": 1456078187000, + "text": "

Suppose you have an object that looks like this:

\n\n
var Hogwarts = {\n    staff : [\n        'Argus Filch',\n        'Filius Flitwick',\n        'Gilderoy Lockhart',\n        'Minerva McGonagall',\n        'Poppy Pomfrey',\n        ...\n    ],\n    students : [\n        'Hannah Abbott',\n        'Katie Bell',\n        'Susan Bones',\n        'Terry Boot',\n        'Lavender Brown',\n        ...\n    ]\n};\n
\n\n

Deleting an object property

\n\n

If you want to use the entire staff array, the proper way to do this, would be to do this:

\n\n
delete Hogwarts.staff;\n
\n\n

Alternatively, you could also do this:

\n\n
delete Hogwarts['staff'];\n
\n\n

Similarly, removing the entire students array would be done by calling delete Hogwarts.students; or delete Hogwarts['students'];.

\n\n

Deleting an array index

\n\n

Now, if you want to remove a single staff member or student, the procedure is a bit different, because both properties are arrays themselves.

\n\n

If you know the index of your staff member, you could simply do this:

\n\n
Hogwarts.staff.splice(3, 1);\n
\n\n

If you do not know the index, you'll also have to do an index search:

\n\n
Hogwarts.staff.splice(Hogwarts.staff.indexOf('Minerva McGonnagall') - 1, 1);\n
\n\n
\n\n

Note

\n\n

While you technically can use delete for an array, using it would result in getting incorrect results when calling for example Hogwarts.staff.length later on. In other words, delete would remove the element, but it wouldn't update the value of length property. Using delete would also mess up your indexing.

\n\n

So, when deleting values from an object, always first consider whether you're dealing with object properties or whether you're dealing with array values, and choose the appropriate strategy based on that.

\n\n

If you want to experiment with this, you can use this Fiddle as a starting point.

\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b7", + "creator": "ayushgp", + "createdAt": 1466674738000, + "text": "

If you want to delete a property deeply nested in the object then you can use the following recursive function with path to the property as the second argument:

\n\n
var deepObjectRemove = function(obj, path_to_key){\n    if(path_to_key.length === 1){\n        delete obj[path_to_key[0]];\n        return true;\n    }else{\n        if(obj[path_to_key[0]])\n            return deepObjectRemove(obj[path_to_key[0]], path_to_key.slice(1));\n        else\n            return false;\n    }\n};\n
\n\n

Example:

\n\n
var a = {\n    level1:{\n        level2:{\n            level3: {\n                level4: \"yolo\"\n            }\n        }\n    }\n};\n\ndeepObjectRemove(a, [\"level1\", \"level2\", \"level3\"]);\nconsole.log(a);\n\n//Prints {level1: {level2: {}}}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b8", + "creator": "Mohammed Safeer", + "createdAt": 1467816663000, + "text": "

Try the following method. Assign the Object property value to undefined. Then stringify the object and parse.

\n\n

\r\n
\r\n
 var myObject = {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\r\n\r\nmyObject.regex = undefined;\r\nmyObject = JSON.parse(JSON.stringify(myObject));\r\n\r\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900b9", + "creator": "Koen.", + "createdAt": 1478628129000, + "text": "

Old question, modern answer. Using object destructuring, an ECMAScript 6 feature, it's as simple as:

\n\n
const { a, ...rest } = { a: 1, b: 2, c: 3 };\n
\n\n

Or with the questions sample:

\n\n
const myObject = {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\nconst { regex, ...newObject } = myObject;\nconsole.log(newObject);\n
\n\n

You can see it in action in the Babel try-out editor.

\n\n
\n\n

Edit:

\n\n

To reassign to the same variable, use a let:

\n\n
let myObject = {\"ircEvent\": \"PRIVMSG\", \"method\": \"newURI\", \"regex\": \"^http://.*\"};\n({ regex, ...myObject } = myObject);\nconsole.log(myObject);\n
\n", + "upvotes": 350, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a7082fcc3049e91276", + "creator": "Ceres", + "createdAt": 1631642570000, + "text": "How do I destructure if the property name varies, i.e if I have it in a variable?", + "upvotes": 1955, + "upvoterUsernames": [], + "downvotes": 1955, + "downvoterUsernames": [] + }, + { + "_id": "62f322a7082fcc3049e91278", + "creator": "Koen.", + "createdAt": 1631735316000, + "text": "See this answer below; stackoverflow.com/a/52301527", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f322a7082fcc3049e9127a", + "creator": "GreenAsJade", + "createdAt": 1641012731000, + "text": "Why is this preferable to delete()? "modern" isn't really a reason...", + "upvotes": 8772, + "upvoterUsernames": [], + "downvotes": 8772, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900ba", + "creator": "Amio.io", + "createdAt": 1480346092000, + "text": "

Using ramda#dissoc you will get a new object without the attribute regex:

\n\n
const newObject = R.dissoc('regex', myObject);\n// newObject !== myObject\n
\n\n

You can also use other functions to achieve the same effect - omit, pick, ...

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900bb", + "creator": "kind user", + "createdAt": 1490541595000, + "text": "

Another solution, using Array#reduce.

\n\n

\r\n
\r\n
var myObject = {\r\n  \"ircEvent\": \"PRIVMSG\",\r\n  \"method\": \"newURI\",\r\n  \"regex\": \"^http://.*\"\r\n};\r\n\r\nmyObject = Object.keys(myObject).reduce(function(obj, key) {\r\n  if (key != \"regex\") {           //key you want to remove\r\n    obj[key] = myObject[key];\r\n  }\r\n  return obj;\r\n}, {});\r\n\r\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n\n

However, it will mutate the original object. If you want to create a new object without the specified key, just assign the reduce function to a new variable, e.g.:

\n\n

(ES6)

\n\n

\r\n
\r\n
const myObject = {\r\n  ircEvent: 'PRIVMSG',\r\n  method: 'newURI',\r\n  regex: '^http://.*',\r\n};\r\n\r\nconst myNewObject = Object.keys(myObject).reduce((obj, key) => {\r\n  key !== 'regex' ? obj[key] = myObject[key] : null;\r\n  return obj;\r\n}, {});\r\n\r\nconsole.log(myNewObject);
\r\n
\r\n
\r\n

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900bc", + "creator": "Alireza", + "createdAt": 1491581761000, + "text": "

Using delete method is the best way to do that, as per MDN description, the delete operator removes a property from an object. So you can simply write:

\n
delete myObject.regex;\n// OR\ndelete myObject['regex'];\n
\n
\n

The delete operator removes a given property from an object. On\nsuccessful deletion, it will return true, else false will be returned.\nHowever, it is important to consider the following scenarios:

\n
\n
\n\n
\n
\n\n
\n
\n\n
\n
\n\n
\n
\n\n
\n

The following snippet gives another simple example:

\n

\r\n
\r\n
var Employee = {\n  age: 28,\n  name: 'Alireza',\n  designation: 'developer'\n}\n\nconsole.log(delete Employee.name);   // returns true\nconsole.log(delete Employee.age);    // returns true\n\n// When trying to delete a property that does \n// not exist, true is returned \nconsole.log(delete Employee.salary); // returns true
\r\n
\r\n
\r\n

\n

For more info about and seeing more examples visit the link below:

\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900bd", + "creator": "Chong Lip Phang", + "createdAt": 1501053570000, + "text": "

Dan's assertion that 'delete' is very slow and the benchmark he posted were doubted. So I carried out the test myself in Chrome 59. It does seem that 'delete' is about 30 times slower:

\n
var iterationsTotal = 10000000;  // 10 million\nvar o;\nvar t1 = Date.now(),t2;\nfor (let i=0; i<iterationsTotal; i++) {\n   o = {a:1,b:2,c:3,d:4,e:5};\n   delete o.a; delete o.b; delete o.c; delete o.d; delete o.e;\n}\nconsole.log ((t2=Date.now())-t1);  // 6135\nfor (let i=0; i<iterationsTotal; i++) {\n   o = {a:1,b:2,c:3,d:4,e:5};\n   o.a = o.b = o.c = o.d = o.e = undefined;\n}\nconsole.log (Date.now()-t2);  // 205\n
\n

Note that I purposely carried out more than one 'delete' operations in one loop cycle to minimize the effect caused by the other operations.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900bf", + "creator": "user8629798", + "createdAt": 1505810261000, + "text": "

Object.assign() & Object.keys() & Array.map()

\n\n

\r\n
\r\n
const obj = {\r\n    \"Filters\":[\r\n        {\r\n            \"FilterType\":\"between\",\r\n            \"Field\":\"BasicInformationRow.A0\",\r\n            \"MaxValue\":\"2017-10-01\",\r\n            \"MinValue\":\"2017-09-01\",\r\n            \"Value\":\"Filters value\"\r\n        }\r\n    ]\r\n};\r\n\r\nlet new_obj1 = Object.assign({}, obj.Filters[0]);\r\nlet new_obj2 = Object.assign({}, obj.Filters[0]);\r\n\r\n/*\r\n\r\n// old version\r\n\r\nlet shaped_obj1 = Object.keys(new_obj1).map(\r\n    (key, index) => {\r\n        switch (key) {\r\n            case \"MaxValue\":\r\n                delete new_obj1[\"MaxValue\"];\r\n                break;\r\n            case \"MinValue\":\r\n                delete new_obj1[\"MinValue\"];\r\n                break;\r\n        }\r\n        return new_obj1;\r\n    }\r\n)[0];\r\n\r\n\r\nlet shaped_obj2 = Object.keys(new_obj2).map(\r\n    (key, index) => {\r\n        if(key === \"Value\"){\r\n            delete new_obj2[\"Value\"];\r\n        }\r\n        return new_obj2;\r\n    }\r\n)[0];\r\n\r\n\r\n*/\r\n\r\n\r\n// new version!\r\n\r\nlet shaped_obj1 = Object.keys(new_obj1).forEach(\r\n    (key, index) => {\r\n        switch (key) {\r\n            case \"MaxValue\":\r\n                delete new_obj1[\"MaxValue\"];\r\n                break;\r\n            case \"MinValue\":\r\n                delete new_obj1[\"MinValue\"];\r\n                break;\r\n            default:\r\n                break;\r\n        }\r\n    }\r\n);\r\n\r\nlet shaped_obj2 = Object.keys(new_obj2).forEach(\r\n    (key, index) => {\r\n        if(key === \"Value\"){\r\n            delete new_obj2[\"Value\"];\r\n        }\r\n    }\r\n);
\r\n
\r\n
\r\n

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900be", + "creator": "johndavedecano", + "createdAt": 1505398447000, + "text": "

Using Lodash

\n
import omit from 'lodash/omit';\n\nconst prevObject = {test: false, test2: true};\n// Removes test2 key from previous object\nconst nextObject = omit(prevObject, 'test2');\n
\n

Using Ramda

\n
R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c0", + "creator": "BEJGAM SHIVA PRASAD", + "createdAt": 1506672190000, + "text": "

I have used Lodash "unset" to make it happen for a nested object also... only this needs to write small logic to get the path of the property key which is expected by the omit method.

\n
    \n
  1. Method which returns the property path as an array
  2. \n
\n

\r\n
\r\n
var a = {\"bool\":{\"must\":[{\"range\":{\"price_index.final_price\":{\"gt\":\"450\", \"lt\":\"500\"}}}, {\"bool\":{\"should\":[{\"term\":{\"color_value.keyword\":\"Black\"}}]}}]}};\n\nfunction getPathOfKey(object,key,currentPath, t){\n    var currentPath = currentPath || [];\n\n    for(var i in object){\n        if(i == key){\n            t = currentPath;\n        }\n        else if(typeof object[i] == \"object\"){\n            currentPath.push(i)\n            return getPathOfKey(object[i], key,currentPath)\n        }\n    }\n    t.push(key);\n    return t;\n}\ndocument.getElementById(\"output\").innerHTML =JSON.stringify(getPathOfKey(a,\"price_index.final_price\"))
\r\n
<div id=\"output\">\n\n</div>
\r\n
\r\n
\r\n

\n
    \n
  1. Then just using Lodash unset method remove property from object.
  2. \n
\n

\r\n
\r\n
var unset = require('lodash.unset');\nunset(a, getPathOfKey(a, \"price_index.final_price\"));
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c1", + "creator": "hygull", + "createdAt": 1527187371000, + "text": "

@johnstock, we can also use JavaScript's prototyping concept to add method to objects to delete any passed key available in calling object.

\n

Above answers are appreciated.

\n

\r\n
\r\n
var myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\"\n};\n\n// 1st and direct way \ndelete myObject.regex; // delete myObject[\"regex\"]\nconsole.log(myObject); // { ircEvent: 'PRIVMSG', method: 'newURI' }\n\n// 2 way -  by using the concept of JavaScript's prototyping concept\nObject.prototype.removeFromObjectByKey = function(key) {\n  // If key exists, remove it and return true\n  if (this[key] !== undefined) {\n    delete this[key]\n    return true;\n  }\n  // Else return false\n  return false;\n}\n\nvar isRemoved = myObject.removeFromObjectByKey('method')\nconsole.log(myObject) // { ircEvent: 'PRIVMSG' }\n\n// More examples\nvar obj = {\n  a: 45,\n  b: 56,\n  c: 67\n}\nconsole.log(obj) // { a: 45, b: 56, c: 67 }\n\n// Remove key 'a' from obj\nisRemoved = obj.removeFromObjectByKey('a')\nconsole.log(isRemoved); //true\nconsole.log(obj); // { b: 56, c: 67 }\n\n// Remove key 'd' from obj which doesn't exist\nvar isRemoved = obj.removeFromObjectByKey('d')\nconsole.log(isRemoved); // false\nconsole.log(obj); // { b: 56, c: 67 }
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c2", + "creator": "Lior Elrom", + "createdAt": 1536777595000, + "text": "

Spread Syntax (ES6)

\n

To complete Koen's answer, in case you want to remove a dynamic variable using the spread syntax, you can do it like so:

\n

\r\n
\r\n
const key = 'a';\n\nconst { [key]: foo, ...rest } = { a: 1, b: 2, c: 3 };\n\nconsole.log(foo);  // 1\nconsole.log(rest); // { b: 2, c: 3 }
\r\n
\r\n
\r\n

\n

* foo will be a new variable with the value of a (which is 1).

\n

Extended answer 😇

\n

There are a few common ways to remove a property from an object.
Each one has its own pros and cons (check this performance comparison):

\n

Delete Operator

\n

It is readable and short, however, it might not be the best choice if you are operating on a large number of objects as its performance is not optimized.

\n
delete obj[key];\n
\n

Reassignment

\n

It is more than two times faster than delete, however the property is not deleted and can be iterated.

\n
obj[key] = null;\nobj[key] = false;\nobj[key] = undefined;\n
\n

Spread Operator

\n

This ES6 operator allows us to return a brand new object, excluding any properties, without mutating the existing object. The downside is that it has the worse performance out of the above and is not suggested to be used when you need to remove many properties at a time.

\n
{ [key]: val, ...rest } = obj;\n
\n", + "upvotes": 255, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c5", + "creator": "B''H Bi'ezras -- Boruch Hashem", + "createdAt": 1600235801000, + "text": "

\r\n
\r\n
let myObject = {\n    \"ircEvent\": \"PRIVMSG\",\n    \"method\": \"newURI\",\n    \"regex\": \"^http://.*\"\n};\n\n\nobj = Object.fromEntries(\n    Object.entries(myObject).filter(function (m){\n        return m[0] != \"regex\"/*or whatever key to delete*/\n    }\n))\n\nconsole.log(obj)
\r\n
\r\n
\r\n

\n

You can also just treat the object like a2d array using Object.entries, and use splice to remove an element as you would in a normal array, or simply filter through the object, as one would an array, and assign the reconstructed object back to the original variable

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c3", + "creator": "YairTawil", + "createdAt": 1557261331000, + "text": "

To clone an object without a property:

\n

For example:

\n
let object = { a: 1, b: 2, c: 3 };\n
\n

And we need to delete a.

\n
    \n
  1. With an explicit prop key:

    \n
    const { a, ...rest } = object;\nobject = rest;\n
    \n
  2. \n
  3. With a variable prop key:

    \n
    const propKey = 'a';\nconst { [propKey]: propValue, ...rest } = object;\nobject = rest;\n
    \n
  4. \n
  5. A cool arrow function 😎:

    \n
    const removeProperty = (propKey, { [propKey]: propValue, ...rest }) => rest;\n\nobject = removeProperty('a', object);\n
    \n
  6. \n
  7. For multiple properties

    \n
    const removeProperties = (object, ...keys) => (keys.length ? removeProperties(removeProperty(keys.pop(), object), ...keys) : object);\n
    \n
  8. \n
\n

Usage

\n
object = removeProperties(object, 'a', 'b') // result => { c: 3 }\n
\n

Or

\n
const propsToRemove = ['a', 'b']\nobject = removeProperties(object, ...propsToRemove) // result => { c: 3 }\n
\n", + "upvotes": 204, + "upvoterUsernames": [], + "downvotes": 92, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c4", + "creator": "ANIK ISLAM SHOJIB", + "createdAt": 1568804359000, + "text": "

You can use a filter like below

\n

\r\n
\r\n
var myObject = {\n    \"ircEvent\": \"PRIVMSG\",\n    \"method\": \"newURI\",\n    \"regex\": \"^http://.*\"\n};\n\n// Way 1\n\nlet filter1 = {}\n  Object.keys({...myObject}).filter(d => {\n  if(d !== 'regex'){\n    filter1[d] = myObject[d];\n  }\n})\n\nconsole.log(filter1)\n\n// Way 2\n\nlet filter2 = Object.fromEntries(Object.entries({...myObject}).filter(d =>\nd[0] !== 'regex'\n))\n\nconsole.log(filter2)
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a8082fcc3049e91284", + "creator": "B''H Bi'ezras -- Boruch Hashem", + "createdAt": 1600237313000, + "text": "You could instead do let filter = Object.fromEntries(Object.entries(myObject).filter(d => d !== 'regex' )) ", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900c6", + "creator": "akhtarvahid", + "createdAt": 1605587820000, + "text": "

If you don't want to modify the original object.

\n

Remove a property without mutating the object

\n

If mutability is a concern, you can create a completely new object by copying all the properties from the old, except the one you want to remove.

\n

\r\n
\r\n
let myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\"\n};\n\nlet prop = 'regex';\nconst updatedObject = Object.keys(myObject).reduce((object, key) => {\n  if (key !== prop) {\n    object[key] = myObject[key]\n  }\n  return object\n}, {})\n\nconsole.log(updatedObject);
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c9", + "creator": "Ran Turner", + "createdAt": 1639847112000, + "text": "

There are a couple of ways to remove properties from an object:

\n
    \n
  1. Remove using a dot property accessor
  2. \n
\n

\r\n
\r\n
const myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\",\n};\n\ndelete myObject.regex;\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n
    \n
  1. Remove using square brackets property accessor
  2. \n
\n

\r\n
\r\n
const myObject = {\n      \"ircEvent\": \"PRIVMSG\",\n      \"method\": \"newURI\",\n      \"regex\": \"^http://.*\",\n    };\n\ndelete myObject['regex'];\nconsole.log(myObject);\n// or\nconst name = 'ircEvent';\ndelete myObject[name];\nconsole.log(myObject);
\r\n
\r\n
\r\n

\n
    \n
  1. Alternative option but in an immutable manner without altering the original object, is using object destructuring and rest syntax.
  2. \n
\n

\r\n
\r\n
 const myObject = {\n      \"ircEvent\": \"PRIVMSG\",\n      \"method\": \"newURI\",\n      \"regex\": \"^http://.*\",\n    };\n\nconst { regex, ...myObjectRest} = myObject;\nconsole.log(myObjectRest); 
\r\n
\r\n
\r\n

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c7", + "creator": "John Doe", + "createdAt": 1610985255000, + "text": "

Here's an ES6 way to remove the entry easily:

\n

\r\n
\r\n
let myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\"\n};\n\nconst removeItem = 'regex';\n\nconst { [removeItem]: remove, ...rest } = myObject;\n\nconsole.log(remove); // \"^http://.*\"\nconsole.log(rest); // Object { ircEvent: \"PRIVMSG\", method: \"newURI\" }
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900c8", + "creator": "Rashid Iqbal", + "createdAt": 1614006908000, + "text": "

Two ways to delete an object

\n
    \n
  1. using for ... in

    \n
     function deleteUser(key) {\n\n     const newUsers = {};\n     for (const uid in users) {\n         if (uid !== key) {\n             newUsers[uid] = users[uid];\n         }\n\n     return newUsers\n }\n
    \n
  2. \n
\n

or

\n
delete users[key]\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a8082fcc3049e91288", + "creator": "mickmackusa", + "createdAt": 1623049663000, + "text": "So you are recommending that to remove one property, the whole object should be copied into a new object without the targeted property?!?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900ca", + "creator": "Sahil Thummar", + "createdAt": 1646290356000, + "text": "

We can remove using

\n
    \n
  1. using delete object.property
  2. \n
  3. using delete object['property']
  4. \n
  5. using rest, remove multiple property
  6. \n
\n

\r\n
\r\n
let myObject = {\n  \"ircEvent\": \"PRIVMSG\",\n  \"method\": \"newURI\",\n  \"regex\": \"^http://.*\",\n  \"regex1\": \"^http://.*\",\n  \"regex2\": \"^http://.*\",\n  \"regex3\": \"^http://.*\",\n  \"regex4\": \"^http://.*\"\n};\n\ndelete myObject.regex; // using delete object.property\n\n// Or \n\ndelete myObject['regex1']; // using delete object['property']\n\nconst { regex2, regex3, regex4, ...newMyObject } = myObject;\n\nconsole.log(newMyObject);
\r\n
\r\n
\r\n

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900cb", + "creator": "Uchiha Madara", + "createdAt": 1651274224000, + "text": "

You can delete property from object using\nDelete property[key]

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900cc", + "creator": "rohithpoya", + "createdAt": 1656135249000, + "text": "

In JavaScript, there are 2 common ways to remove properties from an object.

\n

The first mutable approach is to use the delete object.property operator.

\n

The second approach, which is immutable since it doesn't modify the original object, is to invoke the object destructuring and spread syntax:\nconst {property, ...rest} = object

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2851838, + "uvac": 2851869 + } + }, + { + "_id": "62f321bb082fcc3049e8fec0", + "title": "How do I get a timestamp in JavaScript?", + "title-lowercase": "how do i get a timestamp in javascript?", + "creator": "pupeno", + "createdAt": 1224581373000, + "status": "open", + "text": "

I want a single number that represents the current date and time, like a Unix timestamp.

\n", + "upvotes": 6713, + "upvoterUsernames": [], + "downvotes": 2148, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3695262, + "answers": 40, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e903a1", + "creator": "daveb", + "createdAt": 1224581552000, + "text": "

Timestamp in milliseconds

\n

To get the number of milliseconds since Unix epoch, call Date.now:

\n
Date.now()\n
\n

Alternatively, use the unary operator + to call Date.prototype.valueOf:

\n
+ new Date()\n
\n

Alternatively, call valueOf directly:

\n
new Date().valueOf()\n
\n

To support IE8 and earlier (see compatibility table), create a shim for Date.now:

\n
if (!Date.now) {\n    Date.now = function() { return new Date().getTime(); }\n}\n
\n

Alternatively, call getTime directly:

\n
new Date().getTime()\n
\n
\n

Timestamp in seconds

\n

To get the number of seconds since Unix epoch, i.e. Unix timestamp:

\n
Math.floor(Date.now() / 1000)\n
\n

Alternatively, using bitwise-or to floor is slightly faster, but also less readable and may break in the future (see explanations 1, 2):

\n
Date.now() / 1000 | 0\n
\n
\n

Timestamp in milliseconds (higher resolution)

\n

Use performance.now:

\n

\r\n
\r\n
var isPerformanceSupported = (\n    window.performance &&\n    window.performance.now &&\n    window.performance.timing &&\n    window.performance.timing.navigationStart\n);\n\nvar timeStampInMs = (\n    isPerformanceSupported ?\n    window.performance.now() +\n    window.performance.timing.navigationStart :\n    Date.now()\n);\n\nconsole.log(timeStampInMs, Date.now());
\r\n
\r\n
\r\n

\n", + "upvotes": 6514, + "upvoterUsernames": [], + "downvotes": 848, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a2", + "creator": "Staale", + "createdAt": 1224583536000, + "text": "
var time = Date.now || function() {\n  return +new Date;\n};\n\ntime();\n
\n", + "upvotes": 306, + "upvoterUsernames": [], + "downvotes": 146, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a4", + "creator": "Tom Viner", + "createdAt": 1241110432000, + "text": "

\r\n
\r\n
console.log(new Date().valueOf()); // returns the number of milliseconds since the epoch
\r\n
\r\n
\r\n

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a3", + "creator": "aemkei", + "createdAt": 1224594022000, + "text": "
var timestamp = Number(new Date()); // current time as number\n
\n", + "upvotes": 131, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a5", + "creator": "Kiragaz", + "createdAt": 1257939537000, + "text": "
time = Math.round(((new Date()).getTime()-Date.UTC(1970,0,1))/1000);\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241c082fcc3049e916c8", + "creator": "Skone", + "createdAt": 1316620829000, + "text": "@Kip Good point. We're both getting at the same thing though, the additional arithmetic here is unnecessary.", + "upvotes": 344, + "upvoterUsernames": [], + "downvotes": 344, + "downvoterUsernames": [] + }, + { + "_id": "62f3241c082fcc3049e916ca", + "creator": "Peter", + "createdAt": 1365859331000, + "text": "If the Epoch changes, the definition of Unix Timestamp changes. That makes the above code backwards compatible, but broken :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903a6", + "creator": "xer0x", + "createdAt": 1297989224000, + "text": "

I like this, because it is small:

\n\n
+new Date\n
\n\n

I also like this, because it is just as short and is compatible with modern browsers, and over 500 people voted that it is better:

\n\n
Date.now()\n
\n", + "upvotes": 721, + "upvoterUsernames": [], + "downvotes": 105, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241c082fcc3049e916cd", + "creator": "kirb", + "createdAt": 1380714618000, + "text": "@Niklaus That's because you're concatenating it to another string. In that case, new Date().toString() is called.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3241c082fcc3049e916cf", + "creator": "1j01", + "createdAt": 1426621339000, + "text": "@Niklaus in that case you would need another plus to act as the unary operator: 'NEW-' + +new Date", + "upvotes": 821, + "upvoterUsernames": [], + "downvotes": 821, + "downvoterUsernames": [] + }, + { + "_id": "62f3241c082fcc3049e916d0", + "creator": "Marcus Johansson", + "createdAt": 1472979394000, + "text": "The +new Date does not seem to work in Chrome 52, if the desired effect is to generate an integer. I am confident it worked on older Chromes.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903a7", + "creator": "Daithí", + "createdAt": 1305152825000, + "text": "

JavaScript works with the number of milliseconds since the epoch whereas most other languages work with the seconds. You could work with milliseconds but as soon as you pass a value to say PHP, the PHP native functions will probably fail. So to be sure I always use the seconds, not milliseconds.

\n\n

This will give you a Unix timestamp (in seconds):

\n\n
var unix = Math.round(+new Date()/1000);\n
\n\n

This will give you the milliseconds since the epoch (not Unix timestamp):

\n\n
var milliseconds = new Date().getTime();\n
\n", + "upvotes": 437, + "upvoterUsernames": [], + "downvotes": 127, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a8", + "creator": "Salman A", + "createdAt": 1336035736000, + "text": "

The Date.getTime() method can be used with a little tweak:

\n\n
\n

The value returned by the getTime method is the number of milliseconds\n since 1 January 1970 00:00:00 UTC.

\n
\n\n

Divide the result by 1000 to get the Unix timestamp, floor if necessary:

\n\n
(new Date).getTime() / 1000\n
\n\n
\n\n

The Date.valueOf() method is functionally equivalent to Date.getTime(), which makes it possible to use arithmetic operators on date object to achieve identical results. In my opinion, this approach affects readability.

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903aa", + "creator": "VisioN", + "createdAt": 1363357162000, + "text": "

jQuery provides its own method to get the timestamp:

\n\n
var timestamp = $.now();\n
\n\n

(besides it just implements (new Date).getTime() expression)

\n\n

REF: http://api.jquery.com/jQuery.now/

\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903a9", + "creator": "live-love", + "createdAt": 1348254769000, + "text": "

Just to add up, here's a function to return a timestamp string in Javascript. \nExample: 15:06:38 PM

\n\n
function displayTime() {\n    var str = \"\";\n\n    var currentTime = new Date()\n    var hours = currentTime.getHours()\n    var minutes = currentTime.getMinutes()\n    var seconds = currentTime.getSeconds()\n\n    if (minutes < 10) {\n        minutes = \"0\" + minutes\n    }\n    if (seconds < 10) {\n        seconds = \"0\" + seconds\n    }\n    str += hours + \":\" + minutes + \":\" + seconds + \" \";\n    if(hours > 11){\n        str += \"PM\"\n    } else {\n        str += \"AM\"\n    }\n    return str;\n}\n
\n", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ac", + "creator": "deepakssn", + "createdAt": 1369128140000, + "text": "

Here is a simple function to generate timestamp in the format: mm/dd/yy hh:mi:ss

\n\n
function getTimeStamp() {\n    var now = new Date();\n    return ((now.getMonth() + 1) + '/' +\n            (now.getDate()) + '/' +\n             now.getFullYear() + \" \" +\n             now.getHours() + ':' +\n             ((now.getMinutes() < 10)\n                 ? (\"0\" + now.getMinutes())\n                 : (now.getMinutes())) + ':' +\n             ((now.getSeconds() < 10)\n                 ? (\"0\" + now.getSeconds())\n                 : (now.getSeconds())));\n}\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241d082fcc3049e916d5", + "creator": "Inaimathi", + "createdAt": 1377314237000, + "text": "@b123400 - Here's the Lisp version: (new (chain (-date) (to-i-s-o-string))).", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903ab", + "creator": "SBotirov", + "createdAt": 1368082420000, + "text": "

Any browsers not supported Date.now, you can use this for get current date time:

\n\n
currentTime = Date.now() || +new Date()\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ad", + "creator": "Anoop P S", + "createdAt": 1372661279000, + "text": "

This one has a solution : which converts unixtime stamp to tim in js try this

\n\n
var a = new Date(UNIX_timestamp*1000);\nvar hour = a.getUTCHours();\nvar min = a.getUTCMinutes();\nvar sec = a.getUTCSeconds();\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ae", + "creator": "Vicky Gonsalves", + "createdAt": 1382759482000, + "text": "

more simpler way:

\n\n
var timeStamp=event.timestamp || new Date().getTime();\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241d082fcc3049e916da", + "creator": "alexventuraio", + "createdAt": 1474584874000, + "text": "Do know where event comes from. You need to give a better explanation of the way you resolve it instead of you writing an answer. Please!", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903af", + "creator": "Belldandu", + "createdAt": 1396253928000, + "text": "

One I haven't seen yet

\n\n
Math.floor(Date.now() / 1000); // current time in seconds\n
\n\n

Another one I haven't seen yet is

\n\n
var _ = require('lodash'); // from here https://lodash.com/docs#now\n_.now();\n
\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b0", + "creator": "DevC", + "createdAt": 1398322000000, + "text": "

sometime I need it in objects for xmlhttp calls, so I do like this.

\n\n
timestamp : parseInt(new Date().getTime()/1000, 10)\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3241d082fcc3049e916dd", + "creator": "EaterOfCode", + "createdAt": 1411739597000, + "text": "Even shorter: new Date().getTime()/1000|0 but its slow and dirty", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903b1", + "creator": "Saucier", + "createdAt": 1400792420000, + "text": "

Here is another solution to generate a timestamp in JavaScript - including a padding method for single numbers - using day, month, year, hour, minute and seconds in its result (working example at jsfiddle):

\n\n
var pad = function(int) { return int < 10 ? 0 + int : int; };\nvar timestamp = new Date();\n\n    timestamp.day = [\n        pad(timestamp.getDate()),\n        pad(timestamp.getMonth() + 1), // getMonth() returns 0 to 11.\n        timestamp.getFullYear()\n    ];\n\n    timestamp.time = [\n        pad(timestamp.getHours()),\n        pad(timestamp.getMinutes()),\n        pad(timestamp.getSeconds())\n    ];\n\ntimestamp.now = parseInt(timestamp.day.join(\"\") + timestamp.time.join(\"\"));\nalert(timestamp.now);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b2", + "creator": "Rimian", + "createdAt": 1425602016000, + "text": "

Moment.js can abstract away a lot of the pain in dealing with Javascript Dates.

\n\n

See: http://momentjs.com/docs/#/displaying/unix-timestamp/

\n\n
moment().unix();\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b4", + "creator": "Eugene", + "createdAt": 1427399232000, + "text": "

var my_timestamp = ~~(Date.now()/1000);

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b3", + "creator": "georgez", + "createdAt": 1426067568000, + "text": "

I learned a really cool way of converting a given Date object to a Unix timestamp from the source code of JQuery Cookie the other day.

\n\n

Here's an example:

\n\n
var date = new Date();\nvar timestamp = +date;\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b5", + "creator": "user257319", + "createdAt": 1427456600000, + "text": "

The advised, proper way is Number(new Date()), \nin terms of code- readability,

\n\n

Also, UglifyJS and Google-Closure-Compiler will lower the complexity of the parsed code-logic-tree (relevant if you are using one of them to obscure/minify your code).

\n\n

for Unix timestamp, which has a lower time resolution, just divide current number with 1000, keeping the whole.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b6", + "creator": "Muhammad Reda", + "createdAt": 1427704823000, + "text": "

For lodash and underscore users, use _.now.

\n\n
var timestamp = _.now(); // in milliseconds\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b7", + "creator": "jameslouiz", + "createdAt": 1428913741000, + "text": "
var d = new Date();\nconsole.log(d.valueOf()); \n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b8", + "creator": "Kevin Leary", + "createdAt": 1432906852000, + "text": "

If want a basic way to generate a timestamp in Node.js this works well.

\n\n
var time = process.hrtime();\nvar timestamp = Math.round( time[ 0 ] * 1e3 + time[ 1 ] / 1e6 );\n
\n\n

Our team is using this to bust cache in a localhost environment. The output is /dist/css/global.css?v=245521377 where 245521377 is the timestamp generated by hrtime().

\n\n

Hopefully this helps, the methods above can work as well but I found this to be the simplest approach for our needs in Node.js.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903b9", + "creator": "iter", + "createdAt": 1436140915000, + "text": "

For a timestamp with microsecond resolution, there's performance.now:

\n\n
function time() { \n  return performance.now() + performance.timing.navigationStart;\n}\n
\n\n

This could for example yield 1436140826653.139, while Date.now only gives 1436140826653.

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ba", + "creator": "FullStack", + "createdAt": 1436862596000, + "text": "

I highly recommend using moment.js. To get the number of milliseconds since UNIX epoch, do

\n\n
moment().valueOf()\n
\n\n

To get the number of seconds since UNIX epoch, do

\n\n
moment().unix()\n
\n\n

You can also convert times like so:

\n\n
moment('2015-07-12 14:59:23', 'YYYY-MM-DD HH:mm:ss').valueOf()\n
\n\n

I do that all the time. No pun intended.

\n\n

To use moment.js in the browser:

\n\n
<script src=\"moment.js\"></script>\n<script>\n    moment().valueOf();\n</script>\n
\n\n

For more details, including other ways of installing and using MomentJS, see their docs

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903bb", + "creator": "blueberry0xff", + "createdAt": 1443534941000, + "text": "

\r\n
\r\n
// The Current Unix Timestamp\r\n// 1443534720 seconds since Jan 01 1970. (UTC)\r\n\r\n// seconds\r\nconsole.log(Math.floor(new Date().valueOf() / 1000)); // 1443534720\r\nconsole.log(Math.floor(Date.now() / 1000)); // 1443534720\r\nconsole.log(Math.floor(new Date().getTime() / 1000)); // 1443534720\r\n\r\n// milliseconds\r\nconsole.log(Math.floor(new Date().valueOf())); // 1443534720087\r\nconsole.log(Math.floor(Date.now())); // 1443534720087\r\nconsole.log(Math.floor(new Date().getTime())); // 1443534720087\r\n\r\n// jQuery\r\n// seconds\r\nconsole.log(Math.floor($.now() / 1000)); // 1443534720\r\n// milliseconds\r\nconsole.log($.now()); // 1443534720087
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903bc", + "creator": "Ronnie Royston", + "createdAt": 1444356205000, + "text": "

This seems to work.

\n\n
console.log(clock.now);\n// returns 1444356078076\n\nconsole.log(clock.format(clock.now));\n//returns 10/8/2015 21:02:16\n\nconsole.log(clock.format(clock.now + clock.add(10, 'minutes'))); \n//returns 10/8/2015 21:08:18\n\nvar clock = {\n    now:Date.now(),\n    add:function (qty, units) {\n            switch(units.toLowerCase()) {\n                case 'weeks'   :  val = qty * 1000 * 60 * 60 * 24 * 7;  break;\n                case 'days'    :  val = qty * 1000 * 60 * 60 * 24;  break;\n                case 'hours'   :  val = qty * 1000 * 60 * 60;  break;\n                case 'minutes' :  val = qty * 1000 * 60;  break;\n                case 'seconds' :  val = qty * 1000;  break;\n                default       :  val = undefined;  break;\n                }\n            return val;\n            },\n    format:function (timestamp){\n            var date = new Date(timestamp);\n            var year = date.getFullYear();\n            var month = date.getMonth() + 1;\n            var day = date.getDate();\n            var hours = date.getHours();\n            var minutes = \"0\" + date.getMinutes();\n            var seconds = \"0\" + date.getSeconds();\n            // Will display time in xx/xx/xxxx 00:00:00 format\n            return formattedTime = month + '/' + \n                                day + '/' + \n                                year + ' ' + \n                                hours + ':' + \n                                minutes.substr(-2) + \n                                ':' + seconds.substr(-2);\n            }\n};\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903bd", + "creator": "Valentin Podkamennyi", + "createdAt": 1444840890000, + "text": "

The code Math.floor(new Date().getTime() / 1000) can be shortened to new Date / 1E3 | 0.

\n\n

Consider to skip direct getTime() invocation and use | 0 as a replacement for Math.floor() function.\nIt's also good to remember 1E3 is a shorter equivalent for 1000 (uppercase E is preferred than lowercase to indicate 1E3 as a constant).

\n\n

As a result you get the following:

\n\n

\r\n
\r\n
var ts = new Date / 1E3 | 0;\r\n\r\nconsole.log(ts);
\r\n
\r\n
\r\n

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903be", + "creator": "Joaquinglezsantos", + "createdAt": 1454080116000, + "text": "

In addition to the other options, if you want a dateformat ISO, you can get it directly

\n

\r\n
\r\n
console.log(new Date().toISOString());
\r\n
\r\n
\r\n

\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c0", + "creator": "Alireza", + "createdAt": 1495255761000, + "text": "

Date, a native object in JavaScript is the way we get all data about time.

\n\n

Just be careful in JavaScript the timestamp depends on the client computer set, so it's not 100% accurate timestamp. To get the best result, you need to get the timestamp from the server-side.

\n\n

Anyway, my preferred way is using vanilla. This is a common way of doing it in JavaScript:

\n\n
Date.now(); //return 1495255666921\n
\n\n

In MDN it's mentioned as below:

\n\n
\n

The Date.now() method returns the number of milliseconds elapsed since\n 1 January 1970 00:00:00 UTC.
\n Because now() is a static method of Date, you always use it as Date.now().

\n
\n\n

If you using a version below ES5, Date.now(); not works and you need to use:

\n\n
new Date().getTime();\n
\n", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903bf", + "creator": "Jitendra Pawar", + "createdAt": 1458107122000, + "text": "

You can only use

\n\n

\r\n
\r\n
    var timestamp = new Date().getTime();\r\n    console.log(timestamp);
\r\n
\r\n
\r\n

\n\n

to get the current timestamp. No need to do anything extra.

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c1", + "creator": "Olemak", + "createdAt": 1513246195000, + "text": "

As of writing this, the top answer is 9 years old, and a lot has changed since then - not least, we have near universal support for a non-hacky solution:

\n\n
Date.now()\n
\n\n

If you want to be absolutely certain that this won't break in some ancient (pre ie9) browser, you can put it behind a check, like so:

\n\n
const currentTimestamp = (!Date.now ? +new Date() : Date.now());\n
\n\n

This will return the milliseconds since epoch time, of course, not seconds.

\n\n

MDN Documentation on Date.now

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c2", + "creator": "unknown123", + "createdAt": 1522214564000, + "text": "
function getTimeStamp() {\n    var now = new Date();\n    return ((now.getMonth() + 1) + '/' +\n            (now.getDate()) + '/' +\n             now.getFullYear() + \" \" +\n             now.getHours() + ':' +\n             ((now.getMinutes() < 10)\n                 ? (\"0\" + now.getMinutes())\n                 : (now.getMinutes())) + ':' +\n             ((now.getSeconds() < 10)\n                 ? (\"0\" + now.getSeconds())\n                 : (now.getSeconds())));\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c3", + "creator": "Kamil Kiełczewski", + "createdAt": 1530117193000, + "text": "

Performance

\n

Today - 2020.04.23 I perform tests for chosen solutions. I tested on MacOs High Sierra 10.13.6 on Chrome 81.0, Safari 13.1, Firefox 75.0

\n

Conclusions

\n\n

\"enter

\n

Details

\n

Results for chrome

\n

\"enter

\n

You can perform test on your machine HERE

\n

Code used in tests is presented in below snippet

\n

\r\n
\r\n
function A() {\n  return new Date().getTime();\n}\n\nfunction B() {\n  return new Date().valueOf();\n}\n\nfunction C() {\n  return +new Date();\n}\n\nfunction D() {\n  return new Date()*1;\n}\n\nfunction E() {\n  return Date.now();\n}\n\nfunction F() {\n  return Number(new Date());\n}\n\nfunction G() {\n  // this solution returns time counted from loading the page.\n  // (and on Chrome it gives better precission)\n  return performance.now(); \n}\n\n\n\n// TEST\n\nlog = (n,f) => console.log(`${n} : ${f()}`);\n\nlog('A',A);\nlog('B',B);\nlog('C',C);\nlog('D',D);\nlog('E',E);\nlog('F',F);\nlog('G',G);
\r\n
This snippet only presents code used in external benchmark
\r\n
\r\n
\r\n

\n", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c4", + "creator": "cenkarioz", + "createdAt": 1558212180000, + "text": "

If it is for logging purposes, you can use ISOString

\n\n

new Date().toISOString()

\n\n
\n

\"2019-05-18T20:02:36.694Z\"

\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c6", + "creator": "Ganesh", + "createdAt": 1583155836000, + "text": "
\n

To get time, month, day, year separately this will work

\n
\n\n
var currentTime = new Date();\nvar month = currentTime.getMonth() + 1;\nvar day = currentTime.getDate();\nvar year = currentTime.getFullYear();\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c5", + "creator": "Ashish", + "createdAt": 1574742785000, + "text": "

Get TimeStamp In JavaScript

\n
\n

In JavaScript, a timestamp is the number of milliseconds that have passed since January 1, 1970.

\n

If you don't intend to support < IE8, you can use

\n
\n
new Date().getTime(); + new Date(); and Date.now();\n
\n

to directly get the timestamp without having to create a new Date object.

\n
\n

To return the required timestamp

\n
\n
new Date("11/01/2018").getTime()\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c7", + "creator": "Flash Noob", + "createdAt": 1611330889000, + "text": "

there are many ways to do it.

\n
 Date.now() \n new Date().getTime() \n new Date().valueOf()\n
\n
\n

To get the timestamp in seconds, convert it using:

\n
\n
Math.floor(Date.now() / 1000)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903c8", + "creator": "dazzafact", + "createdAt": 1638909342000, + "text": "

\r\n
\r\n
//if you need 10 digits\n   alert('timestamp '+ts());\nfunction ts() {\n    return parseInt(Date.now()/1000);\n\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3701975, + "uvac": 3702015 + } + }, + { + "_id": "62f321bb082fcc3049e8febf", + "title": "How do I check if an array includes a value in JavaScript?", + "title-lowercase": "how do i check if an array includes a value in javascript?", + "creator": "brad", + "createdAt": 1224972880000, + "status": "open", + "text": "

What is the most concise and efficient way to find out if a JavaScript array contains a value?

\n

This is the only way I know to do it:

\n
function contains(a, obj) {\n    for (var i = 0; i < a.length; i++) {\n        if (a[i] === obj) {\n            return true;\n        }\n    }\n    return false;\n}\n
\n

Is there a better and more concise way to accomplish this?

\n

This is very closely related to Stack Overflow question Best way to find an item in a JavaScript Array? which addresses finding objects in an array using indexOf.

\n", + "upvotes": 6262, + "upvoterUsernames": [], + "downvotes": 1591, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3178992, + "answers": 55, + "answerItems": [ + { + "_id": "62f321bf082fcc3049e9032e", + "creator": "Andru Luvisi", + "createdAt": 1224974697000, + "text": "

If you are using JavaScript 1.6 or later (Firefox 1.5 or later) you can use Array.indexOf. Otherwise, I think you are going to end up with something similar to your original code.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90330", + "creator": "Damir Zekić", + "createdAt": 1224976222000, + "text": "

Update from 2019: This answer is from 2008 (11 years old!) and is not relevant for modern JS usage. The promised performance improvement was based on a benchmark done in browsers of that time. It might not be relevant to modern JS execution contexts. If you need an easy solution, look for other answers. If you need the best performance, benchmark for yourself in the relevant execution environments.

\n

As others have said, the iteration through the array is probably the best way, but it has been proven that a decreasing while loop is the fastest way to iterate in JavaScript. So you may want to rewrite your code as follows:

\n
function contains(a, obj) {\n    var i = a.length;\n    while (i--) {\n       if (a[i] === obj) {\n           return true;\n       }\n    }\n    return false;\n}\n
\n

Of course, you may as well extend Array prototype:

\n
Array.prototype.contains = function(obj) {\n    var i = this.length;\n    while (i--) {\n        if (this[i] === obj) {\n            return true;\n        }\n    }\n    return false;\n}\n
\n

And now you can simply use the following:

\n
alert([1, 2, 3].contains(2)); // => true\nalert([1, 2, 3].contains('2')); // => false\n
\n", + "upvotes": 956, + "upvoterUsernames": [], + "downvotes": 472, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d4082fcc3049e915fe", + "creator": "orip", + "createdAt": 1321776577000, + "text": ""Proven" is a strong word. JS engines constantly improve, and execution time measured 3 years ago is terribly outdated.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9032f", + "creator": "cic", + "createdAt": 1224974993000, + "text": "

indexOf maybe, but it's a \"JavaScript extension to the ECMA-262 standard; as such it may not be present in other implementations of the standard.\"

\n\n

Example:

\n\n
[1, 2, 3].indexOf(1) => 0\n[\"foo\", \"bar\", \"baz\"].indexOf(\"bar\") => 1\n[1, 2, 3].indexOf(4) => -1\n
\n\n

AFAICS Microsoft does not offer some kind of alternative to this, but you can add similar functionality to arrays in Internet Explorer (and other browsers that don't support indexOf) if you want to, as a quick Google search reveals (for example, this one).

\n", + "upvotes": 370, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90332", + "creator": "Mason Houtz", + "createdAt": 1251391559000, + "text": "

Extending the JavaScript Array object is a really bad idea because you introduce new properties (your custom methods) into for-in loops which can break existing scripts. A few years ago the authors of the Prototype library had to re-engineer their library implementation to remove just this kind of thing.

\n\n

If you don't need to worry about compatibility with other JavaScript running on your page, go for it, otherwise, I'd recommend the more awkward, but safer free-standing function solution.

\n", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90331", + "creator": "Már Örlygsson", + "createdAt": 1225067937000, + "text": "

Here's a JavaScript 1.6 compatible implementation of Array.indexOf:

\n\n
if (!Array.indexOf) {\n    Array.indexOf = [].indexOf ?\n        function(arr, obj, from) {\n            return arr.indexOf(obj, from);\n        } :\n        function(arr, obj, from) { // (for IE6)\n            var l = arr.length,\n                i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;\n            i = i < 0 ? 0 : i;\n            for (; i < l; i++) {\n                if (i in arr && arr[i] === obj) {\n                    return i;\n                }\n            }\n            return -1;\n        };\n}\n
\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d5082fcc3049e91603", + "creator": "Már Örlygsson", + "createdAt": 1299850349000, + "text": "@alex yes [].indexOf === Array.prototype.indexOf (try it out in FireBug), but conversely [].indexOf !== Array.indexOf.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90333", + "creator": "Ken", + "createdAt": 1251393053000, + "text": "

Here's how Prototype does it:

\n\n
/**\n *  Array#indexOf(item[, offset = 0]) -> Number\n *  - item (?): A value that may or may not be in the array.\n *  - offset (Number): The number of initial items to skip before beginning the\n *      search.\n *\n *  Returns the position of the first occurrence of `item` within the array &mdash; or\n *  `-1` if `item` doesn't exist in the array.\n**/\nfunction indexOf(item, i) {\n  i || (i = 0);\n  var length = this.length;\n  if (i < 0) i = length + i;\n  for (; i < length; i++)\n    if (this[i] === item) return i;\n  return -1;\n}\n
\n\n

Also see here for how they hook it up.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90334", + "creator": "codeape", + "createdAt": 1253820924000, + "text": "

Modern browsers have Array#includes, which does exactly that and is widely supported by everyone except IE:

\n

\r\n
\r\n
console.log(['joe', 'jane', 'mary'].includes('jane')); //true
\r\n
\r\n
\r\n

\n

You can also use Array#indexOf, which is less direct, but doesn't require polyfills for outdated browsers.

\n

\r\n
\r\n
console.log(['joe', 'jane', 'mary'].indexOf('jane') >= 0); //true
\r\n
\r\n
\r\n

\n
\n

Many frameworks also offer similar methods:

\n\n

Notice that some frameworks implement this as a function, while others add the function to the array prototype.

\n", + "upvotes": 8977, + "upvoterUsernames": [], + "downvotes": 3841, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f323d5082fcc3049e91606", + "creator": "Ryan Florence", + "createdAt": 1276006221000, + "text": "MooTools also has Array.contains that returns a boolean, which sounds like the real question here.", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e91608", + "creator": "user102008", + "createdAt": 1284159270000, + "text": "prototype also has Array.include that returns a boolean", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e9160a", + "creator": "Sam Soffes", + "createdAt": 1286381844000, + "text": "If you are using a good browser, you can just use array.indexOf(object) != -1", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e9160c", + "creator": "plus-", + "createdAt": 1330535829000, + "text": "Also, dont use indexOf alone as a condition, because the first element will return 0 and will be evaluated as falsy", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e9160e", + "creator": "Om Shankar", + "createdAt": 1356893664000, + "text": "var inArray = function(a,b,c,d){for(c in b)d|=b[c]===a;return!!d }", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e91610", + "creator": "user254153", + "createdAt": 1650330445000, + "text": "includes function also checks for data type. [123,23].includes('23');//returns false", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e91612", + "creator": "Timo", + "createdAt": 1653996941000, + "text": "Includes Works also for (sub-)string checking.", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90335", + "creator": "MattMcKnight", + "createdAt": 1261583999000, + "text": "

Thinking out of the box for a second, if you are making this call many many times, it is vastly more efficient to use an associative array a Map to do lookups using a hash function.

\n\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90336", + "creator": "Dennis Allen", + "createdAt": 1280930717000, + "text": "

Just another option

\n\n
// usage: if ( ['a','b','c','d'].contains('b') ) { ... }\nArray.prototype.contains = function(value){\n    for (var key in this)\n        if (this[key] === value) return true;\n    return false;\n}\n
\n\n

Be careful because overloading javascript array objects with custom methods can disrupt the behavior of other javascripts, causing unexpected behavior.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d5082fcc3049e91615", + "creator": "Yi Jiang", + "createdAt": 1295541180000, + "text": "Please don't use a for in loop to iterate over an array - for in loops should be used strictly for objects only.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f323d5082fcc3049e91617", + "creator": "da coconut", + "createdAt": 1591611827000, + "text": "why is it so bad", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90338", + "creator": "Ekim", + "createdAt": 1305054880000, + "text": "

Literally:

\n

(using Firefox v3.6, with for-in caveats as previously noted\n(HOWEVER the use below might endorse for-in for this very purpose! That is, enumerating array elements that ACTUALLY exist via a property index (HOWEVER, in particular, the array length property is NOT enumerated in the for-in property list!).).)

\n

(Drag & drop the following complete URI's for immediate mode browser testing.)

\n

JavaScript:

\n
  function ObjInRA(ra){var has=false; for(i in ra){has=true; break;} return has;}\n\n  function check(ra){\n      return ['There is ',ObjInRA(ra)?'an':'NO',' object in [',ra,'].'].join('')\n  }\n  alert([\n            check([{}]), check([]), check([,2,3]),\n            check(['']), '\\t (a null string)', check([,,,])\n        ].join('\\n'));\n
\n

which displays:

\n
There is an object in [[object Object]].\nThere is NO object in [].\nThere is an object in [,2,3].\nThere is an object in [].\n     (a null string)\nThere is NO object in [,,].\n
\n

Wrinkles: if looking for a "specific" object consider:

\n

JavaScript: alert({}!={}); alert({}!=={});

\n

And thus:

\n

JavaScript:

\n
 obj = {prop:"value"}; \n ra1 = [obj]; \n ra2 = [{prop:"value"}];\n alert(ra1[0] == obj); \n alert(ra2[0] == obj);\n
\n

Often ra2 is considered to "contain" obj as the literal entity {prop:"value"}.

\n

A very coarse, rudimentary, naive (as in code needs qualification enhancing) solution:

\n

JavaScript:

\n
  obj={prop:"value"};   ra2=[{prop:"value"}];\n  alert(\n    ra2 . toSource() . indexOf( obj.toSource().match(/^.(.*).$/)[1] ) != -1 ?\n      'found' :\n      'missing' );\n
\n

See ref: Searching for objects in JavaScript arrays.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90337", + "creator": "Ztyx", + "createdAt": 1296928963000, + "text": "

If you are checking repeatedly for existence of an object in an array you should maybe look into

\n\n
    \n
  1. Keeping the array sorted at all times by doing insertion sort in your array (put new objects in on the right place)
  2. \n
  3. Make updating objects as remove+sorted insert operation and
  4. \n
  5. Use a binary search lookup in your contains(a, obj).
  6. \n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d5082fcc3049e9161b", + "creator": "joeytwiddle", + "createdAt": 1373303300000, + "text": "Or if possible, stop using an Array entirely, and instead use an Object as a dictionary, as MattMcKnight and ninjagecko have suggested.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9033a", + "creator": "william malo", + "createdAt": 1332565199000, + "text": "

Let's say you've defined an array like so:

\n\n
const array = [1, 2, 3, 4]\n
\n\n

Below are three ways of checking whether there is a 3 in there. All of them return either true or false.

\n\n

Native Array method (since ES2016) (compatibility table)

\n\n
array.includes(3) // true\n
\n\n

As custom Array method (pre ES2016)

\n\n
// Prefixing the method with '_' to avoid name clashes\nObject.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})\narray._includes(3) // true\n
\n\n

Simple function

\n\n
const includes = (a, v) => a.indexOf(v) !== -1\nincludes(array, 3) // true\n
\n", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90339", + "creator": "Carlos A", + "createdAt": 1325857394000, + "text": "

Use:

\n\n
Array.prototype.contains = function(x){\n  var retVal = -1;\n\n  // x is a primitive type\n  if([\"string\",\"number\"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}\n\n  // x is a function\n  else if(typeof x ==\"function\") for(var ix in this){\n    if((this[ix]+\"\")==(x+\"\")) retVal = ix;\n  }\n\n  //x is an object...\n  else {\n    var sx=JSON.stringify(x);\n    for(var ix in this){\n      if(typeof this[ix] ==\"object\" && JSON.stringify(this[ix])==sx) retVal = ix;\n    }\n  }\n\n  //Return False if -1 else number if numeric otherwise string\n  return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);\n}\n
\n\n

I know it's not the best way to go, but since there is no native IComparable way to interact between objects, I guess this is as close as you can get to compare two entities in an array. Also, extending Array object might not be a wise thing to do, but sometimes it's OK (if you are aware of it and the trade-off).

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9033c", + "creator": "Lemex", + "createdAt": 1340800325000, + "text": "
function inArray(elem,array)\n{\n    var len = array.length;\n    for(var i = 0 ; i < len;i++)\n    {\n        if(array[i] == elem){return i;}\n    }\n    return -1;\n} \n
\n\n

Returns array index if found, or -1 if not found

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9033b", + "creator": "ninjagecko", + "createdAt": 1335071821000, + "text": "

While array.indexOf(x)!=-1 is the most concise way to do this (and has been supported by non-Internet Explorer browsers for over decade...), it is not O(1), but rather O(N), which is terrible. If your array will not be changing, you can convert your array to a hashtable, then do table[x]!==undefined or ===undefined:

\n\n
Array.prototype.toTable = function() {\n    var t = {};\n    this.forEach(function(x){t[x]=true});\n    return t;\n}\n
\n\n

Demo:

\n\n
var toRemove = [2,4].toTable();\n[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})\n
\n\n

(Unfortunately, while you can create an Array.prototype.contains to \"freeze\" an array and store a hashtable in this._cache in two lines, this would give wrong results if you chose to edit your array later. JavaScript has insufficient hooks to let you keep this state, unlike Python for example.)

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9033e", + "creator": "Simon_Weaver", + "createdAt": 1363067092000, + "text": "

As others have mentioned you can use Array.indexOf, but it isn't available in all browsers. Here's the code from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf to make it work the same in older browsers.

\n\n
\n

indexOf is a recent addition to the ECMA-262 standard; as such it may\n not be present in all browsers. You can work around this by inserting\n the following code at the beginning of your scripts, allowing use of\n indexOf in implementations which do not natively support it. This\n algorithm is exactly the one specified in ECMA-262, 5th edition,\n assuming Object, TypeError, Number, Math.floor, Math.abs, and Math.max\n have their original value.

\n
\n\n
if (!Array.prototype.indexOf) {\n    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {\n        \"use strict\";\n        if (this == null) {\n            throw new TypeError();\n        }\n        var t = Object(this);\n        var len = t.length >>> 0;\n        if (len === 0) {\n            return -1;\n        }\n        var n = 0;\n        if (arguments.length > 1) {\n            n = Number(arguments[1]);\n            if (n != n) { // shortcut for verifying if it's NaN\n                n = 0;\n            } else if (n != 0 && n != Infinity && n != -Infinity) {\n                n = (n > 0 || -1) * Math.floor(Math.abs(n));\n            }\n        }\n        if (n >= len) {\n            return -1;\n        }\n        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);\n        for (; k < len; k++) {\n            if (k in t && t[k] === searchElement) {\n                return k;\n            }\n        }\n        return -1;\n    }\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9033d", + "creator": "Andy Rohr", + "createdAt": 1353689097000, + "text": "

Similar thing: Finds the first element by a \"search lambda\":

\n\n
Array.prototype.find = function(search_lambda) {\n  return this[this.map(search_lambda).indexOf(true)];\n};\n
\n\n

Usage:

\n\n
[1,3,4,5,8,3,5].find(function(item) { return item % 2 == 0 })\n=> 4\n
\n\n

Same in coffeescript:

\n\n
Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d6082fcc3049e91622", + "creator": "Casey Chu", + "createdAt": 1381380563000, + "text": "A more efficient way to implement this method would be to use a loop and stop applying search_lambda once something is found.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9033f", + "creator": "stamat", + "createdAt": 1372868793000, + "text": "

I looked through submitted answers and got that they only apply if you search for the object via reference. A simple linear search with reference object comparison.

\n\n

But lets say you don't have the reference to an object, how will you find the correct object in the array? You will have to go linearly and deep compare with each object. Imagine if the list is too large, and the objects in it are very big containing big pieces of text. The performance drops drastically with the number and size of the elements in the array.

\n\n

You can stringify objects and put them in the native hash table, but then you will have data redundancy remembering these keys cause JavaScript keeps them for 'for i in obj', and you only want to check if the object exists or not, that is, you have the key.

\n\n

I thought about this for some time constructing a JSON Schema validator, and I devised a simple wrapper for the native hash table, similar to the sole hash table implementation, with some optimization exceptions which I left to the native hash table to deal with. It only needs performance benchmarking...\nAll the details and code can be found on my blog: http://stamat.wordpress.com/javascript-quickly-find-very-large-objects-in-a-large-array/\nI will soon post benchmark results.

\n\n

The complete solution works like this:

\n\n
var a = {'a':1,\n 'b':{'c':[1,2,[3,45],4,5],\n 'd':{'q':1, 'b':{'q':1, 'b':8},'c':4},\n 'u':'lol'},\n 'e':2};\n\n var b = {'a':1, \n 'b':{'c':[2,3,[1]],\n 'd':{'q':3,'b':{'b':3}}},\n 'e':2};\n\n var c = \"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\";\n\n var hc = new HashCache([{a:3, b:2, c:5}, {a:15, b:2, c:'foo'}]); //init\n\n hc.put({a:1, b:1});\n hc.put({b:1, a:1});\n hc.put(true);\n hc.put('true');\n hc.put(a);\n hc.put(c);\n hc.put(d);\n console.log(hc.exists('true'));\n console.log(hc.exists(a));\n console.log(hc.exists(c));\n console.log(hc.exists({b:1, a:1}));\n hc.remove(a);\n console.log(hc.exists(c));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90340", + "creator": "Matías Cánepa", + "createdAt": 1379093524000, + "text": "

Use:

\n\n
function isInArray(array, search)\n{\n    return array.indexOf(search) >= 0;\n}\n\n// Usage\nif(isInArray(my_array, \"my_value\"))\n{\n    //...\n}\n
\n", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d6082fcc3049e91626", + "creator": "Ry-", + "createdAt": 1393432733000, + "text": "x ? true : false is usually redundant. It is here.", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f323d6082fcc3049e91627", + "creator": "Matías Cánepa", + "createdAt": 1399131646000, + "text": "@minitech Why do you say it is redundant?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323d6082fcc3049e91629", + "creator": "Ry-", + "createdAt": 1399138717000, + "text": "array.indexOf(search) >= 0 is already a boolean. Just return array.indexOf(search) >= 0.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f323d6082fcc3049e9162a", + "creator": "Matías Cánepa", + "createdAt": 1406658318000, + "text": "@minitech well thanks! Actually I didn't know that such a construction could be returned. TIL something new.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90341", + "creator": "Mina Gabriel", + "createdAt": 1381062335000, + "text": "

Use:

\n\n
var myArray = ['yellow', 'orange', 'red'] ;\n\nalert(!!~myArray.indexOf('red')); //true\n
\n\n

Demo

\n\n

To know exactly what the tilde ~ do at this point, refer to this question What does a tilde do when it precedes an expression?.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d6082fcc3049e9162b", + "creator": "Shadow The Kid Wizard", + "createdAt": 1381062781000, + "text": "This was already posted year and half ago no need to repeat it.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90342", + "creator": "Pradeep Mahdevu", + "createdAt": 1401382525000, + "text": "

ECMAScript 6 has an elegant proposal on find.

\n\n
\n

The find method executes the callback function once for each element\n present in the array until it finds one where callback returns a true\n value. If such an element is found, find immediately returns the value\n of that element. Otherwise, find returns undefined. callback is\n invoked only for indexes of the array which have assigned values; it\n is not invoked for indexes which have been deleted or which have never\n been assigned values.

\n
\n\n

Here is the MDN documentation on that.

\n\n

The find functionality works like this.

\n\n
function isPrime(element, index, array) {\n    var start = 2;\n    while (start <= Math.sqrt(element)) {\n        if (element % start++ < 1) return false;\n    }\n    return (element > 1);\n}\n\nconsole.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found\nconsole.log( [4, 5, 8, 12].find(isPrime) ); // 5\n
\n\n

You can use this in ECMAScript 5 and below by defining the function.

\n\n
if (!Array.prototype.find) {\n  Object.defineProperty(Array.prototype, 'find', {\n    enumerable: false,\n    configurable: true,\n    writable: true,\n    value: function(predicate) {\n      if (this == null) {\n        throw new TypeError('Array.prototype.find called on null or undefined');\n      }\n      if (typeof predicate !== 'function') {\n        throw new TypeError('predicate must be a function');\n      }\n      var list = Object(this);\n      var length = list.length >>> 0;\n      var thisArg = arguments[1];\n      var value;\n\n      for (var i = 0; i < length; i++) {\n        if (i in list) {\n          value = list[i];\n          if (predicate.call(thisArg, value, i, list)) {\n            return value;\n          }\n        }\n      }\n      return undefined;\n    }\n  });\n}\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90343", + "creator": "Eduardo Cuomo", + "createdAt": 1402794933000, + "text": "

I use the following:

\n\n
Array.prototype.contains = function (v) {\n    return this.indexOf(v) > -1;\n}\n\nvar a = [ 'foo', 'bar' ];\n\na.contains('foo'); // true\na.contains('fox'); // false\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90344", + "creator": "Michael", + "createdAt": 1405694191000, + "text": "

The top answers assume primitive types but if you want to find out if an array contains an object with some trait, Array.prototype.some() is an elegant solution:

\n
const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]\n\nitems.some(item => item.a === '3')  // returns true\nitems.some(item => item.a === '4')  // returns false\n
\n

The nice thing about it is that the iteration is aborted once the element is found so unnecessary iteration cycles are spared.

\n

Also, it fits nicely in an if statement since it returns a boolean:

\n
if (items.some(item => item.a === '3')) {\n  // do something\n}\n
\n

* As jamess pointed out in the comment, at the time of this answer, September 2018, Array.prototype.some() is fully supported: caniuse.com support table

\n", + "upvotes": 339, + "upvoterUsernames": [], + "downvotes": 126, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90346", + "creator": "dansalmo", + "createdAt": 1410540936000, + "text": "
function contains(a, obj) {\n    return a.some(function(element){return element == obj;})\n}\n
\n\n

Array.prototype.some() was added to the ECMA-262 standard in the 5th edition

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d7082fcc3049e91630", + "creator": "diEcho", + "createdAt": 1527481469000, + "text": "if using es6 than it cam be shorten as contains = (a, obj) => a.some((element) => element === obj))", + "upvotes": 227, + "upvoterUsernames": [], + "downvotes": 227, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90345", + "creator": "dr.dimitru", + "createdAt": 1410351172000, + "text": "

We use this snippet (works with objects, arrays, strings):

\n\n
/*\n * @function\n * @name Object.prototype.inArray\n * @description Extend Object prototype within inArray function\n *\n * @param {mix}    needle       - Search-able needle\n * @param {bool}   searchInKey  - Search needle in keys?\n *\n */\nObject.defineProperty(Object.prototype, 'inArray',{\n    value: function(needle, searchInKey){\n\n        var object = this;\n\n        if( Object.prototype.toString.call(needle) === '[object Object]' || \n            Object.prototype.toString.call(needle) === '[object Array]'){\n            needle = JSON.stringify(needle);\n        }\n\n        return Object.keys(object).some(function(key){\n\n            var value = object[key];\n\n            if( Object.prototype.toString.call(value) === '[object Object]' || \n                Object.prototype.toString.call(value) === '[object Array]'){\n                value = JSON.stringify(value);\n            }\n\n            if(searchInKey){\n                if(value === needle || key === needle){\n                return true;\n                }\n            }else{\n                if(value === needle){\n                    return true;\n                }\n            }\n        });\n    },\n    writable: true,\n    configurable: true,\n    enumerable: false\n});\n
\n\n

Usage:

\n\n
var a = {one: \"first\", two: \"second\", foo: {three: \"third\"}};\na.inArray(\"first\");          //true\na.inArray(\"foo\");            //false\na.inArray(\"foo\", true);      //true - search by keys\na.inArray({three: \"third\"}); //true\n\nvar b = [\"one\", \"two\", \"three\", \"four\", {foo: 'val'}];\nb.inArray(\"one\");         //true\nb.inArray('foo');         //false\nb.inArray({foo: 'val'})   //true\nb.inArray(\"{foo: 'val'}\") //false\n\nvar c = \"String\";\nc.inArray(\"S\");        //true\nc.inArray(\"s\");        //false\nc.inArray(\"2\", true);  //true\nc.inArray(\"20\", true); //false\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90348", + "creator": "AlonL", + "createdAt": 1420634983000, + "text": "

One-liner:

\n\n
function contains(arr, x) {\n    return arr.filter(function(elem) { return elem == x }).length > 0;\n}\n
\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d7082fcc3049e91633", + "creator": "Apolo", + "createdAt": 1461316945000, + "text": "array.filter(e=>e==x).length > 0 is equivalent to array.some(e=>e==x) but some is more efficient", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90347", + "creator": "Oriol", + "createdAt": 1420076439000, + "text": "

ECMAScript 7 introduces Array.prototype.includes.

\n

It can be used like this:

\n
[1, 2, 3].includes(2); // true\n[1, 2, 3].includes(4); // false\n
\n

It also accepts an optional second argument fromIndex:

\n
[1, 2, 3].includes(3, 3); // false\n[1, 2, 3].includes(3, -1); // true\n
\n

Unlike indexOf, which uses Strict Equality Comparison, includes compares using SameValueZero equality algorithm. That means that you can detect if an array includes a NaN:

\n
[1, 2, NaN].includes(NaN); // true\n
\n

Also unlike indexOf, includes does not skip missing indices:

\n
new Array(5).includes(undefined); // true\n
\n

It can be polyfilled to make it work on all browsers.

\n", + "upvotes": 195, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90349", + "creator": "l3x", + "createdAt": 1445425035000, + "text": "

Use lodash's some function.

\n\n

It's concise, accurate and has great cross platform support.

\n\n

The accepted answer does not even meet the requirements.

\n\n

Requirements: Recommend most concise and efficient way to find out if a JavaScript array contains an object.

\n\n

Accepted Answer:

\n\n
$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])\n> -1\n
\n\n

My recommendation:

\n\n
_.some([{'a': 1}, {'b': 2}], {'b': 2})\n> true\n
\n\n

Notes:

\n\n

$.inArray works fine for determining whether a scalar value exists in an array of scalars...

\n\n
$.inArray(2, [1,2])\n> 1\n
\n\n

... but the question clearly asks for an efficient way to determine if an object is contained in an array.

\n\n

In order to handle both scalars and objects, you could do this:

\n\n
(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9034a", + "creator": "rlib", + "createdAt": 1447676769000, + "text": "

One can use Set that has the method \"has()\":

\n\n

\r\n
\r\n
function contains(arr, obj) {\r\n      var proxy = new Set(arr);\r\n      if (proxy.has(obj))\r\n        return true;\r\n      else\r\n        return false;\r\n    }\r\n\r\n    var arr = ['Happy', 'New', 'Year'];\r\n    console.log(contains(arr, 'Happy'));
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d7082fcc3049e91637", + "creator": "Maciej Bukowski", + "createdAt": 1471563047000, + "text": "I think return proxy.has(obj) is much cleaner than two lines with if-else statement here", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323d7082fcc3049e91639", + "creator": "Gordon Bean", + "createdAt": 1583941725000, + "text": "function contains(arr, obj) { return new Set(arr).has(obj); }", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9034c", + "creator": "user2724028", + "createdAt": 1452420406000, + "text": "

You can also use this trick:

\n\n
var arrayContains = function(object) {\n  return (serverList.filter(function(currentObject) {\n    if (currentObject === object) {\n      return currentObject\n    }\n    else {\n      return false;\n    }\n  }).length > 0) ? true : false\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9034b", + "creator": "sqram", + "createdAt": 1452321523000, + "text": "

By no means the best, but I was just getting creative and adding to the repertoire.

\n

Do not use this

\n

\r\n
\r\n
Object.defineProperty(Array.prototype, 'exists', {\n  value: function(element, index) {\n\n    var index = index || 0\n\n    return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)\n  }\n})\n\n\n// Outputs 1\nconsole.log(['one', 'two'].exists('two'));\n\n// Outputs -1\nconsole.log(['one', 'two'].exists('three'));\n\nconsole.log(['one', 'two', 'three', 'four'].exists('four'));
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d7082fcc3049e9163d", + "creator": "bryc", + "createdAt": 1487612189000, + "text": "What should you use if not this?", + "upvotes": 250, + "upvoterUsernames": [], + "downvotes": 250, + "downvoterUsernames": [] + }, + { + "_id": "62f323d7082fcc3049e9163f", + "creator": "sqram", + "createdAt": 1489693797000, + "text": "@bryc maybe the accepted solution, or another solution from here. If you don't care much for performance, than you can use this", + "upvotes": 5088, + "upvoterUsernames": [], + "downvotes": 5088, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9034e", + "creator": "Maxime Helen", + "createdAt": 1497832909000, + "text": "

Or this solution:

\n\n
Array.prototype.includes = function (object) {\n  return !!+~this.indexOf(object);\n};\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9034d", + "creator": "Igor Barbashin", + "createdAt": 1485303754000, + "text": "

Solution that works in all modern browsers:

\n\n
function contains(arr, obj) {\n  const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration\n  return arr.some(item => JSON.stringify(item) === stringifiedObj);\n}\n
\n\n

Usage:

\n\n
contains([{a: 1}, {a: 2}], {a: 1}); // true\n
\n\n

IE6+ solution:

\n\n
function contains(arr, obj) {\n  var stringifiedObj = JSON.stringify(obj)\n  return arr.some(function (item) {\n    return JSON.stringify(item) === stringifiedObj;\n  });\n}\n\n// .some polyfill, not needed for IE9+\nif (!('some' in Array.prototype)) {\n  Array.prototype.some = function (tester, that /*opt*/) {\n    for (var i = 0, n = this.length; i < n; i++) {\n      if (i in this && tester.call(that, this[i], i, this)) return true;\n    } return false;\n  };\n}\n
\n\n

Usage:

\n\n
contains([{a: 1}, {a: 2}], {a: 1}); // true\n
\n\n

Why to use JSON.stringify?

\n\n

Array.indexOf and Array.includes (as well as most of the answers here) only compare by reference and not by value.

\n\n
[{a: 1}, {a: 2}].includes({a: 1});\n// false, because {a: 1} is a new object\n
\n\n

Bonus

\n\n

Non-optimized ES6 one-liner:

\n\n
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));\n// true\n
\n\n
\n\n

Note:\nComparing objects by value will work better if the keys are in the same order, so to be safe you might sort the keys first with a package like this one: https://www.npmjs.com/package/sort-keys

\n\n
\n\n

Updated the contains function with a perf optimization. Thanks itinance for pointing it out.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90350", + "creator": "KRRySS", + "createdAt": 1501675055000, + "text": "

Using idnexOf() it is a good solution, but you should hide embedded implementation indexOf() function which returns -1 with ~ operator:

\n\n
function include(arr,obj) { \n    return !!(~arr.indexOf(obj)); \n} \n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9034f", + "creator": "Alireza", + "createdAt": 1498995072000, + "text": "

OK, you can just optimise your code to get the result!

\n\n

There are many ways to do this which are cleaner and better, but I just wanted to get your pattern and apply to that using JSON.stringify, just simply do something like this in your case:

\n\n
function contains(a, obj) {\n    for (var i = 0; i < a.length; i++) {\n        if (JSON.stringify(a[i]) === JSON.stringify(obj)) {\n            return true;\n        }\n    }\n    return false;\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90352", + "creator": "Jeeva", + "createdAt": 1504586098000, + "text": "

I was working on a project that I needed a functionality like python set which removes all duplicates values and returns a new list, so I wrote this function maybe useful to someone

\n\n
function set(arr) {\n    var res = [];\n    for (var i = 0; i < arr.length; i++) {\n        if (res.indexOf(arr[i]) === -1) {\n            res.push(arr[i]);\n        }\n    }\n    return res;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90351", + "creator": "Krishna Ganeriwal", + "createdAt": 1503030033000, + "text": "
    \n
  1. Either use Array.indexOf(Object).
  2. \n
  3. With ECMA 7 one can use the Array.includes(Object).
  4. \n
  5. With ECMA 6 you can use Array.find(FunctionName) where FunctionName is a user \ndefined function to search for the object in the array.

    \n\n

    Hope this helps!

  6. \n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90354", + "creator": "Neil Girardi", + "createdAt": 1516853031000, + "text": "

If you're working with ES6 You can use a set:

\n\n
function arrayHas( array, element ) {\n    const s = new Set(array);\n    return s.has(element)\n}\n
\n\n

This should be more performant than just about any other method

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90353", + "creator": "Mitul Panchal", + "createdAt": 1512472574000, + "text": "

It has one parameter: an array numbers of objects. Each object in the array has two integer properties denoted by x and y. The function must return a count of all such objects in the array that satisfy numbers.x == numbers.y

\n\n

\r\n
\r\n
var numbers = [ { x: 1, y: 1 },\r\n                { x: 2, y: 3 },\r\n                { x: 3, y: 3 },\r\n                { x: 3, y: 4 },\r\n                { x: 4, y: 5 } ];\r\nvar count = 0; \r\nvar n = numbers.length;\r\nfor (var i =0;i<n;i++)\r\n{\r\n  if(numbers[i].x==numbers[i].y)\r\n    {count+=1;}\r\n}\r\n\r\nalert(count);
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90356", + "creator": "Shashwat Gupta", + "createdAt": 1539157732000, + "text": "
\n

Simple solution : ES6 Features \"includes\" method

\n
\n\n
let arr = [1, 2, 3, 2, 3, 2, 3, 4];\n\n  arr.includes(2) // true\n\n  arr.includes(93) // false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90355", + "creator": "Durgpal Singh", + "createdAt": 1530007381000, + "text": "

I recommended to use underscore library because its return the value and its supported for all browsers.

\n\n

underscorejs

\n\n
 var findValue = _.find(array, function(item) {\n    return item.id == obj.id;\n });\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90358", + "creator": "Sanjay Magar", + "createdAt": 1544869644000, + "text": "

\r\n
\r\n
    function countArray(originalArray) {\r\n     \r\n    \tvar compressed = [];\r\n    \t// make a copy of the input array\r\n    \tvar copyArray = originalArray.slice(0);\r\n     \r\n    \t// first loop goes over every element\r\n    \tfor (var i = 0; i < originalArray.length; i++) {\r\n     \r\n    \t\tvar count = 0;\t\r\n    \t\t// loop over every element in the copy and see if it's the same\r\n    \t\tfor (var w = 0; w < copyArray.length; w++) {\r\n    \t\t\tif (originalArray[i] == copyArray[w]) {\r\n    \t\t\t\t// increase amount of times duplicate is found\r\n    \t\t\t\tcount++;\r\n    \t\t\t\t// sets item to undefined\r\n    \t\t\t\tdelete copyArray[w];\r\n    \t\t\t}\r\n    \t\t}\r\n     \r\n    \t\tif (count > 0) {\r\n    \t\t\tvar a = new Object();\r\n    \t\t\ta.value = originalArray[i];\r\n    \t\t\ta.count = count;\r\n    \t\t\tcompressed.push(a);\r\n    \t\t}\r\n    \t}\r\n     \r\n    \treturn compressed;\r\n    };\r\n    \r\n    // It should go something like this:\r\n    \r\n    var testArray = new Array(\"dog\", \"dog\", \"cat\", \"buffalo\", \"wolf\", \"cat\", \"tiger\", \"cat\");\r\n    var newArray = countArray(testArray);\r\n    console.log(newArray);
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90357", + "creator": "Nitesh Ranjan", + "createdAt": 1540831347000, + "text": "

In Addition to what others said, if you don't have a reference of the object which you want to search in the array, then you can do something like this.

\n\n
let array = [1, 2, 3, 4, {\"key\": \"value\"}];\n\narray.some((element) => JSON.stringify(element) === JSON.stringify({\"key\": \"value\"})) // true\n\narray.some((element) => JSON.stringify(element) === JSON.stringify({})) // true\n
\n\n

Array.some returns true if any element matches the given condition and returns false if none of the elements matches the given condition.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9035a", + "creator": "Shiva", + "createdAt": 1570815511000, + "text": "

Simple solution for this requirement is using find()

\n

If you're having array of objects like below,

\n
var users = [{id: "101", name: "Choose one..."},\n{id: "102", name: "shilpa"},\n{id: "103", name: "anita"},\n{id: "104", name: "admin"},\n{id: "105", name: "user"}];\n
\n

Then you can check whether the object with your value is already present or not:

\n
let data = users.find(object => object['id'] === '104');\n
\n

if data is null then no admin, else it will return the existing object like:

\n
{id: "104", name: "admin"}\n
\n

Then you can find the index of that object in the array and replace the object using the code:

\n
let indexToUpdate = users.indexOf(data);\nlet newObject = {id: "104", name: "customer"};\nusers[indexToUpdate] = newObject;//your new object\nconsole.log(users);\n
\n

you will get value like:

\n
[{id: "101", name: "Choose one..."},\n{id: "102", name: "shilpa"},\n{id: "103", name: "anita"},\n{id: "104", name: "customer"},\n{id: "105", name: "user"}];\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90359", + "creator": "Sumer", + "createdAt": 1554644570000, + "text": "

Surprised that this question still doesn't have latest syntax added, adding my 2 cents.

\n\n

Let's say we have array of Objects arrObj and we want to search obj in it.

\n\n

Array.prototype.indexOf -> (returns index or -1) is generally used for finding index of element in array.\nThis can also be used for searching object but only works if you are passing reference to same object.

\n\n
let obj = { name: 'Sumer', age: 36 };\nlet arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];\n\n\nconsole.log(arrObj.indexOf(obj));// 0\nconsole.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1\n\nconsole.log([1, 3, 5, 2].indexOf(2)); //3\n
\n\n

Array.prototype.includes -> (returns true or false)

\n\n
console.log(arrObj.includes(obj));  //true\nconsole.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false\n\nconsole.log([1, 3, 5, 2].includes(2)); //true\n
\n\n

Array.prototype.find -> (takes callback, returns first value/object that returns true in CB).

\n\n
console.log(arrObj.find(e => e.age > 40));  //{ name: 'Kishor', age: 46 }\nconsole.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }\n\nconsole.log([1, 3, 5, 2].find(e => e > 2)); //3\n
\n\n

Array.prototype.findIndex -> (takes callback, returns index of first value/object that returns true in CB).

\n\n
console.log(arrObj.findIndex(e => e.age > 40));  //1\nconsole.log(arrObj.findIndex(e => e.age > 40)); //1\n\nconsole.log([1, 3, 5, 2].findIndex(e => e > 2)); //1\n
\n\n

Since find and findIndex takes a callback, we can be fetch any object(even if we don't have the reference) from array by creatively setting the true condition.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9035b", + "creator": "Majedur", + "createdAt": 1579769668000, + "text": "

Object.keys for getting all property names of the object and filter all values that exact or partial match with specified string.

\n

\r\n
\r\n
function filterByValue(array, string) {\n  return array.filter(o =>\n    Object.keys(o).some(k => o[k].toLowerCase().includes(string.toLowerCase())));\n}\n\nconst arrayOfObject = [{\n  name: 'Paul',\n  country: 'Canada',\n}, {\n  name: 'Lea',\n  country: 'Italy',\n}, {\n  name: 'John',\n  country: 'Italy'\n}];\n\nconsole.log(filterByValue(arrayOfObject, 'lea')); // [{name: 'Lea', country: 'Italy'}]\nconsole.log(filterByValue(arrayOfObject, 'ita')); // [{name: 'Lea', country: 'Italy'}, {name: 'John', country: 'Italy'}]
\r\n
\r\n
\r\n

\n

You can also filter by specific key such as.

\n
Object.keys(o).some(k => o.country.toLowerCase().includes(string.toLowerCase())));\n
\n

Now you can just check array count after filtered to check value contains or not.

\n

Hope it's helpful.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9035c", + "creator": "Mamunur Rashid", + "createdAt": 1580059283000, + "text": "

Adding a unique item to a another list

\n\n
searchResults: [\n                {\n                    name: 'Hello',\n                    artist: 'Selana',\n                    album: 'Riga',\n                    id: 1,\n                },\n                {\n                    name: 'Hello;s',\n                    artist: 'Selana G',\n                    album: 'Riga1',\n                    id: 2,\n                },\n                {\n                    name: 'Hello2',\n                    artist: 'Selana',\n                    album: 'Riga11',\n                    id: 3,\n                }\n            ],\n            playlistTracks: [\n              {\n                name: 'Hello',\n                artist: 'Mamunuus',\n                album: 'Riga',\n                id: 4,\n              },\n              {\n                name: 'Hello;s',\n                artist: 'Mamunuus G',\n                album: 'Riga1',\n                id: 2,\n              },\n              {\n                name: 'Hello2',\n                artist: 'Mamunuus New',\n                album: 'Riga11',\n                id: 3,\n              }\n            ],\n            playlistName: \"New PlayListTrack\",\n        };\n    }\n\n    // Adding an unique track in the playList\n    addTrack = track => {\n      if(playlistTracks.find(savedTrack => savedTrack.id === track.id)) {\n        return;\n      }\n      playlistTracks.push(track);\n\n      this.setState({\n        playlistTracks\n      })\n    };\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d9082fcc3049e9164d", + "creator": "stefanowiczp", + "createdAt": 1580999442000, + "text": "What does it have to do with the question asked?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9035d", + "creator": "Riwaj Chalise", + "createdAt": 1587716793000, + "text": "

Use indexOf()

\n\n

You can use the indexOf() method to check whether a given value or element exists in an array or not. The indexOf() method returns the index of the element inside the array if it is found, and returns -1 if it not found. Let's take a look at the following example:

\n\n

\r\n
\r\n
var fruits = [\"Apple\", \"Banana\", \"Mango\", \"Orange\", \"Papaya\"];\r\nvar a = \"Mango\";\r\ncheckArray(a, fruits);\r\n\r\n\r\nfunction checkArray(a, fruits) {\r\n  // Check if a value exists in the fruits array\r\n  if (fruits.indexOf(a) !== -1) {\r\n    return document.write(\"true\");\r\n  } else {\r\n    return document.write(\"false\");\r\n  }\r\n}
\r\n
\r\n
\r\n

\n\n

Use include() Method

\n\n

ES6 has introduced the includes() method to perform this task very easily. But, this method returns only true or false instead of index number:

\n\n

\r\n
\r\n
var fruits = [\"Apple\", \"Banana\", \"Mango\", \"Orange\", \"Papaya\"];\r\nalert(fruits.includes(\"Banana\")); // Outputs: true\r\nalert(fruits.includes(\"Coconut\")); // Outputs: false\r\nalert(fruits.includes(\"Orange\")); // Outputs: true\r\nalert(fruits.includes(\"Cherry\")); // Outputs: false
\r\n
\r\n
\r\n

\n\n

For further reference checkout here

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9035e", + "creator": "Md. Harun Or Rashid", + "createdAt": 1590313696000, + "text": "

This may be a detailed and easy solution.

\n\n
//plain array\nvar arr = ['a', 'b', 'c'];\nvar check = arr.includes('a');\nconsole.log(check); //returns true\nif (check)\n{\n   // value exists in array\n   //write some codes\n}\n\n// array with objects\nvar arr = [\n      {x:'a', y:'b'},\n      {x:'p', y:'q'}\n  ];\n\n// if you want to check if x:'p' exists in arr\nvar check = arr.filter(function (elm){\n    if (elm.x == 'p')\n    {\n       return elm; // returns length = 1 (object exists in array)\n    }\n});\n\n// or y:'q' exists in arr\nvar check = arr.filter(function (elm){\n    if (elm.y == 'q')\n    {\n       return elm; // returns length = 1 (object exists in array)\n    }\n});\n\n// if you want to check, if the entire object {x:'p', y:'q'} exists in arr\nvar check = arr.filter(function (elm){\n    if (elm.x == 'p' && elm.y == 'q')\n    {\n       return elm; // returns length = 1 (object exists in array)\n    }\n});\n\n// in all cases\nconsole.log(check.length); // returns 1\n\nif (check.length > 0)\n{\n   // returns true\n   // object exists in array\n   //write some codes\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9035f", + "creator": "da coconut", + "createdAt": 1591611699000, + "text": "

use Array.prototype.includes for example:

\n

\r\n
\r\n
const fruits = ['coconut', 'banana', 'apple']\n\nconst doesFruitsHaveCoconut = fruits.includes('coconut')// true\n\nconsole.log(doesFruitsHaveCoconut)
\r\n
\r\n
\r\n

\n

maybe read this documentation from MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90360", + "creator": "MsiLucifer", + "createdAt": 1607967194000, + "text": "

You can use findIndex function to check if an array has a specific value.

\n
arrObj.findIndex(obj => obj === comparedValue) !== -1;\n
\n

Returns true if arrObj contains comparedValue, false otherwise.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90361", + "creator": "francis", + "createdAt": 1627048557000, + "text": "

Using RegExp:

\n
console.log(new RegExp('26242').test(['23525', '26242', '25272'].join(''))) // true\n
\n", + "upvotes": 724, + "upvoterUsernames": [], + "downvotes": 724, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90362", + "creator": "Med Aziz CHETOUI", + "createdAt": 1639723552000, + "text": "

The best default method to check if value exist in array JavaScript is some()

\n

Array.prototype.some()

\n

The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false. It doesn't modify the array.

\n
const array = [1, 2, 3, 4, 5];\n\n// checks whether an element is even\nconst even = (element) => element % 2 === 0;\n\nconsole.log(array.some(even));\n// expected output: true\n
\n

The some method is the best one in Browser compatibility\n\"Browser

\n

For more documentation Array.prototype.some() - JavaScript | MDN

\n

Also you can use other two method is find() and includes(). with those method you can get your result but not the best one.

\n

Array.prototype.find() - JavaScript | MDN

\n

Array.prototype.includes() - JavaScript | MDN

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90364", + "creator": "Rohìt Jíndal", + "createdAt": 1652853064000, + "text": "

If you're just trying to check whether a value is included in a collection, It would be more appropriate to use a Set, As Arrays can have duplicate values whereas Sets cannot. Also, Replacing array.includes with set.has improves the performance from O(n2) to O(n). This will be useful when you have to look up multiple values for the same Set. so if you're just going to look up a single value, there's no benefit to use set.has, you can just use an array.includes.

\n

Created a jsbench demo, You can run this to check the performance.

\n

Screenshot of the test execution :

\n

\"enter

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323da082fcc3049e91654", + "creator": "Pranav Garg", + "createdAt": 1658489822000, + "text": "I don't know why, but when I run this .includes is faster.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90363", + "creator": "Ran Turner", + "createdAt": 1640963025000, + "text": "

There are a couple of method which makes this easy to achieve (includes, some, find, findIndex)

\n

\r\n
\r\n
const array = [1, 2, 3, 4, 5, 6, 7];\n\nconsole.log(array.includes(3));\n//includes() determines whether an array includes a certain value among its entries\n\nconsole.log(array.some(x => x === 3)); \n//some() tests if at least one element in the array passes the test implemented by the provided function\n\nconsole.log(array.find(x => x === 3) ? true : false);\n//find() returns the value of the first element in the provided array that satisfies the provided testing function\n\nconsole.log(array.findIndex(x => x === 3) > -1);\n//findIndex() returns the index of the first element in the array that satisfies the provided testing function, else returning -1.
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323da082fcc3049e91657", + "creator": "Sreenikethan I", + "createdAt": 1651502762000, + "text": "for the findIndex one, the ternery is not required right? Since ... > -1 is a comparison and is a boolean by itself...", + "upvotes": 1089, + "upvoterUsernames": [], + "downvotes": 1089, + "downvoterUsernames": [] + }, + { + "_id": "62f323da082fcc3049e91659", + "creator": "Ran Turner", + "createdAt": 1651903544000, + "text": "Thanks @SreenikethanI for mentioning this - I modified that example according to your suggestion", + "upvotes": 930, + "upvoterUsernames": [], + "downvotes": 930, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321be082fcc3049e9024a", + "creator": "mplungjan", + "createdAt": 1491124858000, + "text": "The title is misleading. Where is the [[1,2],[3,4]].includes([3,4]) ?", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e9024b", + "creator": "Ali NajafZadeh", + "createdAt": 1628009768000, + "text": "console.log(['joe', 'jane', 'mary'].includes('jane'));", + "upvotes": 1205, + "upvoterUsernames": [], + "downvotes": 1205, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3185256, + "uvac": 3185311 + } + }, + { + "_id": "62f321bb082fcc3049e8ff09", + "title": "When should I use double or single quotes in JavaScript?", + "title-lowercase": "when should i use double or single quotes in javascript?", + "creator": "aemkei", + "createdAt": 1225190075000, + "status": "open", + "text": "

console.log("double"); vs. console.log('single');

\n

I see more and more JavaScript libraries out there using single quotes when handling strings. What are the reasons to use one over the other?

\n

I thought they're pretty much interchangeable.

\n", + "upvotes": 2304, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 693125, + "answers": 45, + "answerItems": [ + { + "_id": "62f3220c082fcc3049e90ffe", + "creator": "Ady", + "createdAt": 1225190418000, + "text": "

The most likely reason for use of single vs. double in different libraries is programmer preference and/or API consistency. Other than being consistent, use whichever best suits the string.

\n

Using the other type of quote as a literal:

\n
alert('Say "Hello"');\nalert("Say 'Hello'");\n
\n

This can get complicated:

\n
alert("It's \\"game\\" time.");\nalert('It\\'s "game" time.');\n
\n

Another option, new in ECMAScript 6, is template literals which use the backtick character:

\n
alert(`Use "double" and 'single' quotes in the same string`);\nalert(`Escape the \\` back-tick character and the \\${ dollar-brace sequence in a string`);\n
\n

Template literals offer a clean syntax for: variable interpolation, multi-line strings, and more.

\n

Note that JSON is formally specified to use double quotes, which may be worth considering depending on system requirements.

\n", + "upvotes": 1547, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32975082fcc3049e92d00", + "creator": "Ricket", + "createdAt": 1316044114000, + "text": "@Olly Hicks, interesting test!! Single quotes are actually several times faster than double quotes here in Chrome 13 (OSX). Interesting...", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32975082fcc3049e92d02", + "creator": "Cesar Vega", + "createdAt": 1435241167000, + "text": "Sublime Linter complains about double quote on strings so I started adopting single quotes fro strings", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fff", + "creator": "Michiel Overeem", + "createdAt": 1225190599000, + "text": "

There are people that claim to see performance differences: old mailing list thread. But I couldn't find any of them to be confirmed.

\n

The main thing is to look at what kind of quotes (double or single) you are using inside your string. It helps to keep the number of escapes low. For instance, when you are working with HTML content inside your strings, it is easier to use single quotes so that you don't have to escape all double quotes around the attributes.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d05", + "creator": "Damir Zekić", + "createdAt": 1225207016000, + "text": "Though attributes can be as well surrounded with single quotes :)", + "upvotes": 1158, + "upvoterUsernames": [], + "downvotes": 1158, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d06", + "creator": "Michiel Overeem", + "createdAt": 1225304106000, + "text": "Your right, I thought that xml and xhtml prescribed double quotes surrounding attributes, but single quotes are allowed to.", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91000", + "creator": "PhiLho", + "createdAt": 1225198162000, + "text": "

There is strictly no difference, so it is mostly a matter of taste and of what is in the string (or if the JavaScript code itself is in a string), to keep number of escapes low.

\n

The speed difference legend might come from PHP world, where the two quotes have different behavior.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d08", + "creator": "Damir Zekić", + "createdAt": 1225207155000, + "text": "And Ruby, I may add. Python has the same behavior as JavaScript: no difference is made between single/double quotes.", + "upvotes": 879, + "upvoterUsernames": [], + "downvotes": 879, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91001", + "creator": "Kramii", + "createdAt": 1225206907000, + "text": "

Strictly speaking, there is no difference in meaning; so the choice comes down to convenience.

\n

Here are several factors that could influence your choice:

\n\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91002", + "creator": "Andrew Hedges", + "createdAt": 1225226988000, + "text": "

The difference is purely stylistic. I used to be a double-quote Nazi. Now I use single quotes in nearly all cases. There's no practical difference beyond how your editor highlights the syntax.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d0a", + "creator": "Mathias Bynens", + "createdAt": 1241252470000, + "text": "No practical difference? Can you prove it?", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d0b", + "creator": "Anonymous", + "createdAt": 1241255419000, + "text": "The burden of proof is on the guy asserting there's a difference when the language doesn't.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91003", + "creator": "Gareth", + "createdAt": 1225227372000, + "text": "

Section 7.8.4 of the specification describes literal string notation. The only difference is that DoubleStringCharacter is \"SourceCharacter but not double-quote\" and SingleStringCharacter is \"SourceCharacter but not single-quote\". So the only difference can be demonstrated thusly:

\n\n
'A string that\\'s single quoted'\n\n\"A string that's double quoted\"\n
\n\n

So it depends on how much quote escaping you want to do. Obviously the same applies to double quotes in double quoted strings.

\n", + "upvotes": 155, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d0d", + "creator": "SgtPooki", + "createdAt": 1407443814000, + "text": "if you're putting enough apostrophes in your code to make up for how many times you need to hit shift+' then you're doing it wrong.", + "upvotes": 871, + "upvoterUsernames": [], + "downvotes": 871, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d0f", + "creator": "ArtOfWarfare", + "createdAt": 1438978952000, + "text": "@MathiasBynens - That's an interesting observation that hasn't been relevant for at least a year, and maybe as long as 6 years.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d10", + "creator": "jjg", + "createdAt": 1489489810000, + "text": "One should use U+2019 for an apostrophe, not a vertical-single-quote", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91004", + "creator": "Mathias Bynens", + "createdAt": 1241250496000, + "text": "

I'd like to say the difference is purely stylistic, but I'm really having my doubts. Consider the following example:

\n
/*\n    Add trim() functionality to JavaScript...\n      1. By extending the String prototype\n      2. By creating a 'stand-alone' function\n    This is just to demonstrate results are the same in both cases.\n*/\n\n// Extend the String prototype with a trim() method\nString.prototype.trim = function() {\n    return this.replace(/^\\s+|\\s+$/g, '');\n};\n\n// 'Stand-alone' trim() function\nfunction trim(str) {\n    return str.replace(/^\\s+|\\s+$/g, '');\n};\n\ndocument.writeln(String.prototype.trim);\ndocument.writeln(trim);\n
\n

In Safari, Chrome, Opera, and Internet Explorer (tested in Internet Explorer 7 and Internet Explorer 8), this will return the following:

\n
function () {\n    return this.replace(/^\\s+|\\s+$/g, '');\n}\nfunction trim(str) {\n    return str.replace(/^\\s+|\\s+$/g, '');\n}\n
\n

However, Firefox will yield a slightly different result:

\n
function () {\n    return this.replace(/^\\s+|\\s+$/g, "");\n}\nfunction trim(str) {\n    return str.replace(/^\\s+|\\s+$/g, "");\n}\n
\n

The single quotes have been replaced by double quotes. (Also note how the indenting space was replaced by four spaces.) This gives the impression that at least one browser parses JavaScript internally as if everything was written using double quotes. One might think, it takes Firefox less time to parse JavaScript if everything is already written according to this 'standard'.

\n

Which, by the way, makes me a very sad panda, since I think single quotes look much nicer in code. Plus, in other programming languages, they're usually faster to use than double quotes, so it would only make sense if the same applied to JavaScript.

\n

Conclusion: I think we need to do more research on this.

\n

This might explain Peter-Paul Koch's test results from back in 2003.

\n
\n

It seems that single quotes are sometimes faster in Explorer Windows (roughly 1/3 of my tests did show a faster response time), but if Mozilla shows a difference at all, it handles double quotes slightly faster. I found no difference at all in Opera.

\n
\n

2014: Modern versions of Firefox/Spidermonkey don’t do this anymore.

\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d12", + "creator": "Mathias Bynens", + "createdAt": 1241342804000, + "text": "The thing is: it might be slightly faster in one browser (Firefox) while in other browsers, performance is unaffected.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d13", + "creator": "Chris Calo", + "createdAt": 1326200128000, + "text": "I fail to see how the way Firefox prints out functions is relevant here. This doesn't really answer the question.", + "upvotes": 447, + "upvoterUsernames": [], + "downvotes": 447, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d15", + "creator": "Mathias Bynens", + "createdAt": 1326558580000, + "text": "@ChristopherJamesCalo I’m just responding to the other comments here that claim both are the same and there’s no difference at all, other than syntax.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d16", + "creator": "Mathias Bynens", + "createdAt": 1421360423000, + "text": "@JeffLowery In HTML " is escaped as &quot; or similar, not as \\".", + "upvotes": 4296, + "upvoterUsernames": [], + "downvotes": 4296, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91005", + "creator": "Jozzeh", + "createdAt": 1241261950000, + "text": "

I think it's important not to forget that while Internet Explorer might have zero extensions/toolbars installed, Firefox might have some extensions installed (I'm just thinking of Firebug for instance). Those extensions will have an influence on the benchmark result.

\n

Not that it really matters since browser X is faster in getting elementstyles, while browser Y might be faster in rendering a canvas element (hence why a browser "manufacturer" always has the fastest JavaScript engine).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91006", + "creator": "Tom Lianza", + "createdAt": 1248461409000, + "text": "

If you're doing inline JavaScript (arguably a "bad" thing, but avoiding that discussion) single quotes are your only option for string literals, I believe.

\n

E.g., this works fine:

\n
<a onclick="alert('hi');">hi</a>\n
\n

But you can't wrap the "hi" in double quotes, via any escaping method I'm aware of. Even &quot; which would have been my best guess (since you're escaping quotes in an attribute value of HTML) doesn't work for me in Firefox. " won't work either because at this point you're escaping for HTML, not JavaScript.

\n

So, if the name of the game is consistency, and you're going to do some inline JavaScript in parts of your application, I think single quotes are the winner. Someone please correct me if I'm wrong though.

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d18", + "creator": "Konrad Borowski", + "createdAt": 1401042391000, + "text": "@Pacener: Because it's, uh, not wrong. There is a convention in HTML to put attributes between double quotes.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91008", + "creator": "mauro", + "createdAt": 1248604845000, + "text": "

There isn't any difference between single and double quotes in JavaScript.

\n

The specification is important:

\n

Maybe there are performance differences, but they are absolutely minimum and can change any day according to browsers' implementation. Further discussion is futile unless your JavaScript application is hundreds of thousands lines long.

\n

It's like a benchmark if

\n
a=b;\n
\n

is faster than

\n
a = b;\n
\n

(extra spaces)

\n

today, in a particular browser and platform, etc.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d1a", + "creator": "pilavdzice", + "createdAt": 1335995454000, + "text": "without spaces is faster. less characters to parse in the string. :p", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32976082fcc3049e92d1c", + "creator": "Peter Mortensen", + "createdAt": 1593206039000, + "text": "@pilavdzice: Yes, but is it significant? Is it only 0.0057% faster?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91007", + "creator": "Gumbo", + "createdAt": 1248461751000, + "text": "

I would use double quotes when single quotes cannot be used and vice versa:

\n\n
\"'\" + singleQuotedValue + \"'\"\n'\"' + doubleQuotedValue + '\"'\n
\n\n

Instead of:

\n\n
'\\'' + singleQuotedValue + '\\''\n\"\\\"\" + doubleQuotedValue + \"\\\"\"\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32976082fcc3049e92d1e", + "creator": "sudhAnsu63", + "createdAt": 1372317699000, + "text": "What about a string containing both single quote and double quote like O'rea"lly", + "upvotes": 2514, + "upvoterUsernames": [], + "downvotes": 2514, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9100a", + "creator": "Arne", + "createdAt": 1294300786000, + "text": "

If you're dealing with JSON, it should be noted that strictly speaking, JSON strings must be double quoted. Sure, many libraries support single quotes as well, but I had great problems in one of my projects before realizing that single quoting a string is in fact not according to JSON standards.

\n", + "upvotes": 729, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d20", + "creator": "Schmuli", + "createdAt": 1299152779000, + "text": "This is very relevant when working with jQuery.ajax calling into an ASP.NET service (Web Service, Page Method, or MVC).", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d22", + "creator": "jkd", + "createdAt": 1428884490000, + "text": "Less pixel clutter == better", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9100c", + "creator": "Dodzi Dzakuma", + "createdAt": 1334193653000, + "text": "

One more thing that you might want to consider as a reason for the shift from double quotes to single quotes is the increase in popularity of server side scripts. When using PHP you can pass variables and parse JavaScript functions using strings and variables in PHP.

\n

If you write a string and use double quotes for your PHP you won't have to escape any of the single quotes and PHP will automatically retrieve the value of the variables for you.

\n

Example:I need to run a JavaScript function using a variable from my server.

\n
public static function redirectPage( $pageLocation )\n{\n    echo "<script type='text/javascript'>window.location = '$pageLocation';</script>";\n}\n
\n

This saves me a lot of hassle in having to deal with joining strings, and I can effectively call a JavaScript from PHP. This is only one example, but this may be one of several reasons why programmers are defaulting to single quotes in JavaScript.

\n

Quote from PHP documents:

\n
\n

The most important feature of double-quoted strings is the fact that variable names will be expanded. See string parsing for details.

\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d25", + "creator": "DCShannon", + "createdAt": 1426123893000, + "text": "+1, I do this in my MVC.Net project so that the double-quotes from C# don't interfere with the single quotes from javascript, and vice-versa.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d26", + "creator": "BadHorsie", + "createdAt": 1438520919000, + "text": "I think if you are writing JavaScript onto your page from a PHP class method you have bigger problems.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91009", + "creator": "Bastiaan Linders", + "createdAt": 1272555029000, + "text": "

I've been running the following about 20 times. And it appears that double quotes are about 20% faster.

\n\n

The fun part is, if you change part 2 and part 1 around, single quotes are about 20% faster.

\n\n
//Part1\nvar r='';\nvar iTime3 = new Date().valueOf();\nfor(var j=0; j<1000000; j++) {\n    r+='a';\n}\nvar iTime4 = new Date().valueOf();\nalert('With single quote : ' + (iTime4 - iTime3));  \n\n//Part 2                \nvar s=\"\";\nvar iTime1 = new Date().valueOf();\nfor(var i=0; i<1000000; i++) {\n    s += \"a\";\n}\nvar iTime2 = new Date().valueOf();\nalert('With double quote: ' + (iTime2 - iTime1));\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d29", + "creator": "Lauri", + "createdAt": 1350901876000, + "text": "first new Date is slow, add var dummy_date = new Date() to beginning", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d2b", + "creator": "Beejor", + "createdAt": 1528914512000, + "text": "The level of micro-optimization here is so silly that you could also argue that single quotes are faster to type, leading to faster development.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9100b", + "creator": "Mohsen", + "createdAt": 1316323322000, + "text": "

After reading all the answers that say it may be be faster or may be have advantages, I would say double quotes are better or may be faster too because the Google Closure compiler converts single quotes to double quotes.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d2d", + "creator": "ma11hew28", + "createdAt": 1347818944000, + "text": "Do you know why it does that?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d2f", + "creator": "Mohsen", + "createdAt": 1347851967000, + "text": "I don't know. Maybe it's a coding convention and nothing special.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9100e", + "creator": "aelgoa", + "createdAt": 1336326079000, + "text": "

For use of JavaScript code across different languages, I've found single quotes to consistently require less code tweaking.

\n\n

Double quotes support multi-line strings.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d31", + "creator": "James Wilkins", + "createdAt": 1402600261000, + "text": "Double quotes do not support multi-line strings in JavaScript.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d32", + "creator": "aelgoa", + "createdAt": 1404488848000, + "text": "they do if you put back-slashes at each eol", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 168, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d34", + "creator": "James Wilkins", + "createdAt": 1404706647000, + "text": "Just to add, this can break some minifiers as well.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9100d", + "creator": "Mariusz Nowak", + "createdAt": 1335993101000, + "text": "

Technically there's no difference. It's only matter of style and convention.

\n

Douglas Crockford recommends using single quotes for internal strings and double quotes for external (by external we mean those to be displayed to user of application, like messages or alerts).

\n

I personally follow that.

\n

UPDATE: It appears that Mr. Crockford changed his mind and now recommends using double quotes throughout :)

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d35", + "creator": "Eric Rini", + "createdAt": 1348796004000, + "text": "Douglas Crockford vs JQuery. Pick your poison.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d37", + "creator": "BadHorsie", + "createdAt": 1438520977000, + "text": "What is Crockford's reasoning for this?", + "upvotes": 87, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d38", + "creator": "Peter Mortensen", + "createdAt": 1593199411000, + "text": "Yes, see this presentation (at 10 min 08 secs).", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d39", + "creator": "Peter Mortensen", + "createdAt": 1593199528000, + "text": "@Thunderforge: This link is (effectively) broken. Google+ was shut down in 2019.", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 127, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91010", + "creator": "cavalcade", + "createdAt": 1337550497000, + "text": "

It's mostly a matter of style and preference. There are some rather interesting and useful technical explorations in the other answers, so perhaps the only thing I might add is to offer a little worldly advice.

\n\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9100f", + "creator": "mariotti", + "createdAt": 1336418717000, + "text": "

I hope I am not adding something obvious, but I have been struggling with Django, Ajax, and JSON on this.

\n

Assuming that in your HTML code you do use double quotes, as normally should be, I highly suggest to use single quotes for the rest in JavaScript.

\n

So I agree with ady, but with some care.

\n

My bottom line is:

\n

In JavaScript it probably doesn't matter, but as soon as you embed it inside HTML or the like you start to get troubles. You should know what is actually escaping, reading, passing your string.

\n

My simple case was:

\n
tbox.innerHTML = tbox.innerHTML + '<div class="thisbox_des" style="width:210px;" onmouseout="clear()"><a href="/this/thislist/'\n                   + myThis[i].pk +'"><img src="/site_media/'\n                   + myThis[i].fields.thumbnail +'" height="80" width="80" style="float:left;" onmouseover="showThis('\n                   + myThis[i].fields.left +','\n                   + myThis[i].fields.right +',\\''\n                   + myThis[i].fields.title +'\\')"></a><p style="float:left;width:130px;height:80px;"><b>'\n                   + myThis[i].fields.title +'</b> '\n                   + myThis[i].fields.description +'</p></div>'\n
\n

You can spot the ' in the third field of showThis.

\n

The double quote didn't work!

\n

It is clear why, but it is also clear why we should stick to single quotes... I guess...

\n

This case is a very simple HTML embedding, and the error was generated by a simple copy/paste from a 'double quoted' JavaScript code.

\n

So to answer the question:

\n

Try to use single quotes while within HTML. It might save a couple of debug issues...

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91012", + "creator": "Jason", + "createdAt": 1345211861000, + "text": "

For me, if I code in a Vim editor and if something is enclosed in single quotes, I can double-click to select only the text within the quotes. Double quotes, on the other hand, include the quote marks which I find annoying when I want to do some quick copy and pasting.

\n

E.g. 'myVar' double-click in the Vim editor copies: >myVar<\n"myVar" literally copies: >"myVar"< and when I paste, I have to delete the quote marks on either side.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91011", + "creator": "Frederic", + "createdAt": 1343142971000, + "text": "

Let's look what a reference does.

\n

Inside jquery.js, every string is double-quoted.

\n

So, beginning now, I'll use double-quoted strings. (I was using single!)

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32977082fcc3049e92d3d", + "creator": "Eric Rini", + "createdAt": 1348795978000, + "text": "Why is this down voted. This is a question of style and the best style is to be consistent and follow those who came before you.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32977082fcc3049e92d3e", + "creator": "James Wilkins", + "createdAt": 1402435096000, + "text": "Perhaps jQuery failed to follow the people before them (or really didn't care, like most other experts). ;)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91013", + "creator": "Eugene Ramirez", + "createdAt": 1346319193000, + "text": "

As stated by other replies, they are almost the same. But I will try to add more.

\n
    \n
  1. Some efficient algorithms use character arrays to process strings. Those algorithms (browser compiler, etc.) would see " (#34) first before ' (#39) therefore saving several CPU cycles depending on your data structure.
  2. \n
  3. " is escaped by anti-XSS engines
  4. \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32978082fcc3049e92d40", + "creator": "Spets", + "createdAt": 1476082480000, + "text": "speculation at best. I tried to look this up and hit a dead end. Would be interested to see where this algo is referenced", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91014", + "creator": "garysb", + "createdAt": 1351741749000, + "text": "

I am not sure if this is relevant in today's world, but double quotes used to be used for content that needed to have control characters processed and single quotes for strings that didn't.

\n

The compiler will run string manipulation on a double quoted string while leaving a single quoted string literally untouched. This used to lead to 'good' developers choosing to use single quotes for strings that didn't contain control characters like \\n or \\0 (not processed within single quotes) and double quotes when they needed the string parsed (at a slight cost in CPU cycles for processing the string).

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32978082fcc3049e92d43", + "creator": "zkent", + "createdAt": 1420043038000, + "text": "As an ex-Perl programmer, this is what I keep thinking though I know its irrelevant in JS.", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f32978082fcc3049e92d45", + "creator": "Faither", + "createdAt": 1639944105000, + "text": "@MichaelGeary, oh dear. That's what I was looking for in this question. Welcome from shells (i.e. Bash <3)...", + "upvotes": 592, + "upvoterUsernames": [], + "downvotes": 592, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91015", + "creator": "moomoo", + "createdAt": 1374389908000, + "text": "

If you're jumping back an forth between JavaScript and C#, it's best to train your fingers for the common convention which is double quotes.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91016", + "creator": "user1429980", + "createdAt": 1375605361000, + "text": "

There is no one better solution; however, I would like to argue that double quotes may be more desirable at times:

\n\n\n\n

Nonetheless, as others have stated, it is most important to remain consistent.

\n", + "upvotes": 463, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32978082fcc3049e92d49", + "creator": "dudewad", + "createdAt": 1447695855000, + "text": "Case in point-- what I just typed (there are multiple apostrophes, no double quotes ;)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32978082fcc3049e92d4b", + "creator": "Davina Leong", + "createdAt": 1470119601000, + "text": "To add to @user1429980's third point, single quotes denotes a different datatype in Java and C.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32978082fcc3049e92d4d", + "creator": "OCDev", + "createdAt": 1578878893000, + "text": "' is easier to confuse with `, so stick with " so that you can see ` better.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91018", + "creator": "Kat Lim Ruiz", + "createdAt": 1384464807000, + "text": "

I think this is all a matter of convenience/preference.

\n

I prefer double quote because it matches what C# has and this is my environment that I normally work in: C# + JavaScript.

\n

Also one possible reason for double quotes over single quotes is this (which I have found in my projects code):\nFrench or some other languages use single quotes a lot (like English actually), so if by some reason you end up rendering strings from the server side (which I know is bad practice), then a single quote will render wrongly.

\n

The probability of using double quotes in a regular language is low, and therefore I think it has a better chance of not breaking something.

\n", + "upvotes": 183, + "upvoterUsernames": [], + "downvotes": 183, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91017", + "creator": "Jules", + "createdAt": 1380295973000, + "text": "

The best practice is to use double quotes ("") first and single quotes ('') if needed after. The reason being is that if you ever use server-side scripting you will not be able to pull content from a server (example SQL queries from a database) if you use singles quotes over double.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9101a", + "creator": "John Kurlak", + "createdAt": 1385092773000, + "text": "

One (silly) reason to use single quotes would be that they don't require you to hit the shift key to type them, whereas a double quote do. (I'm assuming that the average string doesn't require escaping, which is a reasonable assumption.) Now, let's suppose every day I code 200 lines of code. Maybe in those 200 lines I have 30 quotes. Maybe typing a double quote takes 0.1 seconds more time than typing a single quote (because I have to hit the shift key). Then on any given day, I waste 3 seconds. If I code in this manner for 200 days a year for 40 years, then I've wasted 6.7 hours of my life. Food for thought.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9101c", + "creator": "B.F.", + "createdAt": 1401951972000, + "text": "

If your JavaScript source is

\n
elem.innerHTML="<img src='smily' alt='It\\'s a Smily' style='width:50px'>";\n
\n

the HTML source will be:

\n
<img src="smiley" alt="It's a Smiley" style="width:50px">\n
\n

Or for HTML5

\n
<img src=smiley alt="It's a Smiley" style=width:50px>\n
\n

JavaScript allows arrays like that:

\n
var arr=['this','that'];\n
\n

But if you stringify it, it will be for compatibility reasons:

\n
JSON=["this","that"]\n
\n

I'm sure this takes some time.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91019", + "creator": "Juan C. Roldán", + "createdAt": 1384874405000, + "text": "

Talking about performance, quotes will never be your bottleneck. However, the performance is the same in both cases.

\n

Talking about coding speed, if you use ' for delimiting a string, you will need to escape " quotes. You are more likely to need to use " inside the string. Example:

\n
// JSON Objects:\nvar jsonObject = '{"foo":"bar"}';\n\n// HTML attributes:\ndocument.getElementById("foobar").innerHTML = '<input type="text">';\n
\n

Then, I prefer to use ' for delimiting the string, so I have to escape fewer characters.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9101b", + "creator": "MetallimaX", + "createdAt": 1393514456000, + "text": "

If you are using JSHint, it will raise an error if you use a double quoted string.

\n

I used it through the Yeoman scafflholding of AngularJS, but maybe there is somehow a manner to configure this.

\n

By the way, when you handle HTML into JavaScript, it's easier to use single quote:

\n
var foo = '<div class="cool-stuff">Cool content</div>';\n
\n

And at least JSON is using double quotes to represent strings.

\n

There isn't any trivial way to answer to your question.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9101d", + "creator": "James Wilkins", + "createdAt": 1402602424000, + "text": "

Just to add my two cents: In working with both JavaScript and PHP a few years back, I've become accustomed to using single quotes so I can type the escape character ('') without having to escape it as well. I usually used it when typing raw strings with file paths, etc.

\n

Anyhow, my convention ended up becoming the use of single quotes on identifier-type raw strings, such as if (typeof s == 'string') ... (in which escape characters would never be used - ever), and double quotes for texts, such as "Hey, what's up?". I also use single quotes in comments as a typographical convention to show identifier names. This is just a rule of thumb, and I break off only when needed, such as when typing HTML strings '<a href="#"> like so <a>' (though you could reverse the quotes here also). I'm also aware that, in the case of JSON, double quotes are used for the names - but outside that, personally, I prefer the single quotes when escaping is never required for the text between the quotes - like document.createElement('div').

\n

The bottom line is, and as some have mentioned/alluded to, to pick a convention, stick with it, and only deviate when necessary.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9101f", + "creator": "GijsjanB", + "createdAt": 1423646910000, + "text": "

When using CoffeeScript I use double quotes. I agree that you should pick either one and stick to it. CoffeeScript gives you interpolation when using the double quotes.

\n
"This is my #{name}"\n
\n

ECMAScript 6 is using back ticks (`) for template strings. Which probably has a good reason, but when coding, it can be cumbersome to change the string literals character from quotes or double quotes to backticks in order to get the interpolation feature. CoffeeScript might not be perfect, but using the same string literals character everywhere (double quotes) and always be able to interpolate is a nice feature.

\n
`This is my ${name}`\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32979082fcc3049e92d55", + "creator": "Simone Poggi", + "createdAt": 1469607343000, + "text": "To me the back tick is a clear winner in this contest, (almost) no presence inside common text strings, plus var interpolation", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9101e", + "creator": "JJTalik", + "createdAt": 1410369061000, + "text": "

You can use single quotes or double quotes.

\n

This enables you for example to easily nest JavaScript content inside HTML attributes, without the need to escape the quotes.\nThe same is when you create JavaScript with PHP.

\n

The general idea is: if it is possible use such quotes that you won't need to escape.

\n

Less escaping = better code.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91020", + "creator": "Alec Mev", + "createdAt": 1438968526000, + "text": "

Single Quotes

\n

I wish double quotes were the standard, because they make a little bit more sense, but I keep using single quotes because they dominate the scene.

\n

Single quotes:

\n\n

No preference:

\n\n

Double quotes:

\n\n", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32979082fcc3049e92d59", + "creator": "Adam Calvet Bohl", + "createdAt": 1475126183000, + "text": "Crockford now preferes double quotes.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d5b", + "creator": "Suraj Jain", + "createdAt": 1484245266000, + "text": "airbnb now preferes double quotes", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d5d", + "creator": "Suraj Jain", + "createdAt": 1484492311000, + "text": "Ok thanks , i will do so , and don't you get confused when you know these many language with syntax , and other concepts.", + "upvotes": 426, + "upvoterUsernames": [], + "downvotes": 426, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d5e", + "creator": "styfle", + "createdAt": 1487859872000, + "text": "Facebook React uses single quotes source", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d60", + "creator": "Alec Mev", + "createdAt": 1487928975000, + "text": "@styfle Thanks, added eslint-config-fbjs-opensource.", + "upvotes": 1602, + "upvoterUsernames": [], + "downvotes": 1602, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d62", + "creator": "GabrielOshiro", + "createdAt": 1551986399000, + "text": "Google now prefers single quotes", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32979082fcc3049e92d63", + "creator": "Mikko Rantalainen", + "createdAt": 1643199610000, + "text": "I think tslint for TypeScript actually prefers single quotes by default.", + "upvotes": 1179, + "upvoterUsernames": [], + "downvotes": 1179, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91028", + "creator": "lolol", + "createdAt": 1580342747000, + "text": "

I prefer to use the single quote, '. It is easier to type and looks better.

\n

Also, let’s remember that straight quotes (single and double) are a mistake in good typography. Curly quotes are preferable, so instead of escaping straight quotes I prefer to use the correct characters.

\n
const message = 'That\\'s a \\'magic\\' shoe.' // This is wrong\nconst message = 'That’s a ‘magic’ shoe.' // This is correct\n
\n

https://practicaltypography.com/straight-and-curly-quotes.html

\n

Can someone add a smart-quote feature to Visual Studio Code, please?

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d65", + "creator": "basickarl", + "createdAt": 1587642508000, + "text": "What about... when you have to use... interpolation dramatic music.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91027", + "creator": "OCDev", + "createdAt": 1578881096000, + "text": "

Now that it's 2020, we should consider a third option for JavaScript: The single backtick for everything.

\n

This can be used everywhere instead of single or double quotes.

\n

It allows you to do all the things!

\n
    \n
  1. Embed single quotes inside of it: `It's great!`

    \n
  2. \n
  3. Embed double quotes inside of it: `It's "really" great!`

    \n
  4. \n
  5. Use string interpolation: `It's "${better}" than great!`

    \n
  6. \n
  7. It allows multiple lines: `

    \n

    This

    \n

    Makes

    \n

    JavaScript

    \n

    Better!

    \n
  8. \n
\n

`

\n

It also doesn't cause any performance loss when replacing the other two:\nAre backticks (``) slower than other strings in JavaScript?

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d67", + "creator": "Domino", + "createdAt": 1636753717000, + "text": "Backticks cannot be used in import statements.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d68", + "creator": "OCDev", + "createdAt": 1636815971000, + "text": "@Domino is there a Node.js design reason for that? Or should we suggest to the maintainers that they add that?", + "upvotes": 176, + "upvoterUsernames": [], + "downvotes": 176, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91029", + "creator": "Faither", + "createdAt": 1639945631000, + "text": "

In addition, it seems the specification (currently mentioned at MDN) doesn't state any difference between single and double quotes except closing and some unescaped few characters. However, template literal (`) assumes additional parsing/processing.

\n
\n

A string literal is 0 or more Unicode code points enclosed in single or double quotes. Unicode code points may also be represented by an escape sequence. All code points may appear literally in a string literal except for the closing quote code points, U+005C (REVERSE SOLIDUS), U+000D (CARRIAGE RETURN), and U+000A (LINE FEED). Any code points may appear in the form of an escape sequence. String literals evaluate to ECMAScript String values...

\n
\n

Source: https://tc39.es/ecma262/#sec-literals-string-literals

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9102a", + "creator": "Nabi K.A.Z.", + "createdAt": 1651508692000, + "text": "

Single quotation (') uses one keyboard key, but double quotation uses two keys (shift+'). So saving the number of keys press is also an advantage! (:

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91021", + "creator": "abhisekp", + "createdAt": 1441730279000, + "text": "

Just keep consistency in what you use. But don't let down your comfort level.

\n
"This is my string."; // :-|\n"I'm invincible."; // Comfortable :)\n'You can\\'t beat me.'; // Uncomfortable :(\n'Oh! Yes. I can "beat" you.'; // Comfortable :)\n"Do you really think, you can \\"beat\\" me?"; // Uncomfortable :(\n"You're my guest. I can \\"beat\\" you."; // Sometimes, you've to :P\n'You\\'re my guest too. I can "beat" you too.'; // Sometimes, you've to :P\n
\n

ECMAScript 6 update

\n

Using template literal syntax.

\n
`Be "my" guest. You're in complete freedom.`; // Most comfort :D\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91023", + "creator": "ovidb", + "createdAt": 1477568283000, + "text": "

It is just a matter time for me. A few milliseconds lost of my life every time I have to press the Shift key before every time I'm able to type ".

\n

I prefer ' simply because you don't have to do it!

\n

Other than that, you can escape a ' inside single quotes with backslash \\'.

\n

console.log('Don\\'t lose time'); // "Don't lose time"

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91025", + "creator": "Gordon", + "createdAt": 1518172594000, + "text": "

If you use PHP to generate JavaScript code you should use the following declaration.

\n
let value = "<?php echo 'This is my message, "double quoted" - \\'single quoted\\' ?>";\n
\n

The output will be:

\n
This is my message, "double quoted" - 'single quoted'\n
\n

For some reasons it is recommend to use single quotes rather than double quotes in PHP.

\n

For the normal behaviour in JavaScript it is recommend to use single quotes.

\n

\r\n
\r\n
var value = 'This is my message';\ndocument.getElementById('sample-text').innerHTML = value;
\r\n
<span id=\"sample-text\"></span>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91022", + "creator": "Divyesh Kanzariya", + "createdAt": 1457448714000, + "text": "
\n

Examining the pros and cons

\n
\n\n

In favor of single quotes

\n\n\n\n

\r\n
\r\n
elem.innerHTML = '<a href=\"' + url + '\">Hello</a>';
\r\n
\r\n
\r\n\nHowever, single quotes are just as legal in HTML.

\n\n

\r\n
\r\n
elem.innerHTML = \"<a href='\" + url + \"'>Hello</a>\";
\r\n
\r\n
\r\n

\n\n

Furthermore, inline HTML is normally an anti-pattern. Prefer templates.

\n\n\n\n

\r\n
\r\n
myJson = '{ \"hello world\": true }';
\r\n
\r\n
\r\n

\n\n

Again, you shouldn’t have to construct JSON this way. JSON.stringify() is often enough. If not, use templates.

\n\n

In favor of double quotes

\n\n\n\n

In favor of both

\n\n

There is no difference between the two in JavaScript. Therefore, you can use whatever is convenient at the moment. For example, the following string literals all produce the same string:

\n\n

\r\n
\r\n
    \"He said: \\\"Let's go!\\\"\"\r\n    'He said: \"Let\\'s go!\"'\r\n    \"He said: \\\"Let\\'s go!\\\"\"\r\n    'He said: \\\"Let\\'s go!\\\"'
\r\n
\r\n
\r\n

\n\n

Single quotes for internal strings and double for external. That allows you to distinguish internal constants from strings that are to be displayed to the user (or written to disk etc.). Obviously, you should avoid putting the latter in your code, but that can’t always be done.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0a082fcc3049e92f83", + "creator": "Peter Mortensen", + "createdAt": 1593203630000, + "text": "Re "inline HTML": Do you mean "inline JavaScript"?", + "upvotes": 748, + "upvoterUsernames": [], + "downvotes": 748, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91024", + "creator": "Bruno Jennrich", + "createdAt": 1502364223000, + "text": "

I use single quotes most of the time, because when developing in PHP, single quoted-string are in no way altered, which is what I want. When I use

\n
echo "$xyz";\n
\n

In PHP, $xyz gets evaluated, which is not what I want. Therefore I always use ' instead of " when it comes to web development. So I ensure at least string-consistency when it comes to PHP/JavaScript.

\n

Unfortunately this can't be done in Java or Objective-C, where '' stands for character and "" stands for string. But this is another question.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91026", + "creator": "Anthony", + "createdAt": 1562105056000, + "text": "

Personally I prefer single quotes for the sake of readability. If I'm staring at code all day it's easier to see the words with just single quotes as opposed to double quotes.

\n\n

Easier to read as demonstrated here:

\n\n

'easy to read'

\n\n

\"harder to read\"

\n\n

\"\"hardest to read\"\"

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f32209082fcc3049e90f54", + "creator": "Ryan Miller", + "createdAt": 1307635855000, + "text": "which is easier to read? alert("It's game time"); or alert('It\\'s game time');", + "upvotes": 275, + "upvoterUsernames": [], + "downvotes": 129, + "downvoterUsernames": [] + }, + { + "_id": "62f32209082fcc3049e90f55", + "creator": "Manish", + "createdAt": 1395488134000, + "text": "Taking it from a different perspective.... using single quotes reduces number of keystrokes. As you don't need to hit "Shift" key.... :P", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32209082fcc3049e90f56", + "creator": "Dan Bray", + "createdAt": 1483665481000, + "text": "Single quotes are better than double quotes because they use half as many pixels and are therefore quicker to display in your text editor.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 695432, + "uvac": 695477 + } + }, + { + "_id": "62f321bb082fcc3049e8fed5", + "title": "How can I convert a string to boolean in JavaScript?", + "title-lowercase": "how can i convert a string to boolean in javascript?", + "creator": "Kevin", + "createdAt": 1225843988000, + "status": "open", + "text": "

Can I convert a string representing a boolean value (e.g., 'true', 'false') into a intrinsic type in JavaScript?

\n\n

I have a hidden form in HTML that is updated based upon a user's selection within a list. This form contains some fields which represent boolean values and are dynamically populated with an intrinsic boolean value. However, once this value is placed into the hidden input field it becomes a string.

\n\n

The only way I could find to determine the field's boolean value, once it was converted into a string, was to depend upon the literal value of its string representation.

\n\n
var myValue = document.myForm.IS_TRUE.value;\nvar isTrueSet = myValue == 'true';\n
\n\n

Is there a better way to accomplish this?

\n", + "upvotes": 6135, + "upvoterUsernames": [], + "downvotes": 2906, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2904925, + "answers": 97, + "answerItems": [ + { + "_id": "62f321c4082fcc3049e90826", + "creator": "Jared Farrish", + "createdAt": 1225846994000, + "text": "

Remember to match case:

\n\n
var isTrueSet = (myValue.toLowerCase() === 'true');\n
\n\n

Also, if it's a form element checkbox, you can also detect if the checkbox is checked:

\n\n
var isTrueSet = document.myForm.IS_TRUE.checked;\n
\n\n

Assuming that if it is checked, it is \"set\" equal to true. This evaluates as true/false.

\n", + "upvotes": 259, + "upvoterUsernames": [], + "downvotes": 98, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32629082fcc3049e91f60", + "creator": "Woozar", + "createdAt": 1636361744000, + "text": "This will fail if myValue is null or undefined or any type other than a string.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90825", + "creator": "guinaps", + "createdAt": 1225846262000, + "text": "

Do:

\n
var isTrueSet = (myValue === 'true');\n
\n

using the identity operator (===), which doesn't make any implicit type conversions when the compared variables have different types.

\n

This will set isTrueSet to a boolean true if the string is "true" and boolean false if it is string "false" or not set at all.

\n
\n

Don't:

\n

You should probably be cautious about using these two methods for your specific needs:

\n
var myBool = Boolean("false");  // == true\n\nvar myBool = !!"false";  // == true\n
\n

Any string which isn't the empty string will evaluate to true by using them. Although they're the cleanest methods I can think of concerning to boolean conversion, I think they're not what you're looking for.

\n", + "upvotes": 5860, + "upvoterUsernames": [], + "downvotes": 1312, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32629082fcc3049e91f63", + "creator": "NoxFly", + "createdAt": 1639826438000, + "text": "=== should be used because it's also checking for the right type. Also, it has better comparison performances than ==.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32629082fcc3049e91f65", + "creator": "Jose Quijada", + "createdAt": 1649734146000, + "text": "Thanks. Comes in handy when the value comes from an environment variable, which are strings JavaScript, and need to translate those into booleans.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90828", + "creator": "staticsan", + "createdAt": 1225847797000, + "text": "

You need to separate (in your thinking) the value of your selections and the representation of that value.

\n\n

Pick a point in the JavaScript logic where they need to transition from string sentinels to native type and do a comparison there, preferably where it only gets done once for each value that needs to be converted. Remember to address what needs to happen if the string sentinel is not one the script knows (i.e. do you default to true or to false?)

\n\n

In other words, yes, you need to depend on the string's value. :-)

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90827", + "creator": "Jonny Buchanan", + "createdAt": 1225847531000, + "text": "

Your solution is fine.

\n\n

Using === would just be silly in this case, as the field's value will always be a String.

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9082a", + "creator": "Shadow2531", + "createdAt": 1225851328000, + "text": "

You can use regular expressions:

\n\n
/*\n * Converts a string to a bool.\n *\n * This conversion will:\n *\n *  - match 'true', 'on', or '1' as true.\n *  - ignore all white-space padding\n *  - ignore capitalization (case).\n *\n * '  tRue  ','ON', and '1   ' will all evaluate as true.\n *\n */\nfunction strToBool(s)\n{\n    // will match one and only one of the string 'true','1', or 'on' rerardless\n    // of capitalization and regardless off surrounding white-space.\n    //\n    regex=/^\\s*(true|1|on)\\s*$/i\n\n    return regex.test(s);\n}\n
\n\n

If you like extending the String class you can do:

\n\n
String.prototype.bool = function() {\n    return strToBool(this);\n};\n\nalert(\"true\".bool());\n
\n\n

For those (see the comments) that would like to extend the String object to get this but are worried about enumerability and are worried about clashing with other code that extends the String object:

\n\n
Object.defineProperty(String.prototype, \"com_example_bool\", {\n    get : function() {\n        return (/^(true|1)$/i).test(this);\n    }\n});\nalert(\"true\".com_example_bool);\n
\n\n

(Won't work in older browsers of course and Firefox shows false while Opera, Chrome, Safari and IE show true. Bug 720760)

\n", + "upvotes": 208, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90829", + "creator": "JW.", + "createdAt": 1225847878000, + "text": "

If there's some other code that's converting the boolean value to a string, you need to know exactly how that code stores true/false values. Either that or you need to have access to a function that reverses that conversion.

\n\n

There are infinitely many ways to represent boolean values in strings (\"true\", \"Y\", \"1\", etc.). So you shouldn't rely on some general-purpose string-to-boolean converter, like Boolean(myValue). You need to use a routine that reverses the original boolean-to-string conversion, whatever that is.

\n\n

If you know that it converts true booleans to \"true\" strings, then your sample code is fine. Except that you should use === instead of ==, so there's no automatic type conversion.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9082c", + "creator": "Icaro Dourado", + "createdAt": 1243518666000, + "text": "
if (String(a) == \"true\"){\n  //true block\n} else {\n  //false block\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9082b", + "creator": "ander", + "createdAt": 1227786445000, + "text": "

I think this is much universal:

\n

if (String(a).toLowerCase() == "true") ...

\n

It goes:

\n
String(true) == "true"     //returns true\nString(false) == "true"    //returns false\nString("true") == "true"   //returns true\nString("false") == "true"  //returns false\n
\n", + "upvotes": 434, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32629082fcc3049e91f6b", + "creator": "pauloya", + "createdAt": 1330519767000, + "text": "When you can receive a string in uppercase or a boolean, then String(a).toLowerCase() === 'true'", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9082e", + "creator": "PixelSlave", + "createdAt": 1264119758000, + "text": "

Just do a:

\n\n
var myBool = eval (yourString);\n
\n\n

Examples:

\n\n
alert (eval (\"true\") == true); // TRUE\nalert (eval (\"true\") == false); // FALSE\nalert (eval (\"1\") == true); // TRUE\nalert (eval (\"1\") == false); // FALSE\nalert (eval (\"false\") == true); // FALSE;\nalert (eval (\"false\") == false); // TRUE\nalert (eval (\"0\") == true); // FALSE\nalert (eval (\"0\") == false); // TRUE\nalert (eval (\"\") == undefined); // TRUE\nalert (eval () == undefined); // TRUE\n
\n\n

This method handles the empty string and undefined string naturally as if you declare a variable without assigning it a value.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32629082fcc3049e91f6e", + "creator": "Thomas Eding", + "createdAt": 1264120325000, + "text": "-1: Please don't advocate the use of eval (except perhaps for clever hacks and necessity).", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32629082fcc3049e91f70", + "creator": "moo", + "createdAt": 1264120559000, + "text": "that's a really bad and insecure use of eval. and it's not even clever. -1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9082d", + "creator": "Steven", + "createdAt": 1252726001000, + "text": "
const stringToBoolean = (stringValue) => {\n    switch(stringValue?.toLowerCase()?.trim()){\n        case "true": \n        case "yes": \n        case "1": \n          return true;\n\n        case "false": \n        case "no": \n        case "0": \n        case null: \n        case undefined:\n          return false;\n\n        default: \n          return JSON.parse(stringValue);\n    }\n}\n
\n", + "upvotes": 458, + "upvoterUsernames": [], + "downvotes": 175, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32629082fcc3049e91f73", + "creator": "dav_i", + "createdAt": 1625664302000, + "text": "Note, this will default to true - for example: stringToBoolean('banana') // true", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9082f", + "creator": "Thomas Eding", + "createdAt": 1264120207000, + "text": "
Boolean.parse = function (str) {\n  switch (str.toLowerCase ()) {\n    case \"true\":\n      return true;\n    case \"false\":\n      return false;\n    default:\n      throw new Error (\"Boolean.parse: Cannot convert string to boolean.\");\n  }\n};\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262a082fcc3049e91f76", + "creator": "tillda", + "createdAt": 1298372627000, + "text": "You can use true.toString() instead of "true" to be even more clean :-)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3262a082fcc3049e91f78", + "creator": "Steel Brain", + "createdAt": 1535390981000, + "text": "Don't change globals, try to keep your changes isolated, maybe create a new function parseBoolean instead", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90830", + "creator": "cypher", + "createdAt": 1280818063000, + "text": "

The following would be enough

\n\n
String.prototype.boolean = function() {\n    return \"true\" == this; \n};\n\n\"true\".boolean() // returns true \"false\".boolean() // returns false\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262a082fcc3049e91f7a", + "creator": "Szymon Wygnański", + "createdAt": 1312708713000, + "text": "Modifying the prototype is very bad idea", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90832", + "creator": "risingfish", + "createdAt": 1316702813000, + "text": "

Hands down the easiest way (assuming you string will be 'true' or 'false') is:

\n\n
var z = 'true';\nvar y = 'false';\nvar b = (z === 'true'); // will evaluate to true\nvar c = (y === 'true'); // will evaluate to false\n
\n\n

Always use the === operator instead of the == operator for these types of conversions!

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262a082fcc3049e91f7d", + "creator": "YMMD", + "createdAt": 1337301704000, + "text": "What conversion were you talking about? :-)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90831", + "creator": "thdoan", + "createdAt": 1291750528000, + "text": "

The Boolean object doesn't have a 'parse' method. Boolean('false') returns true, so that won't work. !!'false' also returns true, so that won't work also.

\n\n

If you want string 'true' to return boolean true and string 'false' to return boolean false, then the simplest solution is to use eval(). eval('true') returns true and eval('false') returns false. Keep in mind the performance implications when using eval() though.

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262a082fcc3049e91f80", + "creator": "thdoan", + "createdAt": 1359358557000, + "text": "I agree that var isTrueSet = (myValue === 'true'); is the best answer.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90833", + "creator": "Vitim.us", + "createdAt": 1317145648000, + "text": "
function returnBoolean(str){\n\n    str=str.toString().toLowerCase();\n\n    if(str=='true' || str=='1' || str=='yes' || str=='y' || str=='on' || str=='+'){\n        return(true);\n    }\n    else if(str=='false' || str=='0' || str=='no' || str=='n' || str=='off' || str=='-'){\n        return(false);\n    }else{\n        return(undefined);\n    }\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90834", + "creator": "Luke", + "createdAt": 1319103303000, + "text": "

Warning

\n\n

This highly upvoted legacy answer is technically correct but only covers a very specific scenario, when your string value is EXACTLY \"true\" or \"false\".

\n\n

An invalid json string passed into these functions below WILL throw an exception.

\n\n
\n\n

Original answer:

\n\n

How about?

\n\n
JSON.parse(\"True\".toLowerCase());\n
\n\n

or with jQuery

\n\n
$.parseJSON(\"TRUE\".toLowerCase());\n
\n", + "upvotes": 1589, + "upvoterUsernames": [], + "downvotes": 784, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262a082fcc3049e91f82", + "creator": "Yuck", + "createdAt": 1375974049000, + "text": "It's pretty simple to just say JSON.parse("TRUE".toLowerCase()) so that it can parse correctly.", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90835", + "creator": "hajikelist", + "createdAt": 1331757543000, + "text": "

I've found that using '1' and an empty value '' for boolean values works far more predictably than 'true' or 'false' string values... specifically with html forms since uninitialized/empty values in Dom elements will consistently evaluate to false whereas any value within them evaluates to true.

\n\n

For instance:

\n\n
<input type='button' onclick='this.value = tog(this.value);' />\n\n<script type=\"text/javascript\">\n\n    function tog(off) {\n        if(off) {\n            alert('true, toggle to false');\n            return '';\n        } else {\n            alert('false, toggle to true');\n            return '1';\n        }\n    }   \n</script>\n
\n\n

Just seemed like an easier road, so far it's been very consistent/easy... perhaps someone can determine a way to break this?

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90836", + "creator": "imjustmatthew", + "createdAt": 1333650122000, + "text": "

Like @Shadow2531 said, you can't just convert it directly. I'd also suggest that you consider string inputs besides \"true\" and \"false\" that are 'truthy' and 'falsey' if your code is going to be reused/used by others. This is what I use:

\n\n
function parseBoolean(string) {\n  switch (String(string).toLowerCase()) {\n    case \"true\":\n    case \"1\":\n    case \"yes\":\n    case \"y\":\n      return true;\n    case \"false\":\n    case \"0\":\n    case \"no\":\n    case \"n\":\n      return false;\n    default:\n      //you could throw an error, but 'undefined' seems a more logical reply\n      return undefined;\n  }\n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90837", + "creator": "Scrimothy", + "createdAt": 1337807368000, + "text": "

@guinaps> Any string which isn't the empty string will evaluate to true by using them.

\n\n

How about using the String.match() method

\n\n
var str=\"true\";\nvar boolStr=Boolean(str.match(/^true$/i)); \n
\n\n

this alone won't get the 1/0 or the yes/no, but it will catch the TRUE/true, as well, it will return false for any string that happens to have \"true\" as a substring.

\n\n

EDIT

\n\n

Below is a function to handle true/false, 1/0, yes/no (case-insensitive)

\n\n
​function stringToBool(str) {\n    var bool;\n    if (str.match(/^(true|1|yes)$/i) !== null) {\n        bool = true;\n    } else if (str.match(/^(false|0|no)*$/i) !== null) {\n        bool = false;\n    } else {\n        bool = null;\n        if (console) console.log('\"' + str + '\" is not a boolean value');\n    }\n    return bool;\n}\n\nstringToBool('1'); // true\nstringToBool('No'); // false\nstringToBool('falsey'); // null (\"falsey\" is not a boolean value.)\nstringToBool(''); // false\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90838", + "creator": "mbeasley", + "createdAt": 1345466705000, + "text": "

Boolean.parse() does exist in some browser implementations. It's definitely not universal, so if that's something that you need than you shouldn't use this method. But in Chrome, for example (I'm using v21) it works just fine and as one would expect.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262b082fcc3049e91f87", + "creator": "thisismydesign", + "createdAt": 1583406705000, + "text": "Boolean("false") => true", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90839", + "creator": "jerone", + "createdAt": 1350996070000, + "text": "

I've been using this snippet to convert Numbers and Booleans:

\n\n
var result = !isNaN(value) ? parseFloat(value) : /^\\s*(true|false)\\s*$/i.exec(value) ? RegExp.$1.toLowerCase() === \"true\" : value;\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9083a", + "creator": "AndreasPizsa", + "createdAt": 1358870715000, + "text": "

The expression you're looking for simply is

\n\n
/^true$/i.test(myValue)\n
\n\n

as in

\n\n
var isTrueSet = /^true$/i.test(myValue);\n
\n\n

This tests myValue against a regular expression , case-insensitive, and doesn't modify the prototype.

\n\n

Examples:

\n\n
/^true$/i.test(\"true\"); // true\n/^true$/i.test(\"TRUE\"); // true\n/^true$/i.test(\"tRuE\"); // true\n/^true$/i.test(\" tRuE\"); // false (notice the space at the beginning)\n/^true$/i.test(\"untrue\"); // false (some other solutions here will incorrectly return true\n/^true$/i.test(\"false\");// returns false\n/^true$/i.test(\"xyz\");  // returns false\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9083b", + "creator": "BishopZ", + "createdAt": 1359241370000, + "text": "

My take on this question is that it aims to satisfy three objectives:

\n\n\n\n

The problem with using JSON is that it fails by causing a Javascript error. This solution is not resilient (though it satisfies 1 and 3):

\n\n
JSON.parse(\"FALSE\") // fails\n
\n\n

This solution is not concise enough:

\n\n
if(value === \"TRUE\" || value === \"yes\" || ...) { return true; }\n
\n\n

I am working on solving this exact problem for Typecast.js. And the best solution to all three objectives is this one:

\n\n
return /^true$/i.test(v);\n
\n\n

It works for many cases, does not fail when values like {} are passed in, and is very concise. Also it returns false as the default value rather than undefined or throwing an Error, which is more useful in loosely-typed Javascript development. Bravo to the other answers that suggested it!

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262b082fcc3049e91f8c", + "creator": "Kevin Boucher", + "createdAt": 1468008602000, + "text": "return /^(true|yes|1|t|y)$/i.test(str);", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9083c", + "creator": "jackvsworld", + "createdAt": 1362011235000, + "text": "

Building on Steven's answer above, I wrote this function as a generic parser for string input:

\n\n
parse:\n  function (value) {\n    switch (value && value.toLowerCase()) {\n      case null: return null;\n      case \"true\": return true;\n      case \"false\": return false;\n      default: try { return parseFloat(value); } catch (e) { return value; }\n    }\n  }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9083e", + "creator": "dalimian", + "createdAt": 1371966120000, + "text": "

i wrote a helper function that handles your cases (and some more). Feel free to alter it to your specific needs

\n\n
/**\n * @example\n * <code>\n * var pageRequestParams = {'enableFeatureX': 'true'};\n * toBool(pageRequestParams.enableFeatureX);  // returns true\n *\n * toBool(pageRequestParams.enableFeatureY, true, options.enableFeatureY)\n * </code>\n * @param {*}value\n * @param {Boolean}[mapEmptyStringToTrue=false]\n * @param {Boolean}[defaultVal=false] this is returned if value is undefined.\n *\n * @returns {Boolean}\n * @example\n * <code>\n * toBool({'enableFeatureX': ''        }.enableFeatureX);          // false\n * toBool({'enableFeatureX': ''        }.enableFeatureX, true);    // true\n * toBool({                            }.enableFeatureX, true);    // false\n * toBool({'enableFeatureX': 0         }.enableFeatureX);          // false\n * toBool({'enableFeatureX': '0'       }.enableFeatureX);          // false\n * toBool({'enableFeatureX': '0 '      }.enableFeatureX);          // false\n * toBool({'enableFeatureX': 'false'   }.enableFeatureX);          // false\n * toBool({'enableFeatureX': 'falsE '  }.enableFeatureX);          // false\n * toBool({'enableFeatureX': 'no'      }.enableFeatureX);          // false\n *\n * toBool({'enableFeatureX': 1         }.enableFeatureX);          // true\n * toBool({'enableFeatureX': '-2'      }.enableFeatureX);          // true\n * toBool({'enableFeatureX': 'true'    }.enableFeatureX);          // true\n * toBool({'enableFeatureX': 'false_'  }.enableFeatureX);          // true\n * toBool({'enableFeatureX': 'john doe'}.enableFeatureX);          // true\n * </code>\n *\n */\nvar toBool = function (value, mapEmptyStringToTrue, defaultVal) {\n    if (value === undefined) {return Boolean(defaultVal); }\n    mapEmptyStringToTrue = mapEmptyStringToTrue !== undefined ? mapEmptyStringToTrue : false; // default to false\n    var strFalseValues = ['0', 'false', 'no'].concat(!mapEmptyStringToTrue ? [''] : []);\n    if (typeof value === 'string') {\n        return (strFalseValues.indexOf(value.toLowerCase().trim()) === -1);\n    }\n    // value is likely null, boolean, or number\n    return Boolean(value);\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9083d", + "creator": "purab", + "createdAt": 1370429148000, + "text": "

You even do not need to convert the string to boolean. just use the following:\nvar yourstring = yourstringValue == 1 ? true : false;

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90840", + "creator": "user3638793", + "createdAt": 1373404380000, + "text": "

Here is my 1 liner submission: I needed to evaluate a string and output, true if 'true', false if 'false' and a number if anything like '-12.35673'.

\n\n
val = 'false';\n\nval = /^false$/i.test(val) ? false : ( /^true$/i.test(val) ? true : val*1 ? val*1 : val );\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9083f", + "creator": "Andreas Dyballa", + "createdAt": 1372016381000, + "text": "
    MyLib.Convert.bool = function(param) {\n         var res = String(param).toLowerCase();\n         return !(!Boolean(res) || res === \"false\" || res === \"0\");\n     }; \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90841", + "creator": "zobier", + "createdAt": 1375243077000, + "text": "

I use the following:

\n\n
function parseBool(b) {\n    return !(/^(false|0)$/i).test(b) && !!b;\n}\n
\n\n

This function performs the usual Boolean coercion with the exception of the strings \"false\" (case insensitive) and \"0\".

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90842", + "creator": "Dead.Rabit", + "createdAt": 1377081311000, + "text": "

I'm a little late, but I have a little snippet to do this, it essentially maintains all of JScripts truthey/falsey/filthy-ness but includes \"false\" as an acceptible value for false.

\n\n

I prefer this method to the ones mentioned because it doesn't rely on a 3rd party to parse the code (i.e: eval/JSON.parse), which is overkill in my mind, it's short enough to not require a utility function and maintains other truthey/falsey conventions.

\n\n
var value = \"false\";\nvar result = (value == \"false\") != Boolean(value);\n\n// value = \"true\"  => result = true\n// value = \"false\" => result = false\n// value = true    => result = true\n// value = false   => result = false\n// value = null    => result = false\n// value = []      => result = true\n// etc..\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90843", + "creator": "Cliff Mayson", + "createdAt": 1381891222000, + "text": "
function parseBool(value) {\n    if (typeof value === \"boolean\") return value;\n\n    if (typeof value === \"number\") {\n        return value === 1 ? true : value === 0 ? false : undefined;\n    }\n\n    if (typeof value != \"string\") return undefined;\n\n    return value.toLowerCase() === 'true' ? true : false;\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90844", + "creator": "CMCDragonkai", + "createdAt": 1384027783000, + "text": "

I wrote a function to match PHP's filter_var which does this nicely. Available in a gist: https://gist.github.com/CMCDragonkai/7389368

\n\n
/**\n * Parses mixed type values into booleans. This is the same function as filter_var in PHP using boolean validation\n * @param  {Mixed}        value \n * @param  {Boolean}      nullOnFailure = false\n * @return {Boolean|Null}\n */\nvar parseBooleanStyle = function(value, nullOnFailure = false){\n    switch(value){\n        case true:\n        case 'true':\n        case 1:\n        case '1':\n        case 'on':\n        case 'yes':\n            value = true;\n            break;\n        case false:\n        case 'false':\n        case 0:\n        case '0':\n        case 'off':\n        case 'no':\n            value = false;\n            break;\n        default:\n            if(nullOnFailure){\n                value = null;\n            }else{\n                value = false;\n            }\n            break;\n    }\n    return value;\n};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90845", + "creator": "Jan Remunda", + "createdAt": 1390401595000, + "text": "

Universal solution with JSON parse:

\n\n
function getBool(val) {\n    return !!JSON.parse(String(val).toLowerCase());\n}\n\ngetBool(\"1\"); //true\ngetBool(\"0\"); //false\ngetBool(\"true\"); //true\ngetBool(\"false\"); //false\ngetBool(\"TRUE\"); //true\ngetBool(\"FALSE\"); //false\n
\n\n

UPDATE (without JSON):

\n\n
function getBool(val){ \n    var num = +val;\n    return !isNaN(num) ? !!num : !!String(val).toLowerCase().replace(!!0,'');\n}\n
\n\n

I also created fiddle to test it http://jsfiddle.net/remunda/2GRhG/

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90846", + "creator": "Kyle Falconer", + "createdAt": 1392627131000, + "text": "

A lot of the existing answers are similar, but most ignore the fact that the given argument could also be an object.

\n\n

Here is something I just whipped up:

\n\n
Utils.parseBoolean = function(val){\n    if (typeof val === 'string' || val instanceof String){\n        return /true/i.test(val);\n    } else if (typeof val === 'boolean' || val instanceof Boolean){\n        return new Boolean(val).valueOf();\n    } else if (typeof val === 'number' || val instanceof Number){\n        return new Number(val).valueOf() !== 0;\n    }\n    return false;\n};\n
\n\n

...and the unit test for it

\n\n
Utils.Tests = function(){\n    window.console.log('running unit tests');\n\n    var booleanTests = [\n        ['true', true],\n        ['false', false],\n        ['True', true],\n        ['False', false],\n        [, false],\n        [true, true],\n        [false, false],\n        ['gibberish', false],\n        [0, false],\n        [1, true]\n    ];\n\n    for (var i = 0; i < booleanTests.length; i++){\n        var lhs = Utils.parseBoolean(booleanTests[i][0]);\n        var rhs = booleanTests[i][1];\n        var result = lhs === rhs;\n\n        if (result){\n            console.log('Utils.parseBoolean('+booleanTests[i][0]+') === '+booleanTests[i][1]+'\\t : \\tpass');\n        } else {\n            console.log('Utils.parseBoolean('+booleanTests[i][0]+') === '+booleanTests[i][1]+'\\t : \\tfail');\n        }\n    }\n};\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90847", + "creator": "BrDaHa", + "createdAt": 1393200044000, + "text": "

I thought that @Steven 's answer was the best one, and took care of a lot more cases than if the incoming value was just a string. I wanted to extend it a bit and offer the following:

\n\n
function isTrue(value){\n    if (typeof(value) === 'string'){\n        value = value.trim().toLowerCase();\n    }\n    switch(value){\n        case true:\n        case \"true\":\n        case 1:\n        case \"1\":\n        case \"on\":\n        case \"yes\":\n            return true;\n        default: \n            return false;\n    }\n}\n
\n\n

It's not necessary to cover all the false cases if you already know all of the true cases you'd have to account for. You can pass anything into this method that could pass for a true value (or add others, it's pretty straightforward), and everything else would be considered false

\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90848", + "creator": "Stefan Steiger", + "createdAt": 1394141849000, + "text": "

Wood-eye be careful.\nAfter seeing the consequences after applying the top answer with 500+ upvotes, I feel obligated to post something that is actually useful:

\n

Let's start with the shortest, but very strict way:

\n
var str = "true";\nvar mybool = JSON.parse(str);\n
\n

And end with a proper, more tolerant way:

\n
var parseBool = function(str) \n{\n    // console.log(typeof str);\n    // strict: JSON.parse(str)\n    \n    if(str == null)\n        return false;\n    \n    if (typeof str === 'boolean')\n    {\n        return (str === true);\n    } \n    \n    if(typeof str === 'string')\n    {\n        if(str == "")\n            return false;\n            \n        str = str.replace(/^\\s+|\\s+$/g, '');\n        if(str.toLowerCase() == 'true' || str.toLowerCase() == 'yes')\n            return true;\n        \n        str = str.replace(/,/g, '.');\n        str = str.replace(/^\\s*\\-\\s*/g, '-');\n    }\n    \n    // var isNum = string.match(/^[0-9]+$/) != null;\n    // var isNum = /^\\d+$/.test(str);\n    if(!isNaN(str))\n        return (parseFloat(str) != 0);\n        \n    return false;\n}\n
\n

Testing:

\n
var array_1 = new Array(true, 1, "1",-1, "-1", " - 1", "true", "TrUe", "  true  ", "  TrUe", 1/0, "1.5", "1,5", 1.5, 5, -3, -0.1, 0.1, " - 0.1", Infinity, "Infinity", -Infinity, "-Infinity"," - Infinity", " yEs");\n\nvar array_2 = new Array(null, "", false, "false", "   false   ", " f alse", "FaLsE", 0, "00", "1/0", 0.0, "0.0", "0,0", "100a", "1 00", " 0 ", 0.0, "0.0", -0.0, "-0.0", " -1a ", "abc");\n\n\nfor(var i =0; i < array_1.length;++i){ console.log("array_1["+i+"] ("+array_1[i]+"): " + parseBool(array_1[i]));}\n\nfor(var i =0; i < array_2.length;++i){ console.log("array_2["+i+"] ("+array_2[i]+"): " + parseBool(array_2[i]));}\n\nfor(var i =0; i < array_1.length;++i){ console.log(parseBool(array_1[i]));}\nfor(var i =0; i < array_2.length;++i){ console.log(parseBool(array_2[i]));}\n
\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9084a", + "creator": "Mahes", + "createdAt": 1403716449000, + "text": "

Simple solution i have been using it for a while

\n\n
function asBoolean(value) {\n\n    return (''+value) === 'true'; \n\n}\n\n\n// asBoolean(true) ==> true\n// asBoolean(false) ==> false\n// asBoolean('true') ==> true\n// asBoolean('false') ==> false\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90849", + "creator": "user3310384", + "createdAt": 1397133778000, + "text": "

A shorter way to write this, could be var isTrueSet = (myValue === \"true\") ? true : false; Presuming only \"true\" is true and other values are false.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262c082fcc3049e91f9a", + "creator": "jakubiszon", + "createdAt": 1418914764000, + "text": "If you wanted it short why not writing just var isTrueSet = myValue === "true"; ?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9084b", + "creator": "Prestaul", + "createdAt": 1405367022000, + "text": "
var falsy = /^(?:f(?:alse)?|no?|0+)$/i;\nBoolean.parse = function(val) { \n    return !falsy.test(val) && !!val;\n};\n
\n\n

This returns false for every falsy value and true for every truthy value except for 'false', 'f', 'no', 'n', and '0' (case-insensitive).

\n\n
// False\nBoolean.parse(false);\nBoolean.parse('false');\nBoolean.parse('False');\nBoolean.parse('FALSE');\nBoolean.parse('f');\nBoolean.parse('F');\nBoolean.parse('no');\nBoolean.parse('No');\nBoolean.parse('NO');\nBoolean.parse('n');\nBoolean.parse('N');\nBoolean.parse('0');\nBoolean.parse('');\nBoolean.parse(0);\nBoolean.parse(null);\nBoolean.parse(undefined);\nBoolean.parse(NaN);\nBoolean.parse();\n\n//True\nBoolean.parse(true);\nBoolean.parse('true');\nBoolean.parse('True');\nBoolean.parse('t');\nBoolean.parse('yes');\nBoolean.parse('YES');\nBoolean.parse('y');\nBoolean.parse('1');\nBoolean.parse('foo');\nBoolean.parse({});\nBoolean.parse(1);\nBoolean.parse(-1);\nBoolean.parse(new Date());\n
\n", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9084d", + "creator": "Timo Ernst", + "createdAt": 1422284697000, + "text": "

I use an own method which includes a check if the object exists first and a more intuitive conversion to boolean:

\n\n
function str2bool(strvalue){\n  return (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true' || strvalue == '1') : (strvalue == true);\n}\n
\n\n

The results are:

\n\n
var test; // false\nvar test2 = null; // false\nvar test3 = 'undefined'; // false\nvar test4 = 'true'; // true\nvar test5 = 'false'; // false\nvar test6 = true; // true\nvar test7 = false; // false\nvar test8 = 1; // true\nvar test9 = 0; // false\nvar test10 = '1'; // true\nvar test11 = '0'; // false\n
\n\n

Fiddle:\nhttp://jsfiddle.net/av5xcj6s/

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9084c", + "creator": "konsumer", + "createdAt": 1410985037000, + "text": "

I do this, which will handle 1=TRUE=yes=YES=true, 0=FALSE=no=NO=false:

\n\n
BOOL=false\nif (STRING)\n  BOOL=JSON.parse(STRING.toLowerCase().replace('no','false').replace('yes','true'));\n
\n\n

Replace STRING with the name of your string variable.

\n\n

If it's not null, a numerical value or one of these strings:\n\"true\", \"TRUE\", \"false\", \"FALSE\", \"yes\", \"YES\", \"no\", \"NO\"\nIt will throw an error (intentionally.)

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9084e", + "creator": "Adam Pietrasiak", + "createdAt": 1423254585000, + "text": "

I'm using this one

\n\n
String.prototype.maybeBool = function(){\n\n    if ( [\"yes\", \"true\", \"1\", \"on\"].indexOf( this.toLowerCase() ) !== -1 ) return true;\n    if ( [\"no\", \"false\", \"0\", \"off\"].indexOf( this.toLowerCase() ) !== -1 ) return false;\n\n    return this;\n\n}\n\n\"on\".maybeBool(); //returns true;\n\"off\".maybeBool(); //returns false;\n\"I like js\".maybeBool(); //returns \"I like js\"\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9084f", + "creator": "sospedra", + "createdAt": 1424276176000, + "text": "

There are a lot of answers and it's hard to pick one. In my case, I prioritise the performance when choosing, so I create this jsPerf that I hope can throw some light here.

\n\n

Brief of results (the higher the better):

\n\n
    \n
  1. Conditional statement: 2,826,922
  2. \n
  3. Switch case on Bool object: 2,825,469
  4. \n
  5. Casting to JSON: 1,867,774
  6. \n
  7. !! conversions: 805,322
  8. \n
  9. Prototype of String: 713,637
  10. \n
\n\n

They are linked to the related answer where you can find more information (pros and cons) about each one; specially in the comments.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262d082fcc3049e91fa0", + "creator": "spottedmahn", + "createdAt": 1517591769000, + "text": ""something went wrong" when trying to view jsPerf test", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90850", + "creator": "Martin Malinda", + "createdAt": 1424813443000, + "text": "

To evaluate both boolean and boolean-like strings like boolean I used this easy formula:

\n\n
var trueOrStringTrue = (trueOrStringTrue === true) || (trueOrStringTrue === 'true');\n
\n\n

As is apparent, it will return true for both true and 'true'. Everything else returns false.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90851", + "creator": "Fizer Khan", + "createdAt": 1426079785000, + "text": "

To convert both string(\"true\", \"false\") and boolean to boolean

\n\n
('' + flag) === \"true\"\n
\n\n

Where flag can be

\n\n
 var flag = true\n var flag = \"true\"\n var flag = false\n var flag = \"false\"\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90852", + "creator": "null", + "createdAt": 1429847019000, + "text": "

works perfectly and very simple:

\n\n
var boolean = \"false\";\nboolean = (boolean === \"true\");\n\n//boolean = JSON.parse(boolean); //or this way.. \n
\n\n

to test it:

\n\n

\r\n
\r\n
var boolean = \"false\";\r\nboolean = (boolean === \"true\");\r\n\r\n//boolean = JSON.parse(boolean); //or this way.. \r\n\r\nif(boolean == true){\r\n    alert(\"boolean = \"+boolean);\r\n}else{\r\n    alert(\"boolean = \"+boolean);\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262d082fcc3049e91fa5", + "creator": "null", + "createdAt": 1431439661000, + "text": "my bad i have fixed the above snippet - works now. not sure how those inverted commas "" got in there! ;/ @HugoZapata", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3262d082fcc3049e91fa7", + "creator": "Hugo Zapata", + "createdAt": 1431461309000, + "text": "But the question is, how to convert a string to boolean. new Boolean("false") doesn't work, so your answer is not correct.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f3262d082fcc3049e91fa9", + "creator": "null", + "createdAt": 1431688686000, + "text": "@HugoZapata updated the answer, yes was incorrect (but strangely was working before) updated answer works correctly now.", + "upvotes": 1519, + "upvoterUsernames": [], + "downvotes": 1519, + "downvoterUsernames": [] + }, + { + "_id": "62f3262d082fcc3049e91faa", + "creator": "foxdonut", + "createdAt": 1435260876000, + "text": "What is the point of the if/else? You are alert'ing the same thing in both branches.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90854", + "creator": "Siten", + "createdAt": 1457956229000, + "text": "

To Get Boolean values from string or number Here is good solution:

\n\n
var boolValue = Boolean(Number('0'));\n\nvar boolValue = Boolean(Number('1'));\n
\n\n

First will return false and second will return true.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90853", + "creator": "vbranden", + "createdAt": 1438479948000, + "text": "

another solution. jsFiddle

\n\n
var toBoolean = function(value) {\n    var strValue = String(value).toLowerCase();\n    strValue = ((!isNaN(strValue) && strValue !== '0') &&\n        strValue !== '' &&\n        strValue !== 'null' &&\n        strValue !== 'undefined') ? '1' : strValue;\n    return strValue === 'true' || strValue === '1' ? true : false\n};\n
\n\n

test cases run in node

\n\n
> toBoolean(true)\ntrue\n> toBoolean(false)\nfalse\n> toBoolean(undefined)\nfalse\n> toBoolean(null)\nfalse\n> toBoolean('true')\ntrue\n> toBoolean('True')\ntrue\n> toBoolean('False')\nfalse\n> toBoolean('false')\nfalse\n> toBoolean('0')\nfalse\n> toBoolean('1')\ntrue\n> toBoolean('100')\ntrue\n> \n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90855", + "creator": "ecabuk", + "createdAt": 1458724361000, + "text": "
function isTrue(val) {\n    try {\n        return !!JSON.parse(val);\n    } catch (e) {\n        return false;\n    }\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90856", + "creator": "jdnichollsc", + "createdAt": 1461019097000, + "text": "

Take care, maybe in the future the code change and return boolean instead of one string at the moment.

\n\n

The solution would be:

\n\n
//Currently\nvar isTrue = 'true';\n//In the future (Other developer change the code)\nvar isTrue = true;\n//The solution to both cases\n(isTrue).toString() == 'true'\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90858", + "creator": "Sandip Nirmal", + "createdAt": 1464946646000, + "text": "

There are already so many answers available. But following can be useful in some scenarios.

\n\n
// One can specify all values against which you consider truthy\nvar TRUTHY_VALUES = [true, 'true', 1];\n\nfunction getBoolean(a) {\n    return TRUTHY_VALUES.some(function(t) {\n        return t === a;\n    });\n}\n
\n\n

This can be useful where one examples with non-boolean values.

\n\n
getBoolean('aa'); // false\ngetBoolean(false); //false\ngetBoolean('false'); //false\n\ngetBoolean('true'); // true\ngetBoolean(true); // true\ngetBoolean(1); // true\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90857", + "creator": "benipsen", + "createdAt": 1461943715000, + "text": "

Lots of fancy answers here. Really surprised no one has posted this solution:

\n\n
var booleanVal = toCast > '';\n
\n\n

This resolves to true in most cases other than bool false, number zero and empty string (obviously). You can easily look for other falsey string values after the fact e.g.:

\n\n
var booleanVal = toCast > '' && toCast != 'false' && toCast != '0';  \n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9085a", + "creator": "jose.serapicos", + "createdAt": 1483715922000, + "text": "

I use this simple approach (using \"myVarToTest\"):

\n\n
var trueValuesRange = ['1', 1, 'true', true];\n\nmyVarToTest = (trueValuesRange.indexOf(myVarToTest) >= 0);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90859", + "creator": "Hakan Fıstık", + "createdAt": 1480064232000, + "text": "

This has been taken from the accepted answer, but really it has a very weak point, and I am shocked how it got that count of upvotes, the problem with it that you have to consider the case of the string because this is case sensitive

\n\n
var isTrueSet = (myValue.toLowerCase() === 'true');\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262e082fcc3049e91fb3", + "creator": "Angelo Oparah", + "createdAt": 1592127971000, + "text": "not to mention that .toLowerCase might throw if myValue is equal to null or undefined", + "upvotes": 268, + "upvoterUsernames": [], + "downvotes": 268, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9085b", + "creator": "Rohman HM", + "createdAt": 1486643466000, + "text": "

Take it easy using this lib.

\n\n

https://github.com/rohmanhm/force-boolean

\n\n

you just need to write a single line

\n\n
const ForceBoolean = require('force-boolean')\n\nconst YOUR_VAR = 'false'\nconsole.log(ForceBoolean(YOUR_VAR)) // it's return boolean false\n
\n\n

It's also support for following

\n\n
 return false if value is number 0\n return false if value is string '0'\n return false if value is string 'false'\n return false if value is boolean false\n return true if value is number 1\n return true if value is string '1'\n return true if value is string 'true'\n return true if value is boolean true\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9085c", + "creator": "Steve Mc", + "createdAt": 1487058777000, + "text": "
var trueVals = [\"y\", \"t\", \"yes\", \"true\", \"gimme\"];\nvar isTrueSet = (trueVals.indexOf(myValue) > -1) ? true : false;\n
\n\n

or even just

\n\n
var trueVals = [\"y\", \"t\", \"yes\", \"true\", \"gimme\"];\nvar isTrueSet = (trueVals.indexOf(myValue) > -1);\n
\n\n

Similar to some of the switch statements but more compact. The value returned will only be true if the string is one of the trueVals strings. Everything else is false. Of course, you might want to normalise the input string to make it lower case and trim any spaces.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9085d", + "creator": "Surya R Praveen", + "createdAt": 1491839987000, + "text": "

Convert String to Boolean

\n\n
var vIn = \"true\";\nvar vOut = vIn.toLowerCase()==\"true\"?1:0;\n
\n\n

Convert String to Number

\n\n
var vIn = 0;\nvar vOut = parseInt(vIn,10/*base*/);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9085e", + "creator": "Dayem Siddiqui", + "createdAt": 1493464084000, + "text": "

Here is simple function that will do the trick,

\n\n
   function convertStringToBool(str){\n        return ((str === \"True\") || (str === \"true\")) ? true:false;\n    }\n
\n\n

This will give the following result

\n\n
convertStringToBool(\"false\") //returns false\nconvertStringToBool(\"true\") // returns true\nconvertStringToBool(\"False\") // returns false\nconvertStringToBool(\"True\") // returns true\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262e082fcc3049e91fb9", + "creator": "serdar.sanri", + "createdAt": 1557327857000, + "text": "wouldn't return str.toLowerCase() === 'true' simpler?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90860", + "creator": "Chanakya Vadla", + "createdAt": 1494923645000, + "text": "
String(true).toLowerCase() == 'true'; // true\nString(\"true\").toLowerCase() == 'true'; // true\nString(\"True\").toLowerCase() == 'true'; // true\nString(\"TRUE\").toLowerCase() == 'true'; // true\n\nString(false).toLowerCase() == 'true'; // false\n
\n\n

If you are not sure of the input, the above works for boolean and as well any string.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9085f", + "creator": "Vitim.us", + "createdAt": 1494258778000, + "text": "

One Liner

\n\n

We just need to account for the \"false\" string since any other string (including \"true\") is already true.

\n\n
function b(v){ return v===\"false\" ? false : !!v; }\n
\n\n

Test

\n\n
b(true)    //true\nb('true')  //true\nb(false)   //false\nb('false') //false\n
\n\n
\n\n

A more exaustive version

\n\n
function bool(v){ return v===\"false\" || v===\"null\" || v===\"NaN\" || v===\"undefined\" || v===\"0\" ? false : !!v; }\n
\n\n

Test

\n\n
bool(true)        //true\nbool(\"true\")      //true\nbool(1)           //true\nbool(\"1\")         //true\nbool(\"hello\")     //true\n\nbool(false)       //false\nbool(\"false\")     //false\nbool(0)           //false\nbool(\"0\")         //false\nbool(null)        //false\nbool(\"null\")      //false\nbool(NaN)         //false\nbool(\"NaN\")       //false\nbool(undefined)   //false\nbool(\"undefined\") //false\nbool(\"\")          //false\n\nbool([])          //true\nbool({})          //true\nbool(alert)       //true\nbool(window)      //true\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262e082fcc3049e91fbd", + "creator": "Prashanth Hegde", + "createdAt": 1614154591000, + "text": "Any specific reason for using !!v instead of using true directly?", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90861", + "creator": "guest271314", + "createdAt": 1500441667000, + "text": "

You can use Function to return a Boolean value from string \"true\" or \"false\"

\n\n

\r\n
\r\n
const TRUE_OR_FALSE = str => new Function(`return ${str}`)();\r\n\r\nconst [TRUE, FALSE] = [\"true\", \"false\"];\r\n\r\nconst [T, F] = [TRUE_OR_FALSE(TRUE), TRUE_OR_FALSE(FALSE)];\r\n\r\nconsole.log(T, typeof T); // `true` `\"boolean\"`\r\n\r\nconsole.log(F, typeof F); // `false` `\"boolean\"`
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90862", + "creator": "Mohammad Farahani", + "createdAt": 1500958394000, + "text": "

you can use JSON.parse as follows:

\n

\r\n
\r\n
   \nvar trueOrFalse='True';\nresult =JSON.parse(trueOrFalse.toLowerCase());\nif(result==true)\n  alert('this is true');\nelse \n  alert('this is false');
\r\n
\r\n
\r\n

\n

in this case .toLowerCase is important

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262e082fcc3049e91fc0", + "creator": "D. Pardal", + "createdAt": 1593793571000, + "text": "Not guaranteed to return a boolean, though.", + "upvotes": 958, + "upvoterUsernames": [], + "downvotes": 958, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90863", + "creator": "danday74", + "createdAt": 1501628180000, + "text": "

The `toBoolean' function returns false for null, undefined, '', 'false'. It returns true for any other string:

\n\n
const toBoolean = (bool) => {\n  if (bool === 'false') bool = false\n  return !!bool\n}\n\ntoBoolean('false') // returns false\n
\n", + "upvotes": 372, + "upvoterUsernames": [], + "downvotes": 372, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262f082fcc3049e91fc3", + "creator": "Oskar Berggren", + "createdAt": 1509559638000, + "text": "So it would return true for "False", would it not?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90864", + "creator": "Kostanos", + "createdAt": 1508981684000, + "text": "

I'm using this one when I get value from URL/Form or other source.

\n\n

It is pretty universal one line piece of code.

\n\n

Maybe not the best for performance, if you need to run it millions times let me know, we can check how to optimize it, otherwise is pretty good and customizable.

\n\n
boolResult = !(['false', '0', '', 'undefined'].indexOf(String(myVar).toLowerCase().trim()) + 1);\n
\n\n

Result:

\n\n
myVar = true;  // true\nmyVar = 'true';  // true\nmyVar = 'TRUE';  // true\nmyVar = '1';  // true\nmyVar = 'any other value not related to false';  // true\n\nmyVar = false; // false\nmyVar = 'false';  // false\nmyVar = 'FALSE';  // false\nmyVar = '0';  // false\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90865", + "creator": "OhkaBaka", + "createdAt": 1512079074000, + "text": "

Holy god some of these answers are just wild. I love JS and its infinite number of ways to skin a bool.

\n\n

My preference, which I was shocked not to see already, is:

\n\n
testVar = testVar.toString().match(/^(true|[1-9][0-9]*|[0-9]*[1-9]+|yes)$/i) ? true : false;\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90866", + "creator": "Yohan", + "createdAt": 1513266277000, + "text": "

why don't you try something like this

\n\n
Boolean(JSON.parse((yourString.toString()).toLowerCase()));\n
\n\n

It will return an error when some other text is given rather than true or false regardless of the case and it will capture the numbers also as

\n\n
// 0-> false\n// any other number -> true\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90867", + "creator": "pankaj sharma", + "createdAt": 1519309585000, + "text": "

This function can handle string as well as Boolean true/false.

\n\n
function stringToBoolean(val){\n    var a = {\n        'true':true,\n        'false':false\n    };\n    return a[val];\n}\n
\n\n

Demonstration below:

\n\n

\r\n
\r\n
function stringToBoolean(val) {\r\n  var a = {\r\n    'true': true,\r\n    'false': false\r\n  };\r\n  return a[val];\r\n}\r\n\r\nconsole.log(stringToBoolean(\"true\"));\r\n\r\nconsole.log(typeof(stringToBoolean(\"true\")));\r\n\r\nconsole.log(stringToBoolean(\"false\"));\r\n\r\nconsole.log(typeof(stringToBoolean(\"false\")));\r\n\r\nconsole.log(stringToBoolean(true));\r\n\r\nconsole.log(typeof(stringToBoolean(true)));\r\n\r\nconsole.log(stringToBoolean(false));\r\n\r\nconsole.log(typeof(stringToBoolean(false)));\r\n\r\nconsole.log(\"=============================================\");\r\n// what if value was undefined? \r\nconsole.log(\"undefined result:  \" + stringToBoolean(undefined));\r\nconsole.log(\"type of undefined result:  \" + typeof(stringToBoolean(undefined)));\r\nconsole.log(\"=============================================\");\r\n// what if value was an unrelated string?\r\nconsole.log(\"unrelated string result:  \" + stringToBoolean(\"hello world\"));\r\nconsole.log(\"type of unrelated string result:  \" + typeof(stringToBoolean(undefined)));
\r\n
\r\n
\r\n

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90868", + "creator": "Ratan Uday Kumar", + "createdAt": 1546942582000, + "text": "

In nodejs by using node-boolify it is possible

\n\n

Boolean Conversion Results

\n\n
Boolify(true); //true\nBoolify('true'); //true\nBoolify('TRUE'); //null\nBoolify(1); //true\nBoolify(2); //null\nBoolify(false); //false\nBoolify('false'); //false\nBoolify('FALSE'); //null\nBoolify(0); //false\nBoolify(null); //null\nBoolify(undefined); //null\nBoolify(); //null\nBoolify(''); //null\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3262f082fcc3049e91fc9", + "creator": "Denis Nutiu", + "createdAt": 1551109804000, + "text": "I'd rather not introduce a new dependency to the project just for converting a string to a boolean.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f3262f082fcc3049e91fcb", + "creator": "Ratan Uday Kumar", + "createdAt": 1551151947000, + "text": "It is very light weight and also u can validate whether string is boolean", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3262f082fcc3049e91fcd", + "creator": "Dan", + "createdAt": 1610385718000, + "text": "For such a simple task, a library would not be desirable especially when the library controls how a boolean is defined.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9086a", + "creator": "Al Albers", + "createdAt": 1554715218000, + "text": "

If you are certain that the test subject is always a string, then explicitly checking that it equals true is your best bet.

\n\n

You may want to consider including an extra bit of code just in case the subject could actually a boolean.

\n\n
var isTrueSet =\n    myValue === true ||\n    myValue != null &&\n    myValue.toString().toLowerCase() === 'true';\n
\n\n

This could save you a bit of work in the future if the code gets improved/refactored to use actual boolean values instead of strings.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90869", + "creator": "Yuriy Litvin", + "createdAt": 1551863769000, + "text": "

For TypeScript we can use the function:

\n\n
export function stringToBoolean(s: string, valueDefault: boolean = false): boolean {\n    switch(s.toLowerCase())\n    {\n        case \"true\":\n        case \"1\":\n        case \"on\":\n        case \"yes\":\n        case \"y\":\n            return true;\n\n        case \"false\":\n        case \"0\":\n        case \"off\":\n        case \"no\":\n        case \"n\":\n            return false;\n    }\n\n    return valueDefault;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9086c", + "creator": "olegtaranenko", + "createdAt": 1571552219000, + "text": "

I hope this is a most comprehensive use case

\n\n
function parseBoolean(token) {\n  if (typeof token === 'string') {\n    switch (token.toLowerCase()) {\n      case 'on':\n      case 'yes':\n      case 'ok':\n      case 'ja':\n      case 'да':\n      // case '':\n      // case '':\n        token = true;\n        break;\n      default:\n        token = false;\n    }\n  }\n  let ret = false;\n  try {\n    ret = Boolean(JSON.parse(token));\n  } catch (e) {\n    // do nothing or make a notification\n  }\n  return ret;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9086b", + "creator": "panatoni", + "createdAt": 1559294611000, + "text": "

The simplest way which I always use:

\n\n
let value = 'true';\nlet output = value === 'true';\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32630082fcc3049e91fd1", + "creator": "zeross", + "createdAt": 1559808350000, + "text": "Ternary operator is not necesary. Only with let output = value === 'true' works.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32630082fcc3049e91fd3", + "creator": "panatoni", + "createdAt": 1565989396000, + "text": "Yeah sure you are right, I edited my answered - my fault ;]", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9086e", + "creator": "sçuçu", + "createdAt": 1582217283000, + "text": "

In HTML the values of attributes eventually become strings. To mitigate that in undesired situations you can have a function to conditionally parse them into values they represent in the JavaScript or any other programming langauge of interest.

\n\n

Following is an explanation to do it for reviving boolean type from the string type, but it can be further expanded into other data types too, like numbers, arrays or objects.

\n\n

In addition to that JSON.parse has a revive parameter which is a function. It also can be used to achieve the same.

\n\n

Let's call a string looking like a boolean, \"true\", a boolean string likewise we can call a string like a number, \"1\", a number string. Then we can determine if a string is a boolean string:

\n\n
const isBooleanString = (string) => ['true', 'false'].some(item => item === string);\n
\n\n

After that we need to parse the boolean string as JSON by JSON.parse method:

\n\n
JSON.parse(aBooleanString);\n
\n\n

However, any string that is not a boolean string, number string, or any stringified object or array (any invalid JSON) will cause the JSON.parse method to throw a SyntaxError.

\n\n

So, you will need to know with what to call it, i.e. if it is a boolean string. You can achieve this by writing a function that makes the above defiend boolean string check and call JSON.parse:

\n\n
function parse(string){\n  return isBooleanString(string) ? JSON.parse(string)\n    : string;\n}\n
\n\n

One can further generalize the isBooleanString utility to have a more broader perspective on what qualifies as a boolean string by further parametrizing it to accept an optional array of accepted boolean strings:

\n\n
const isBooleanString = (string, spec = ['true', 'false', 'True', 'False']) => spec.some(item => item === string);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9086d", + "creator": "James Anderson Jr.", + "createdAt": 1579529153000, + "text": "

Try this solution (it works like a charm!):

\n\n
function convertStrToBool(str)\n    {\n        switch(String(str).toLowerCase())\n            {\n                case 'undefined': case 'null': case 'nan': case 'false': case 'no': case 'f': case 'n': case '0': case 'off': case '':\n                    return false;\n                    break;\n                default:\n                    return true;\n            };\n    };\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90870", + "creator": "Mansi", + "createdAt": 1588924902000, + "text": "
const result: Boolean = strValue === \"true\" ? true : false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9086f", + "creator": "Faizan Khan", + "createdAt": 1582798000000, + "text": "

The most simple way is

\n\n
a = 'True';\na = !!a && ['1', 'true', 1, true].indexOf(a.toLowerCase()) > -1;\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90872", + "creator": "Justin Liu", + "createdAt": 1590938813000, + "text": "

Use an if statment:

\n

\r\n
\r\n
function parseBool(str) {\n  if (str.toLowerCase() == 'true') {\n    var val = true;\n  } else if (str.toLowerCase() == 'false') {\n    var val = false;\n  } else {\n    //If it is not true of false it returns undefined.//\n    var val = undefined;\n  }\n  return val;\n}\nconsole.log(parseBool(''), typeof parseBool(''));\nconsole.log(parseBool('TrUe'), typeof parseBool('TrUe'));\nconsole.log(parseBool('false'), typeof parseBool('false'));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90871", + "creator": "jacobq", + "createdAt": 1588949491000, + "text": "

Many of the existing answers use an approach that is semantically similar to this,\nbut I think there is value in mentioning that the following \"one liner\" is often sufficient. For example, in addition to the OP's case (strings in a form) one often wants to read environment variables from process.env in NodeJS (whose values, to the best of my knowledge, are always strings) in order to enable or disable certain behaviors, and it is common for these to have the form SOME_ENV_VAR=1.

\n\n
const toBooleanSimple = (input) => \n  ['t', 'y', '1'].some(truePrefix => truePrefix === input[0].toLowerCase());\n
\n\n

A slightly more robust and expressive implementation might look like this:

\n\n
/**\n * Converts strings to booleans in a manner that is less surprising\n * to the non-JS world (e.g. returns true for \"1\", \"yes\", \"True\", etc.\n * and false for \"0\", \"No\", \"false\", etc.)\n * @param input\n * @returns {boolean}\n */\nfunction toBoolean(input) {\n  if (typeof input !== 'string') {\n    return Boolean(input);\n  }\n  const s = input.toLowerCase();\n  return ['t', 'y', '1'].some(prefix => s.startsWith(prefix));\n}\n
\n\n

A (jest) unit test for this might look like this:

\n\n
describe(`toBoolean`, function() {\n  const groups = [{\n    inputs: ['y', 'Yes', 'true', '1', true, 1],\n    expectedOutput: true\n  }, {\n    inputs: ['n', 'No', 'false', '0', false, 0],\n    expectedOutput: false\n  }]\n  for (let group of groups) {\n    for (let input of group.inputs) {\n      it(`should return ${group.expectedOutput} for ${JSON.stringify(input)}`, function() {\n        expect(toBoolean(input)).toEqual(group.expectedOutput);\n      });\n    }      \n  }\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90874", + "creator": "Neil Higgins", + "createdAt": 1595447367000, + "text": "

You don't even need to use a variable, if you know that 'true' will always be lowercase you can use this which will return true or false:

\n
(eval(yourBooleanString == 'true'))\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90873", + "creator": "ritesh", + "createdAt": 1591194965000, + "text": "

WARNING: Never use this method for untrusted input, such as URL parameters.

\n

You can use the eval() function.\nDirectly pass your string to eval() function.

\n

\r\n
\r\n
console.log(eval('true'), typeof eval('true'))\nconsole.log(eval('false'), typeof eval('false'))
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32630082fcc3049e91fdc", + "creator": "D. Pardal", + "createdAt": 1593793401000, + "text": "There are so many more performant and safer ways to do this.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90875", + "creator": "Bhupender Keswani", + "createdAt": 1597223331000, + "text": "

I think it can be done in 1 liner with a use arrow function

\n
const convertStringToBoolean = (value) => value ? String(value).toLowerCase() === 'true' : false;\n
\n

You guys can run and test various cases with following code snippet

\n

\r\n
\r\n
const convertStringToBoolean = (value) => value ? String(value).toLowerCase() === 'true' : false;\n\nconsole.log(convertStringToBoolean(\"a\"));\nconsole.log(convertStringToBoolean(null));\nconsole.log(convertStringToBoolean(undefined));\nconsole.log(convertStringToBoolean(\"undefined\"));\nconsole.log(convertStringToBoolean(true));\nconsole.log(convertStringToBoolean(false));\nconsole.log(convertStringToBoolean(0));\nconsole.log(convertStringToBoolean(1)); // only case which will not work
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90876", + "creator": "Abderrahmen", + "createdAt": 1601394079000, + "text": "

The simplest way to convert a string to a boolean is the following:

\n
Boolean(<stringVariable>)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91fdf", + "creator": "Vaibhav S", + "createdAt": 1605004730000, + "text": "Boolean("false") === true // output true", + "upvotes": 219, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90878", + "creator": "Kev", + "createdAt": 1604525075000, + "text": "

\"Possible\nI recommend you to create a function like the third option in the image and place it in a helper class as export, and reuse this function when you need.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91fe2", + "creator": "Wyck", + "createdAt": 1611694691000, + "text": "Downvoted because you posted an image of the code rather than the code itself.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32631082fcc3049e91fe4", + "creator": "Kieran Ryan", + "createdAt": 1628978401000, + "text": "@Wyck you failed to mention that presentation is nice.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90877", + "creator": "Kal", + "createdAt": 1603990140000, + "text": "

Simple one line operation if you need Boolean false and true from the string values:

\n
storeBooleanHere = stringVariable=="true"?true:false;\n
\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91fe6", + "creator": "Vaibhav S", + "createdAt": 1605005035000, + "text": "simplified version: storeBooleanHere = stringVariable.toLowerCase() !== 'false' ;", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90879", + "creator": "cskwg", + "createdAt": 1605253661000, + "text": "
/// Convert something to boolean\nfunction toBoolean( o ) {\n    if ( null !== o ) {\n        let t = typeof o;\n        if ( "undefined" !== typeof o ) {\n            if ( "string" !== t ) return !!o;\n            o = o.toLowerCase().trim();\n            return "true" === o || "1" === o;\n        }\n    }\n    return false;\n}\n\ntoBoolean(false) --> false\ntoBoolean(true) --> true\ntoBoolean("false") --> false\ntoBoolean("true") --> true\ntoBoolean("TRue") --> true\ntoBoolean("1") --> true\ntoBoolean("0") --> false\ntoBoolean(1) --> true\ntoBoolean(0) --> false\ntoBoolean(123.456) --> true\ntoBoolean(0.0) --> false\ntoBoolean("") --> false\ntoBoolean(null) --> false\ntoBoolean() --> false\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91fe8", + "creator": "Jonathan Lyon", + "createdAt": 1611872595000, + "text": "this is so awesome and useful - thank you so much!", + "upvotes": 328, + "upvoterUsernames": [], + "downvotes": 328, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087a", + "creator": "Alexandre Annic", + "createdAt": 1606931202000, + "text": "

The strongest way is the following because it also handle undefined case:

\n
    ({'true': true, 'false': false})[myValue];\n
\n
    ({'true': true, 'false': false})[undefined] // => undefined\n    ({'true': true, 'false': false})['true'] // => true\n    ({'true': true, 'false': false})['false] // => false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91feb", + "creator": "Khanakia", + "createdAt": 1611483767000, + "text": "What do we say this type of function ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087c", + "creator": "OMANSAK", + "createdAt": 1612438128000, + "text": "
function convertBoolean(value): boolean {\n    if (typeof value == 'string') {\n        value = value.toLowerCase();\n    }\n    switch (value) {\n        case true:\n        case "true":\n        case "evet": // Locale\n        case "t":\n        case "e": // Locale\n        case "1":\n        case "on":\n        case "yes":\n        case 1:\n            return true;\n        case false:\n        case "false":\n        case "hayır": // Locale\n        case "f":\n        case "h": // Locale\n        case "0":\n        case "off":\n        case "no":\n        case 0:\n            return false;\n        default:\n            return null;\n    }\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91fee", + "creator": "Alexander Santos", + "createdAt": 1626879138000, + "text": "This sounds too specific. Mind explaining it?", + "upvotes": 444, + "upvoterUsernames": [], + "downvotes": 444, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087b", + "creator": "Debasish Jena", + "createdAt": 1609356454000, + "text": "

if you are sure the input is anything only within 'true' and 'false'\nwhy not :

\n

\r\n
\r\n
let x = 'true' ;\n//let x = 'false';\nlet y = x === 'true' ? true : false;\nconsole.log(typeof(y), y);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91ff1", + "creator": "Akin Zeman", + "createdAt": 1622938781000, + "text": "same as y= (x=='true');", + "upvotes": 1259, + "upvoterUsernames": [], + "downvotes": 1259, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087e", + "creator": "Force Bolt", + "createdAt": 1619880610000, + "text": "

// Try this in two ways convert a string to boolean

\n
    const checkBoolean = Boolean("false"); \n    const checkBoolean1 = !!"false";  \n    \n    console.log({checkBoolean, checkBoolean1});  \n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91ff4", + "creator": "jbalintac", + "createdAt": 1621385704000, + "text": "Isn't Boolean("false") returns true", + "upvotes": 254, + "upvoterUsernames": [], + "downvotes": 254, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087d", + "creator": "Felipe Chernicharo", + "createdAt": 1614298202000, + "text": "

Simplest solution 🙌🏽

\n

with ES6+

\n

use the logical NOT twice [ !! ] to get the string converted

\n

Just paste this expression...

\n
const stringToBoolean = (string) => string === 'false' ? false : !!string\n
\n

And pass your string to it!

\n
stringToBoolean('')                 // false\nstringToBoolean('false')            // false\nstringToBoolean('true')             // true\nstringToBoolean('hello my friend!') // true\n
\n 🤙🏽 Bonus! 🤙🏽 \n
const betterStringToBoolean = (string) => \n  string === 'false' || string === 'undefined' || string === 'null' || string === '0' ?\n  false : !!string\n
\n

You can include other strings at will to easily extend the usage of this expression...:

\n
betterStringToBoolean('undefined')     // false\nbetterStringToBoolean('null')          // false\nbetterStringToBoolean('0')             // false\nbetterStringToBoolean('false')         // false\nbetterStringToBoolean('')              // false\nbetterStringToBoolean('true')          // true\nbetterStringToBoolean('anything else') // true\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32631082fcc3049e91ff6", + "creator": "ksugiarto", + "createdAt": 1622734699000, + "text": "Ah sorry! My bad, I was testing the first function instead. Thanks for clarifying this.", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9087f", + "creator": "ru4ert", + "createdAt": 1623502177000, + "text": "

ES6+

\n

\r\n
\r\n
const string = \"false\"\nconst string2 = \"true\"\n\nconst test = (val) => (val === \"true\" || val === \"True\")\nconsole.log(test(string))\nconsole.log(test(string2))
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e91ff8", + "creator": "opticyclic", + "createdAt": 1641766710000, + "text": "Pass "True" to this and it returns false", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e91ffa", + "creator": "ru4ert", + "createdAt": 1641812020000, + "text": "@opticyclic ok, i minded this and created a fix.", + "upvotes": 1721, + "upvoterUsernames": [], + "downvotes": 1721, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e91ffc", + "creator": "Nat", + "createdAt": 1658890769000, + "text": "val.toLowerCase() === "true"", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90880", + "creator": "Sigit", + "createdAt": 1627483382000, + "text": "

\r\n
\r\n
const boolTrue = JSON.parse(\"true\")\nconst boolFalse = JSON.parse(\"false\")\n\n\nconsole.log(boolTrue) // true\nconsole.log(boolFalse) // false
\r\n
\r\n
\r\n

\n

To convert string boolean like "true" to actually boolean value is just wrapping to JSON.parse()\nexample: JSON.parse("true")

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90881", + "creator": "Deepak paramesh", + "createdAt": 1627592387000, + "text": "

This is the easiest way to do boolean conversion I came across recently. Thought of adding it.

\n
JSON.parse('true');\n
\n

\r\n
\r\n
let trueResponse = JSON.parse('true');\n\nlet falseResponse = JSON.parse('false');\n\nconsole.log(trueResponse);\nconsole.log(falseResponse);
\r\n
\r\n
\r\n

\n", + "upvotes": 138, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e91fff", + "creator": "Asinus Rex", + "createdAt": 1629193171000, + "text": "This works, but you have to be careful not to pass it empty strings or it'll pop.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92001", + "creator": "Muhammed Moussa", + "createdAt": 1635875368000, + "text": "the sexy solution, I liked it!", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92003", + "creator": "Just a coder", + "createdAt": 1646848537000, + "text": "take all my money! 💵", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92005", + "creator": "marko-36", + "createdAt": 1650469289000, + "text": "This is a quick and dirty solution for storing true/false inside .env", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32632082fcc3049e92007", + "creator": "Ivan Yulin", + "createdAt": 1650891483000, + "text": "should wrap in try/catch in many cases", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90882", + "creator": "cabbage dude", + "createdAt": 1629742529000, + "text": "

The shorthand of Boolean(value) is !!value, this is because ! converts a value to the opposite of what it currently is, and then ! reverses it again back to original form.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90883", + "creator": "Richard Torcato", + "createdAt": 1633028473000, + "text": "

It would be great if there was a function on the String object that did this for us, but we can easily add our own prototypes to extend the String object.

\n

Add this code somewhere in your project before you use it.

\n
String.prototype.toBoolean = function() {\n   return String(this.valueOf()).toLowerCase() === true.toString();\n};\n
\n

Try it out like this:

\n
var myValue = "false"\nconsole.log("Bool is " + myValue.toBoolean())\nconsole.log("Bool is " + "False".toBoolean())\nconsole.log("Bool is " + "FALSE".toBoolean())\nconsole.log("Bool is " + "TRUE".toBoolean())\nconsole.log("Bool is " + "true".toBoolean())\nconsole.log("Bool is " + "True".toBoolean())\n
\n

So the result of the original question would then be:

\n
var myValue = document.myForm.IS_TRUE.value;\nvar isTrueSet = myValue.toBoolean();\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e9200a", + "creator": "Richard Torcato", + "createdAt": 1633028800000, + "text": "Boolean.prototype is fully supported in all browsers", + "upvotes": 2756, + "upvoterUsernames": [], + "downvotes": 2756, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90884", + "creator": "Friedrich", + "createdAt": 1636187990000, + "text": "

I'm suprised that includes was not suggested

\n
let bool = "false"\nbool = !["false", "0", 0].includes(bool)\n
\n

You can modify the check for truely or include more conditions (e.g. null, '').

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32632082fcc3049e9200c", + "creator": "prabhatojha", + "createdAt": 1640679216000, + "text": "hmm, this is perfect", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90885", + "creator": "M. Sherbeeny", + "createdAt": 1649205079000, + "text": "

I needed a code that converts any variable type into Boolean.\nHere's what I came up with:

\n

\r\n
\r\n
const toBoolean = (x) => {\n    if (typeof x === 'object') {\n      for (var i in x) return true\n      return false\n    }\n    return (x !== null) && (x !== undefined) && !['false', '', '0', 'no', 'off'].includes(x.toString().toLowerCase())\n  }
\r\n
\r\n
\r\n

\n

Let's test it!

\n

\r\n
\r\n
const toBoolean = (x) => {\n    if (typeof x === 'object') {\n      for (var i in x) return true\n      return false\n    }\n    return (x !== null) && (x !== undefined) && !['false', '', '0', 'no', 'off'].includes(x.toString().toLowerCase())\n  }\n  \n\n  // Let's test it!\n  let falseValues = [false, 'False', 0, '', 'off', 'no', [], {}, null, undefined]\n  let trueValues = [  true, 'true', 'True', 1, -1, 'Any thing', ['filled array'], {'object with any key': null}]\n  \n  falseValues.forEach((value, index) => console.log(`False value ${index} of type ${typeof value}: ${value} -> ${toBoolean(value)}`))\n  trueValues.forEach((value, index) => console.log(`True value ${index} of type ${typeof value}: ${value} -> ${toBoolean(value)}`))
\r\n
\r\n
\r\n

\n

You can remove words like "off" and "no" from the array if they don't match your case.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 9, + "commentItems": [ + { + "_id": "62f321c3082fcc3049e90785", + "creator": "Rodrigo Siqueira", + "createdAt": 1384170987000, + "text": "Possible duplicate of stackoverflow.com/questions/263965", + "upvotes": 986, + "upvoterUsernames": [], + "downvotes": 986, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e90786", + "creator": "WickyNilliams", + "createdAt": 1441895064000, + "text": "Easily handle strings and bools: function parseBool(val) { return val === true || val === "true" }", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e90787", + "creator": "Sebi", + "createdAt": 1480432228000, + "text": "@Mark function checkBool(x) { if(x) {return true;} else {return false;} }", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e90788", + "creator": "Mark K Cowan", + "createdAt": 1480443704000, + "text": "@Sebi: You forgot to document it: if (checkBool(x) != false) { ... } else { ... }", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e90789", + "creator": "iamandrewluca", + "createdAt": 1487735938000, + "text": "!!(parseInt(value) || value === "true")", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9078a", + "creator": "vitaly-t", + "createdAt": 1570391273000, + "text": "You can't, it is impossible!", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9078b", + "creator": "user13507084", + "createdAt": 1591570702000, + "text": "Why would you want to?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9078c", + "creator": "ZenAtWork", + "createdAt": 1595002551000, + "text": "!myVar.slice(4,5); myVar = 'TRUE' // true myVar = 'FALSE' // false", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9078d", + "creator": "Deepak paramesh", + "createdAt": 1627592210000, + "text": "JSON.parse('true') is the most easiest way to convert to boolean", + "upvotes": 148, + "upvoterUsernames": [], + "downvotes": 148, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2911069, + "uvac": 2911166 + } + }, + { + "_id": "62f321bb082fcc3049e8ff00", + "title": "How can I guarantee that my enums definition doesn't change in JavaScript?", + "title-lowercase": "how can i guarantee that my enums definition doesn't change in javascript?", + "creator": "David Citron", + "createdAt": 1226603346000, + "status": "open", + "text": "

Would the following make the objects fulfil all characteristics that enums have in JavaScript? Something like:

\n
my.namespace.ColorEnum = {\n  RED : 0,\n  GREEN : 1,\n  BLUE : 2\n}\n\n// later on\n\nif(currentColor == my.namespace.ColorEnum.RED) {\n  // whatever\n}\n\n
\n

Or is there some other way I can do this?

\n", + "upvotes": 4049, + "upvoterUsernames": [], + "downvotes": 1697, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1259462, + "answers": 42, + "answerItems": [ + { + "_id": "62f321cc082fcc3049e90e91", + "creator": "Stijn de Witt", + "createdAt": 1267741880000, + "text": "

UPDATE

\n

I don't think my answer below is the best way to write enums in JavaScript anymore. See my blog post for more details: Enums in JavaScript.

\n
\n

Alerting the name is already possible:

\n
if (currentColor == my.namespace.ColorEnum.RED) {\n   // alert name of currentColor (RED: 0)\n   var col = my.namespace.ColorEnum;\n   for (var name in col) {\n     if (col[name] == col.RED)\n       alert(name);\n   }\n}\n
\n

Alternatively, you could make the values objects, so you can have the cake and eat it too:

\n
var SIZE = {\n  SMALL : {value: 0, name: "Small", code: "S"}, \n  MEDIUM: {value: 1, name: "Medium", code: "M"}, \n  LARGE : {value: 2, name: "Large", code: "L"}\n};\n\nvar currentSize = SIZE.MEDIUM;\nif (currentSize == SIZE.MEDIUM) {\n  // this alerts: "1: Medium"\n  alert(currentSize.value + ": " + currentSize.name);\n}\n
\n

In JavaScript, as it is a dynamic language, it is even possible to add enum values to the set later:

\n
// Add EXTRALARGE size\nSIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};\n
\n

Remember, the fields of the enum (value, name and code in this example) are not needed for the identity check and are only there for convenience. Also the name of the size property itself does not need to be hard coded, but can also be set dynamically. So supposing you only know the name for your new enum value, you can still add it without problems:

\n
// Add 'Extra Large' size, only knowing it's name\nvar name = "Extra Large";\nSIZE[name] = {value: -1, name: name, code: "?"};\n
\n

Of course this means that some assumptions can no longer be made (that value represents the correct order for the size for example).

\n

Remember, in JavaScript an object is just like a map or hash table. A set of name-value pairs. You can loop through them or otherwise manipulate them without knowing much about them in advance.

\n

Example

\n
for (var sz in SIZE) {\n  // sz will be the names of the objects in SIZE, so\n  // 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'\n  var size = SIZE[sz]; // Get the object mapped to the name in sz\n  for (var prop in size) {\n    // Get all the properties of the size object, iterates over\n    // 'value', 'name' and 'code'. You can inspect everything this way.        \n  }\n} \n
\n

And by the way, if you are interested in namespaces, you may want to have a look at my solution for simple but powerful namespace and dependency management for JavaScript: Packages JS

\n", + "upvotes": 868, + "upvoterUsernames": [], + "downvotes": 346, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291b082fcc3049e92a1b", + "creator": "Johanisma", + "createdAt": 1320898014000, + "text": "so then how would you go and create simply a SIZE if you only have its name?", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e90", + "creator": "Randolpho", + "createdAt": 1250888211000, + "text": "

Bottom line: You can't.

\n\n

You can fake it, but you won't get type safety. Typically this is done by creating a simple dictionary of string values mapped to integer values. For example:

\n\n
var DaysEnum = {\"monday\":1, \"tuesday\":2, \"wednesday\":3, ...}\n\nDocument.Write(\"Enumerant: \" + DaysEnum.tuesday);\n
\n\n

The problem with this approach? You can accidentally redefine your enumerant, or accidentally have duplicate enumerant values. For example:

\n\n
DaysEnum.monday = 4; // whoops, monday is now thursday, too\n
\n\n

Edit

\n\n
\n

What about Artur Czajka's Object.freeze? Wouldn't that work to prevent you from setting monday to thursday? – Fry Quad

\n
\n\n

Absolutely, Object.freeze would totally fix the problem I complained about. I would like to remind everyone that when I wrote the above, Object.freeze didn't really exist.

\n\n

Now.... now it opens up some very interesting possibilities.

\n\n

Edit 2
\nHere's a very good library for creating enums.

\n\n

http://www.2ality.com/2011/10/enums.html

\n\n

While it probably doesn't fit every valid use of enums, it goes a very long way.

\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291b082fcc3049e92a20", + "creator": "Scott Evernden", + "createdAt": 1250888529000, + "text": "there is type safety in javascript ?", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a21", + "creator": "Michael", + "createdAt": 1325779103000, + "text": "@Randolpho: What about Artur Czajka's Object.freeze? Wouldn't that work to prevent you from setting monday to thursday?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e8f", + "creator": "Gareth", + "createdAt": 1226603620000, + "text": "

This isn't much of an answer, but I'd say that works just fine, personally

\n\n

Having said that, since it doesn't matter what the values are (you've used 0, 1, 2), I'd use a meaningful string in case you ever wanted to output the current value.

\n", + "upvotes": 755, + "upvoterUsernames": [], + "downvotes": 128, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e93", + "creator": "Andre 'Fi'", + "createdAt": 1310516898000, + "text": "

Here's what we all want:

\n\n
function Enum(constantsList) {\n    for (var i in constantsList) {\n        this[constantsList[i]] = i;\n    }\n}\n
\n\n

Now you can create your enums:

\n\n
var YesNo = new Enum(['NO', 'YES']);\nvar Color = new Enum(['RED', 'GREEN', 'BLUE']);\n
\n\n

By doing this, constants can be acessed in the usual way (YesNo.YES, Color.GREEN) and they get a sequential int value (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

\n\n

You can also add methods, by using Enum.prototype:

\n\n
Enum.prototype.values = function() {\n    return this.allValues;\n    /* for the above to work, you'd need to do\n            this.allValues = constantsList at the constructor */\n};\n
\n\n


\nEdit - small improvement - now with varargs: (unfortunately it doesn't work properly on IE :S... should stick with previous version then)

\n\n
function Enum() {\n    for (var i in arguments) {\n        this[arguments[i]] = i;\n    }\n}\n\nvar YesNo = new Enum('NO', 'YES');\nvar Color = new Enum('RED', 'GREEN', 'BLUE');\n
\n", + "upvotes": 118, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e92", + "creator": "Artur Czajka", + "createdAt": 1298027036000, + "text": "

Since 1.8.5 it's possible to seal and freeze the object, so define the above as:

\n
const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})\n
\n

or

\n
const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}\nObject.freeze(DaysEnum)\n
\n

and voila! JS enums.

\n

However, this doesn't prevent you from assigning an undesired value to a variable, which is often the main goal of enums:

\n
let day = DaysEnum.tuesday\nday = 298832342 // goes through without any errors\n
\n

One way to ensure a stronger degree of type safety (with enums or otherwise) is to use a tool like TypeScript or Flow.

\n

Quotes aren't needed but I kept them for consistency.

\n", + "upvotes": 1193, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3291b082fcc3049e92a25", + "creator": "saluce", + "createdAt": 1345823771000, + "text": "For backward compatibility, if (Object.freeze) { Object.freeze(DaysEnum); }", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a27", + "creator": "Dan", + "createdAt": 1363721546000, + "text": "If you did want to use the number, you can get it with DaysEnum.monday.toString().", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a28", + "creator": "Ky.", + "createdAt": 1397021755000, + "text": "@GabrielLlamas Sometimes, though, you do need the ordinal of an enumerated constant.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a2a", + "creator": "George", + "createdAt": 1523539272000, + "text": "@CasBloem const will still allow you to change the values of the object.", + "upvotes": 640, + "upvoterUsernames": [], + "downvotes": 640, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a2c", + "creator": "Aaron Franke", + "createdAt": 1575606233000, + "text": "What exactly does freeze do?", + "upvotes": 166, + "upvoterUsernames": [], + "downvotes": 166, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e95", + "creator": "Chris", + "createdAt": 1337073961000, + "text": "

This is the solution that I use.

\n\n
function Enum() {\n    this._enums = [];\n    this._lookups = {};\n}\n\nEnum.prototype.getEnums = function() {\n    return _enums;\n}\n\nEnum.prototype.forEach = function(callback){\n    var length = this._enums.length;\n    for (var i = 0; i < length; ++i){\n        callback(this._enums[i]);\n    }\n}\n\nEnum.prototype.addEnum = function(e) {\n    this._enums.push(e);\n}\n\nEnum.prototype.getByName = function(name) {\n    return this[name];\n}\n\nEnum.prototype.getByValue = function(field, value) {\n    var lookup = this._lookups[field];\n    if(lookup) {\n        return lookup[value];\n    } else {\n        this._lookups[field] = ( lookup = {});\n        var k = this._enums.length - 1;\n        for(; k >= 0; --k) {\n            var m = this._enums[k];\n            var j = m[field];\n            lookup[j] = m;\n            if(j == value) {\n                return m;\n            }\n        }\n    }\n    return null;\n}\n\nfunction defineEnum(definition) {\n    var k;\n    var e = new Enum();\n    for(k in definition) {\n        var j = definition[k];\n        e[k] = j;\n        e.addEnum(j)\n    }\n    return e;\n}\n
\n\n

And you define your enums like this:

\n\n
var COLORS = defineEnum({\n    RED : {\n        value : 1,\n        string : 'red'\n    },\n    GREEN : {\n        value : 2,\n        string : 'green'\n    },\n    BLUE : {\n        value : 3,\n        string : 'blue'\n    }\n});\n
\n\n

And this is how you access your enums:

\n\n
COLORS.BLUE.string\nCOLORS.BLUE.value\nCOLORS.getByName('BLUE').string\nCOLORS.getByValue('value', 1).string\n\nCOLORS.forEach(function(e){\n    // do what you want with e\n});\n
\n\n

I usually use the last 2 methods for mapping enums from message objects.

\n\n

Some advantages to this approach:

\n\n\n\n

Some disadvantages:

\n\n\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e94", + "creator": "Yaroslav", + "createdAt": 1335276286000, + "text": "

If you're using Backbone, you can get full-blown enum functionality (find by id, name, custom members) for free using Backbone.Collection.

\n\n
// enum instance members, optional\nvar Color = Backbone.Model.extend({\n    print : function() {\n        console.log(\"I am \" + this.get(\"name\"))\n    }\n});\n\n// enum creation\nvar Colors = new Backbone.Collection([\n    { id : 1, name : \"Red\", rgb : 0xFF0000},\n    { id : 2, name : \"Green\" , rgb : 0x00FF00},\n    { id : 3, name : \"Blue\" , rgb : 0x0000FF}\n], {\n    model : Color\n});\n\n// Expose members through public fields.\nColors.each(function(color) {\n    Colors[color.get(\"name\")] = color;\n});\n\n// using\nColors.Red.print()\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e96", + "creator": "David Miró", + "createdAt": 1365682278000, + "text": "

I've modified the solution of Andre 'Fi':

\n\n
  function Enum() {\n    var that = this;\n    for (var i in arguments) {\n        that[arguments[i]] = i;\n    }\n    this.name = function(value) {\n        for (var key in that) {\n            if (that[key] == value) {\n                return key;\n            }\n        }\n    };\n    this.exist = function(value) {\n        return (typeof that.name(value) !== \"undefined\");\n    };\n    if (Object.freeze) {\n        Object.freeze(that);\n    }\n  }\n
\n\n

Test:

\n\n
var Color = new Enum('RED', 'GREEN', 'BLUE');\nundefined\nColor.name(Color.REDs)\nundefined\nColor.name(Color.RED)\n\"RED\"\nColor.exist(Color.REDs)\nfalse\nColor.exist(Color.RED)\ntrue\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e97", + "creator": "Rob Hardy", + "createdAt": 1372090274000, + "text": "

This is an old one I know, but the way it has since been implemented via the TypeScript interface is:

\n\n
var MyEnum;\n(function (MyEnum) {\n    MyEnum[MyEnum[\"Foo\"] = 0] = \"Foo\";\n    MyEnum[MyEnum[\"FooBar\"] = 2] = \"FooBar\";\n    MyEnum[MyEnum[\"Bar\"] = 1] = \"Bar\";\n})(MyEnum|| (MyEnum= {}));\n
\n\n

This enables you to look up on both MyEnum.Bar which returns 1, and MyEnum[1] which returns \"Bar\" regardless of the order of declaration.

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291c082fcc3049e92a31", + "creator": "David Karlaš", + "createdAt": 1388914881000, + "text": "Plus MyEnum["Bar"] works which returns 1... <3 TypeScript so far...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291c082fcc3049e92a33", + "creator": "parliament", + "createdAt": 1438126974000, + "text": "and of course if you actually ARE using Typescript: enum MyEnum { Foo, Bar, Foobar }", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e98", + "creator": "Duncan", + "createdAt": 1377081273000, + "text": "

I've been playing around with this, as I love my enums. =)

\n\n

Using Object.defineProperty I think I came up with a somewhat viable solution.

\n\n

Here's a jsfiddle: http://jsfiddle.net/ZV4A6/

\n\n

Using this method.. you should (in theory) be able to call and define enum values for any object, without affecting other attributes of that object.

\n\n
Object.defineProperty(Object.prototype,'Enum', {\n    value: function() {\n        for(i in arguments) {\n            Object.defineProperty(this,arguments[i], {\n                value:parseInt(i),\n                writable:false,\n                enumerable:true,\n                configurable:true\n            });\n        }\n        return this;\n    },\n    writable:false,\n    enumerable:false,\n    configurable:false\n}); \n
\n\n

Because of the attribute writable:false this should make it type safe.

\n\n

So you should be able to create a custom object, then call Enum() on it. The values assigned start at 0 and increment per item.

\n\n
var EnumColors={};\nEnumColors.Enum('RED','BLUE','GREEN','YELLOW');\nEnumColors.RED;    // == 0\nEnumColors.BLUE;   // == 1\nEnumColors.GREEN;  // == 2\nEnumColors.YELLOW; // == 3\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291c082fcc3049e92a36", + "creator": "Duncan", + "createdAt": 1377105616000, + "text": "I didn't consider that, as it's not my normal method of doing things. But you're absolutely correct! I'll edit that in.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f3291c082fcc3049e92a37", + "creator": "ceving", + "createdAt": 1446480205000, + "text": "false is the default for writable, enumerable and configurable. No need for chewing over defaults.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e99", + "creator": "GTF", + "createdAt": 1380213997000, + "text": "

I had done it a while ago using a mixture of __defineGetter__ and __defineSetter__ or defineProperty depending on the JS version.

\n\n

Here's the enum generating function I made: https://gist.github.com/gfarrell/6716853

\n\n

You'd use it like this:

\n\n
var Colours = Enum('RED', 'GREEN', 'BLUE');\n
\n\n

And it would create an immutable string:int dictionary (an enum).

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291c082fcc3049e92a38", + "creator": "GTF", + "createdAt": 1380215029000, + "text": "Ok I rewrote it and now it is library agnostic (although it assumes some sort of AMD loader, but that can be removed).", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e9a", + "creator": "user2254487", + "createdAt": 1380618176000, + "text": "

A quick and simple way would be :

\n\n
var Colors = function(){\nreturn {\n    'WHITE':0,\n    'BLACK':1,\n    'RED':2,\n    'GREEN':3\n    }\n}();\n\nconsole.log(Colors.WHITE)  //this prints out \"0\"\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291c082fcc3049e92a3a", + "creator": "Sildoreth", + "createdAt": 1389899968000, + "text": "The function is unnecessary and gives you the exact same result as what the OP posted.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e9b", + "creator": "arcseldon", + "createdAt": 1399202042000, + "text": "

As of writing, October 2014 - so here is a contemporary solution. Am writing the solution as a Node Module, and have included a test using Mocha and Chai, as well as underscoreJS. You can easily ignore these, and just take the Enum code if preferred.

\n\n

Seen a lot of posts with overly convoluted libraries etc. The solution to getting enum support in Javascript is so simple it really isn't needed. Here is the code:

\n\n

File: enums.js

\n\n
_ = require('underscore');\n\nvar _Enum = function () {\n\n   var keys = _.map(arguments, function (value) {\n      return value;\n   });\n   var self = {\n      keys: keys\n   };\n   for (var i = 0; i < arguments.length; i++) {\n      self[keys[i]] = i;\n   }\n   return self;\n};\n\nvar fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));\nvar encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));\n\nexports.fileFormatEnum = fileFormatEnum;\nexports.encodingEnum = encodingEnum;\n
\n\n

And a test to illustrate what it gives you:

\n\n

file: enumsSpec.js

\n\n
var chai = require(\"chai\"),\n    assert = chai.assert,\n    expect = chai.expect,\n    should = chai.should(),\n    enums = require('./enums'),\n    _ = require('underscore');\n\n\ndescribe('enums', function () {\n\n    describe('fileFormatEnum', function () {\n        it('should return expected fileFormat enum declarations', function () {\n            var fileFormatEnum = enums.fileFormatEnum;\n            should.exist(fileFormatEnum);\n            assert('{\"keys\":[\"CSV\",\"TSV\"],\"CSV\":0,\"TSV\":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');\n            assert('[\"CSV\",\"TSV\"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');\n        });\n    });\n\n    describe('encodingEnum', function () {\n        it('should return expected encoding enum declarations', function () {\n            var encodingEnum = enums.encodingEnum;\n            should.exist(encodingEnum);\n            assert('{\"keys\":[\"UTF8\",\"SHIFT_JIS\"],\"UTF8\":0,\"SHIFT_JIS\":1}' === JSON.stringify(encodingEnum), 'Unexpected format');\n            assert('[\"UTF8\",\"SHIFT_JIS\"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');\n        });\n    });\n\n});\n
\n\n

As you can see, you get an Enum factory, you can get all the keys simply by calling enum.keys, and you can match the keys themselves to integer constants. And you can reuse the factory with different values, and export those generated Enums using Node's modular approach.

\n\n

Once again, if you are just a casual user, or in the browser etc, just take the factory part of the code, potentially removing underscore library too if you don't wish to use it in your code.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e9d", + "creator": "Andrew Philips", + "createdAt": 1409944147000, + "text": "

Really like what @Duncan did above, but I don't like mucking up global Object function space with Enum, so I wrote the following:

\n\n
function mkenum_1()\n{\n  var o = new Object();\n  var c = -1;\n  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};\n\n  for (i in arguments) {\n    var e = arguments[i];\n    if ((!!e) & (e.constructor == Object))\n      for (j in e)\n        f(j, (c=e[j]));\n    else\n      f(e, ++c);\n    }\n\n  return Object.freeze ? Object.freeze(o) : o;\n}\n\nvar Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');\n\nconsole.log(\"MED := \" + Sizes.MEDIUM);\nconsole.log(\"LRG := \" + Sizes.LARGE);\n\n// Output is:\n// MED := 1\n// LRG := 100\n
\n\n

@Stijin also has a neat solution (referring to his blog) which includes properties on these objects. I wrote some code for that, too, which I'm including next.

\n\n
function mkenum_2(seed)\n{\n    var p = {};\n\n    console.log(\"Seed := \" + seed);\n\n    for (k in seed) {\n        var v = seed[k];\n\n        if (v instanceof Array)\n            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };\n        else\n            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };\n    }\n    seed.properties = p;\n\n    return Object.freeze ? Object.freeze(seed) : seed;\n}\n
\n\n

This version produces an additional property list allowing friendly name conversion and short codes. I like this version because one need not duplicate data entry in properties as the code does it for you.

\n\n
var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});\nvar SizeEnum3 = mkenum_2({ SMALL: [1, \"small\", \"S\"], MEDIUM: [2, \"medium\", \"M\"], LARGE: [3, \"large\", \"L\"] });\n
\n\n

These two can be combined into a single processing unit, mkenum, (consume enums, assign values, create and add property list). However, as I've spent far too much time on this today already, I will leave the combination as an exercise for the dear reader.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e9c", + "creator": "Xeltor", + "createdAt": 1400127602000, + "text": "

your answers are far too complicated

\n\n
var buildSet = function(array) {\n  var set = {};\n  for (var i in array) {\n    var item = array[i];\n    set[item] = item;\n  }\n  return set;\n}\n\nvar myEnum = buildSet(['RED','GREEN','BLUE']);\n// myEnum.RED == 'RED' ...etc\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e9f", + "creator": "Gildas.Tambo", + "createdAt": 1424006307000, + "text": "

You can use Object.prototype.hasOwnProperty()

\n\n

\r\n
\r\n
var findInEnum,\r\n    colorEnum = {\r\n    red : 0,\r\n    green : 1,\r\n    blue : 2\r\n};\r\n\r\n// later on\r\n\r\nfindInEnum = function (enumKey) {\r\n  if (colorEnum.hasOwnProperty(enumKey)) {\r\n    return enumKey+' Value: ' + colorEnum[enumKey]\r\n  }\r\n}\r\n\r\nalert(findInEnum(\"blue\"))
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e9e", + "creator": "Shivanshu Goyal", + "createdAt": 1422495143000, + "text": "
var ColorEnum = {\n    red: {},\n    green: {},\n    blue: {}\n}\n
\n\n

You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291d082fcc3049e92a41", + "creator": "Andrew", + "createdAt": 1582143760000, + "text": "Hmm, just make sure that this code doesn't get called twice...", + "upvotes": 181, + "upvoterUsernames": [], + "downvotes": 181, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90ea1", + "creator": "Gelin Luo", + "createdAt": 1430133238000, + "text": "

I've just published an NPM package gen_enum allows you to create Enum data structure in Javascript quickly:

\n\n
var genEnum = require('gen_enum');\n\nvar AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');\nvar curMode = AppMode.LOG_IN;\nconsole.log(curMode.isLogIn()); // output true \nconsole.log(curMode.isSignUp()); // output false \nconsole.log(curMode.isForgotPassword()); // output false \n
\n\n

One nice thing about this little tool is in modern environment (including nodejs and IE 9+ browsers) the returned Enum object is immutable.

\n\n

For more information please checkout https://github.com/greenlaw110/enumjs

\n\n

Updates

\n\n

I obsolete gen_enum package and merge the function into constjs package, which provides more features including immutable objects, JSON string deserialization, string constants and bitmap generation etc. Checkout https://www.npmjs.com/package/constjs for more information

\n\n

To upgrade from gen_enum to constjs just change the statement

\n\n
var genEnum = require('gen_enum');\n
\n\n

to

\n\n
var genEnum = require('constjs').enum;\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ea0", + "creator": "Blake Bowen", + "createdAt": 1426502553000, + "text": "

Here's a couple different ways to implement TypeScript enums.

\n\n

The easiest way is to just iterate over an object, adding inverted key-value pairs to the object. The only drawback is that you must manually set the value for each member.

\n\n
function _enum(list) {       \n  for (var key in list) {\n    list[list[key] = list[key]] = key;\n  }\n  return Object.freeze(list);\n}\n\nvar Color = _enum({\n  Red: 0,\n  Green: 5,\n  Blue: 2\n});\n\n// Color → {0: \"Red\", 2: \"Blue\", 5: \"Green\", \"Red\": 0, \"Green\": 5, \"Blue\": 2}\n// Color.Red → 0\n// Color.Green → 5\n// Color.Blue → 2\n// Color[5] → Green\n// Color.Blue > Color.Green → false\n
\n\n


\nAnd here's a lodash mixin to create an enum using a string. While this version is a little bit more involved, it does the numbering automatically for you. All the lodash methods used in this example have a regular JavaScript equivalent, so you can easily switch them out if you want.

\n\n
function enum() {\n    var key, val = -1, list = {};\n    _.reduce(_.toArray(arguments), function(result, kvp) {    \n        kvp = kvp.split(\"=\");\n        key = _.trim(kvp[0]);\n        val = _.parseInt(kvp[1]) || ++val;            \n        result[result[val] = key] = val;\n        return result;\n    }, list);\n    return Object.freeze(list);\n}    \n\n// Add enum to lodash \n_.mixin({ \"enum\": enum });\n\nvar Color = _.enum(\n    \"Red\",\n    \"Green\",\n    \"Blue = 5\",\n    \"Yellow\",\n    \"Purple = 20\",\n    \"Gray\"\n);\n\n// Color.Red → 0\n// Color.Green → 1\n// Color.Blue → 5\n// Color.Yellow → 6\n// Color.Purple → 20\n// Color.Gray → 21\n// Color[5] → Blue\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ea2", + "creator": "Pylinux", + "createdAt": 1430806854000, + "text": "
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });\n
\n\n

You don't need to specify an id, you can just use an empty object to compare enums.

\n\n
if (incommingEnum === DaysEnum.monday) //incommingEnum is monday\n
\n\n

EDIT: If you are going to serialize the object (to JSON for instance) you'll the id again.

\n\n\n", + "upvotes": 1048, + "upvoterUsernames": [], + "downvotes": 1048, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291d082fcc3049e92a45", + "creator": "Little Alien", + "createdAt": 1478122236000, + "text": "{red:{green:{blue:{}}}}.unwindme()!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90ea3", + "creator": "Vitalii Fedorenko", + "createdAt": 1430843531000, + "text": "

In most modern browsers, there is a symbol primitive data type which can be used to create an enumeration. It will ensure type safety of the enum as each symbol value is guaranteed by JavaScript to be unique, i.e. Symbol() != Symbol(). For example:

\n\n
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});\n
\n\n

To simplify debugging, you can add a description to enum values:

\n\n
const COLOR = Object.freeze({RED: Symbol(\"RED\"), BLUE: Symbol(\"BLUE\")});\n
\n\n

Plunker demo

\n\n

On GitHub you can find a wrapper that simplifies the code required to initialize the enum:

\n\n
const color = new Enum(\"RED\", \"BLUE\")\n\ncolor.RED.toString() // Symbol(RED)\ncolor.getName(color.RED) // RED\ncolor.size // 2\ncolor.values() // Symbol(RED), Symbol(BLUE)\ncolor.toString() // RED,BLUE\n
\n", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291d082fcc3049e92a48", + "creator": "vbraun", + "createdAt": 1434442477000, + "text": "This is the correct answer in theory. In practice, 2015 browser support is far from sufficient. Not production ready by far.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f3291d082fcc3049e92a4a", + "creator": "rvighne", + "createdAt": 1468363788000, + "text": "Though browser support isn't there yet, this is the best answer since this is close to what Symbol is intended for.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291d082fcc3049e92a4c", + "creator": "Andy", + "createdAt": 1532477361000, + "text": "Meh...enum values often need to be serializable though, and Symbols aren't so handy to serialize and deserialize.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90ea4", + "creator": "Manohar Reddy Poreddy", + "createdAt": 1435842197000, + "text": "

IE8 does Not support freeze() method.
\nSource: http://kangax.github.io/compat-table/es5/, Click on \"Show obsolete browsers?\" on top, and check IE8 & freeze row col intersection.

\n\n

In my current game project, I have used below, since few customers still use IE8:

\n\n
var CONST_WILD_TYPES = {\n    REGULAR: 'REGULAR',\n    EXPANDING: 'EXPANDING',\n    STICKY: 'STICKY',\n    SHIFTING: 'SHIFTING'\n};\n
\n\n

We could also do:

\n\n
var CONST_WILD_TYPES = {\n    REGULAR: 'RE',\n    EXPANDING: 'EX',\n    STICKY: 'ST',\n    SHIFTING: 'SH'\n};\n
\n\n

or even this:

\n\n
var CONST_WILD_TYPES = {\n    REGULAR: '1',\n    EXPANDING: '2',\n    STICKY: '3',\n    SHIFTING: '4'\n};\n
\n\n

The last one, seems most efficient for string, it reduces your total bandwidth if you have server & client exchanging this data.
\nOf course, now it's your duty to make sure there are no conflicts in the data (RE, EX, etc. must be unique, also 1, 2, etc. should be unique). Note that you need to maintain these forever for backward compatibility.

\n\n

Assignment:

\n\n
var wildType = CONST_WILD_TYPES.REGULAR;\n
\n\n

Comparision:

\n\n
if (wildType === CONST_WILD_TYPES.REGULAR) {\n    // do something here\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ea5", + "creator": "hvdd", + "createdAt": 1437912190000, + "text": "

Create an object literal:

\n\n
const Modes = {\n  DRAGGING: 'drag',\n  SCALING:  'scale',\n  CLICKED:  'click'\n};\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291d082fcc3049e92a4d", + "creator": "Jack G", + "createdAt": 1538918462000, + "text": "Please do not use Object.freeze. It prevent Closure Compiler from inlining the object.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90ea6", + "creator": "Jules Sam. Randolph", + "createdAt": 1445745807000, + "text": "

I wrote enumerationjs a very tiny library to address the issue which ensures type safety, allow enum constants to inherit from a prototype, guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

\n\n
var CloseEventCodes = new Enumeration(\"closeEventCodes\", {\n  CLOSE_NORMAL:          { _id: 1000, info: \"Connection closed normally\" },\n  CLOSE_GOING_AWAY:      { _id: 1001, info: \"Connection closed going away\" },\n  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: \"Connection closed due to protocol error\"  },\n  CLOSE_UNSUPPORTED:     { _id: 1003, info: \"Connection closed due to unsupported operation\" },\n  CLOSE_NO_STATUS:       { _id: 1005, info: \"Connection closed with no status\" },\n  CLOSE_ABNORMAL:        { _id: 1006, info: \"Connection closed abnormally\" },\n  CLOSE_TOO_LARGE:       { _id: 1009, info: \"Connection closed due to too large packet\" }\n},{ talk: function(){\n    console.log(this.info); \n  }\n});\n\n\nCloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints \"Connection closed due to too large packet\"\nCloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true\n
\n\n

Enumeration is basically a factory.

\n\n

Fully documented guide available here. Hope this helps.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291d082fcc3049e92a50", + "creator": "user1228", + "createdAt": 1468516999000, + "text": "That's not really an enumeration. But I can see this being very useful.", + "upvotes": 155, + "upvoterUsernames": [], + "downvotes": 155, + "downvoterUsernames": [] + }, + { + "_id": "62f3291d082fcc3049e92a52", + "creator": "Jules Sam. Randolph", + "createdAt": 1468519677000, + "text": "@Will, how would you call them^^ ? AugmentedEnum ?", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + }, + { + "_id": "62f3291d082fcc3049e92a54", + "creator": "Jack G", + "createdAt": 1529796441000, + "text": "Very ill performant. Makes all code slower. Makes minified code several times bigger. Very bad.", + "upvotes": 1181, + "upvoterUsernames": [], + "downvotes": 1181, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90ea7", + "creator": "Muhammad Awais", + "createdAt": 1462962696000, + "text": "

You can try this:

\n\n
   var Enum = Object.freeze({\n            Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),\n            Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })\n            });\n\n    alert(Enum.Role.Supervisor);\n    alert(Enum.Color.GREEN);\n    var currentColor=0;\n    if(currentColor == Enum.Color.RED) {\n       alert('Its Red');\n    }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ea8", + "creator": "LNT", + "createdAt": 1464157945000, + "text": "

You can do something like this

\n\n
    var Enum = (function(foo) {\n\n    var EnumItem = function(item){\n        if(typeof item == \"string\"){\n            this.name = item;\n        } else {\n            this.name = item.name;\n        }\n    }\n    EnumItem.prototype = new String(\"DEFAULT\");\n    EnumItem.prototype.toString = function(){\n        return this.name;\n    }\n    EnumItem.prototype.equals = function(item){\n        if(typeof item == \"string\"){\n            return this.name == item;\n        } else {\n            return this == item && this.name == item.name;\n        }\n    }\n\n    function Enum() {\n        this.add.apply(this, arguments);\n        Object.freeze(this);\n    }\n    Enum.prototype.add = function() {\n        for (var i in arguments) {\n            var enumItem = new EnumItem(arguments[i]);\n            this[enumItem.name] = enumItem;\n        }\n    };\n    Enum.prototype.toList = function() {\n        return Object.keys(this);\n    };\n    foo.Enum = Enum;\n    return Enum;\n})(this);\nvar STATUS = new Enum(\"CLOSED\",\"PENDING\", { name : \"CONFIRMED\", ackd : true });\nvar STATE = new Enum(\"CLOSED\",\"PENDING\",\"CONFIRMED\",{ name : \"STARTED\"},{ name : \"PROCESSING\"});\n
\n\n

As defined in this library.\nhttps://github.com/webmodule/foo/blob/master/foo.js#L217

\n\n

Complete example\nhttps://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eaa", + "creator": "Little Alien", + "createdAt": 1478125201000, + "text": "

The Alien solution is to make things as simple as possible:

\n\n
    \n
  1. use enum keyword (reserved in javascript)
  2. \n
  3. If enum keyword is just reserved but not implemented in your javascript, define the following

    \n\n
    const enumerate = spec => spec.split(/\\s*,\\s*/)\n  .reduce((e, n) => Object.assign(e,{[n]:n}), {}) \n
  4. \n
\n\n

Now, you can easily use it

\n\n
const kwords = enumerate(\"begin,end, procedure,if\")\nconsole.log(kwords, kwords.if, kwords.if == \"if\", kwords.undef)\n
\n\n

I see no reason to make the enum values explicit variables. The scripts are morphic anyway and it makes no difference if part of your code is a string or valid code. What really matters is that you do not need to deal with tons of quotation marks whenever use or define them.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ea9", + "creator": "Ilya Gazman", + "createdAt": 1471437525000, + "text": "

Simplest solution:

\n\n

Create

\n\n
var Status = Object.freeze({\n    \"Connecting\":0,\n    \"Ready\":1,\n    \"Loading\":2,\n    \"Processing\": 3\n});\n
\n\n

Get Value

\n\n
console.log(Status.Ready) // 1\n
\n\n

Get Key

\n\n
console.log(Object.keys(Status)[Status.Ready]) // Ready\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eac", + "creator": "mika", + "createdAt": 1491984804000, + "text": "

You could try using https://bitbucket.org/snippets/frostbane/aAjxM.

\n\n
my.namespace.ColorEnum = new Enum(\n    \"RED = 0\",\n    \"GREEN\",\n    \"BLUE\"\n)\n
\n\n

It should work up to ie8.

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eab", + "creator": "Abdennour TOUMI", + "createdAt": 1483686479000, + "text": "

In ES7 , you can do an elegant ENUM relying on static attributes:

\n\n
class ColorEnum  {\n    static RED = 0 ;\n    static GREEN = 1;\n    static BLUE = 2;\n}\n
\n\n

then

\n\n
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}\n
\n\n

The advantage ( of using class instead of literal object) is to have a parent class Enum then all your Enums will extends that class.

\n\n
 class ColorEnum  extends Enum {/*....*/}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291e082fcc3049e92a58", + "creator": "Jon G", + "createdAt": 1485943042000, + "text": "Could you explain why having a parent class is an advantage, please? I feel like I'm missing something!", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3291e082fcc3049e92a5a", + "creator": "Bergi", + "createdAt": 1496972594000, + "text": "Don't do that. new ColorEnum() makes absolutely no sense.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f3291e082fcc3049e92a5b", + "creator": "Codii", + "createdAt": 1499347812000, + "text": "extending an enum sounds crazy, really", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3291e082fcc3049e92a5d", + "creator": "Idemax", + "createdAt": 1507275098000, + "text": "once the language doesnt support it natively would make sense keep this convention and use like this! i agree!", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90eaf", + "creator": "Julius Dzidzevičius", + "createdAt": 1525174372000, + "text": "

This is how Typescript translates it's enum into Javascript:

\n\n
var makeEnum = function(obj) {\n    obj[ obj['Active'] = 1 ] = 'Active';\n    obj[ obj['Closed'] = 2 ] = 'Closed';\n    obj[ obj['Deleted'] = 3 ] = 'Deleted';\n}\n
\n\n

Now:

\n\n
makeEnum( NewObj = {} )\n// => {1: \"Active\", 2: \"Closed\", 3: \"Deleted\", Active: 1, Closed: 2, Deleted: 3}\n
\n\n

At first I was confused why obj[1] returns 'Active', but then realised that its dead simple - Assignment operator assigns value and then returns it:

\n\n
obj['foo'] = 1\n// => 1\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90ead", + "creator": "David Lemon", + "createdAt": 1501067622000, + "text": "

You can use a simple funcion to invert keys and values, it will work with arrays also as it converts numerical integer strings to numbers. The code is small, simple and reusable for this and other use cases.

\n\n

\r\n
\r\n
var objInvert = function (obj) {\r\n    var invert = {}\r\n    for (var i in obj) {\r\n      if (i.match(/^\\d+$/)) i = parseInt(i,10)\r\n      invert[obj[i]] = i\r\n    }\r\n    return invert\r\n}\r\n \r\nvar musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',\r\n'BOSSA-NOVA','POP','INDIE']))\r\n\r\nconsole.log(musicStyles)
\r\n
\r\n
\r\n

\n", + "upvotes": 625, + "upvoterUsernames": [], + "downvotes": 625, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb0", + "creator": "jamess", + "createdAt": 1535218071000, + "text": "

This answer is an alternative approach for specific circumstances. I needed a set of bitmask constants based on attribute sub-values (cases where an attribute value is an array or list of values). It encompasses the equivalent of several overlapping enums.

\n\n

I created a class to both store and generate the bitmask values. I can then use the pseudo-constant bitmask values this way to test, for example, if green is present in an RGB value:

\n\n
if (value & Ez.G) {...}\n
\n\n

In my code I create only one instance of this class. There doesn't seem to be a clean way to do this without instantiating at least one instance of the class. Here is the class declaration and bitmask value generation code:

\n\n
class Ez {\nconstructor() {\n    let rgba = [\"R\", \"G\", \"B\", \"A\"];\n    let rgbm = rgba.slice();\n    rgbm.push(\"M\");              // for feColorMatrix values attribute\n    this.createValues(rgba);\n    this.createValues([\"H\", \"S\", \"L\"]);\n    this.createValues([rgba, rgbm]);\n    this.createValues([attX, attY, attW, attH]);\n}\ncreateValues(a) {                // a for array\n    let i, j;\n    if (isA(a[0])) {             // max 2 dimensions\n        let k = 1;\n        for (i of a[0]) {\n            for (j of a[1]) {\n                this[i + j] = k;\n                k *= 2;\n            }\n        }\n    }\n    else {                       // 1D array is simple loop\n        for (i = 0, j = 1; i < a.length; i++, j *= 2)\n            this[a[i]] = j;\n   }\n}\n
\n\n

The 2D array is for the SVG feColorMatrix values attribute, which is a 4x5 matrix of RGBA by RGBAM, where M is a multiplier. The resulting Ez properties are Ez.RR, Ez.RG, etc.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eae", + "creator": "Joseph Merdrignac", + "createdAt": 1518651874000, + "text": "

es7 way, (iterator, freeze), usage:

\n\n
const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')\n\nfor (let name of ThreeWiseMen)\n    console.log(name)\n\n\n// with a given key\nlet key = ThreeWiseMen.Melchior\n\nconsole.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)\n\nfor (let entry from key.enum)\n     console.log(entry)\n\n\n// prevent alteration (throws TypeError in strict mode)\nThreeWiseMen.Me = 'Me too!'\nThreeWiseMen.Melchior.name = 'Foo'\n
\n\n

code:

\n\n
class EnumKey {\n\n    constructor(props) { Object.freeze(Object.assign(this, props)) }\n\n    toString() { return this.name }\n\n}\n\nexport class Enum {\n\n    constructor(...keys) {\n\n        for (let [index, key] of keys.entries()) {\n\n            Object.defineProperty(this, key, {\n\n                value: new EnumKey({ name:key, index, enum:this }),\n                enumerable: true,\n\n            })\n\n        }\n\n        Object.freeze(this)\n\n    }\n\n    *[Symbol.iterator]() {\n\n        for (let key of Object.keys(this))\n            yield this[key]\n\n    }\n\n    toString() { return [...this].join(', ') }\n\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb1", + "creator": "papiro", + "createdAt": 1537369868000, + "text": "
class Enum {\n  constructor (...vals) {\n    vals.forEach( val => {\n      const CONSTANT = Symbol(val);\n      Object.defineProperty(this, val.toUpperCase(), {\n        get () {\n          return CONSTANT;\n        },\n        set (val) {\n          const enum_val = \"CONSTANT\";\n          // generate TypeError associated with attempting to change the value of a constant\n          enum_val = val;\n        }\n      });\n    });\n  }\n}\n
\n\n

Example of usage:

\n\n
const COLORS = new Enum(\"red\", \"blue\", \"green\");\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb2", + "creator": "oluckyman", + "createdAt": 1555355761000, + "text": "

Read all the answers and didn't found any non-verbose and DRY solution.\nI use this one-liner:

\n\n
const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});\n
\n\n

it generates an object with human-readable values:

\n\n
{\n  DRAW: 'DRAW',\n  SCALE: 'SCALE',\n  DRAG: 'DRAG'\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291f082fcc3049e92a63", + "creator": "mattsven", + "createdAt": 1555366578000, + "text": "Fair solution, though you'd lose syntax hinting in a code editor", + "upvotes": 1143, + "upvoterUsernames": [], + "downvotes": 1143, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90eb4", + "creator": "Aral Roca", + "createdAt": 1592042812000, + "text": "

This can be useful:

\n
const [CATS, DOGS, BIRDS] = ENUM();\n
\n

The implementation is simple and efficient:

\n
function * ENUM(count=1) { while(true) yield count++ }\n
\n

A generator can yield the exact sequence of integers required, without knowing how many constants there are. It can also support an optional argument that specifies which (possibly negative) number to start from (defaulting to 1).

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291f082fcc3049e92a66", + "creator": "Bergi", + "createdAt": 1598228788000, + "text": "@Carl Smith I might have missed some comments, but that's a quite substantial edit?!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90eb3", + "creator": "Andrew", + "createdAt": 1582147275000, + "text": "

I wasn't satisfied with any of the answers, so I made Yet Another Enum (YEA!).

\n\n

This implementation:

\n\n\n\n

Special thanks to Andre 'Fi''s answer for some inspiration.

\n\n
\n\n

The codes:

\n\n
class Enums {\n  static create({ name = undefined, items = [] }) {\n    let newEnum = {};\n    newEnum.length = items.length;\n    newEnum.items = items;\n    for (let itemIndex in items) {\n      //Map by name.\n      newEnum[items[itemIndex]] = parseInt(itemIndex, 10);\n      //Map by index.\n      newEnum[parseInt(itemIndex, 10)] = items[itemIndex];\n    }\n    newEnum.toString = Enums.enumToString.bind(newEnum);\n    newEnum.valueOf = newEnum.toString;\n    //Optional naming and global registration.\n    if (name != undefined) {\n      newEnum.name = name;\n      Enums[name] = newEnum;\n    }\n    //Prevent modification of the enum object.\n    Object.freeze(newEnum);\n    return newEnum;\n  }\n  static enumToString() {\n    return \"Enum \" +\n      (this.name != undefined ? this.name + \" \" : \"\") +\n      \"[\" + this.items.toString() + \"]\";\n  }\n}\n
\n\n
\n\n

Usage:

\n\n
let colors = Enums.create({\n  name: \"COLORS\",\n  items: [ \"RED\", \"GREEN\", \"BLUE\", \"PORPLE\" ]\n});\n\n//Global access, if named.\nEnums.COLORS;\n\ncolors.items; //Array(4) [ \"RED\", \"GREEN\", \"BLUE\", \"PORPLE\" ]\ncolors.length; //4\n\ncolors.RED; //0\ncolors.GREEN; //1\ncolors.BLUE; //2\ncolors.PORPLE; //3\ncolors[0]; //\"RED\"\ncolors[1]; //\"GREEN\"\ncolors[2]; //\"BLUE\"\ncolors[3]; //\"PORPLE\"\n\ncolors.toString(); //\"Enum COLORS [RED,GREEN,BLUE,PORPLE]\"\n\n//Enum frozen, makes it a real enum.\ncolors.RED = 9001;\ncolors.RED; //0\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb6", + "creator": "dsanchez", + "createdAt": 1603043108000, + "text": "

Update 05.11.2020:
\nModified to include static fields and methods to closer replicate "true" enum behavior.

\n

Has anyone tried doing this with a class that contains private fields and "get" accessors?\nI realize private class fields are still experimental at this point but it seems to work for the purposes of creating a class with immutable fields/properties. Browser support is decent as well. The only "major" browsers that don't support it are Firefox (which I'm sure they will soon) and IE (who cares).

\n

DISCLAIMER:
\nI am not a developer. I was just looking for an answer to this question and started thinking about how I sometimes create "enhanced" enums in C# by creating classes with private fields and restricted property accessors.

\n

Sample Class

\n
class Sizes {\n    // Private Fields\n    static #_SMALL = 0;\n    static #_MEDIUM = 1;\n    static #_LARGE = 2;\n\n    // Accessors for "get" functions only (no "set" functions)\n    static get SMALL() { return this.#_SMALL; }\n    static get MEDIUM() { return this.#_MEDIUM; }\n    static get LARGE() { return this.#_LARGE; }\n}\n
\n

You should now be able to call your enums directly.

\n
Sizes.SMALL; // 0\nSizes.MEDIUM; // 1\nSizes.LARGE; // 2\n
\n

The combination of using private fields and limited accessors means that the enum values are well protected.

\n
Sizes.SMALL = 10 // Sizes.SMALL is still 0\nSizes._SMALL = 10 // Sizes.SMALL is still 0\nSizes.#_SMALL = 10 // Sizes.SMALL is still 0\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291f082fcc3049e92a67", + "creator": "beruic", + "createdAt": 1606127072000, + "text": "This doesn't seem to provide more protection than what Object.freeze() does, and there is a lot of redundant code.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90eb5", + "creator": "Idan", + "createdAt": 1594885309000, + "text": "
export const ButtonType = Object.freeze({ \n   DEFAULT: 'default', \n   BIG: 'big', \n   SMALL: 'small'\n})\n
\n

source: https://medium.com/@idanlevi2/enum-in-javascript-5f2ff500f149

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb7", + "creator": "KooiInc", + "createdAt": 1604230259000, + "text": "

Here's my take on a (flagged) Enum factory. Here's a working demo.

\n
/*\n * Notes: \n * The proxy handler enables case insensitive property queries\n * BigInt is used to enable bitflag strings /w length > 52\n*/\nfunction EnumFactory() {\n  const proxyfy = {\n    construct(target, args) { \n      const caseInsensitiveHandler = { \n          get(target, key) {\n          return target[key.toUpperCase()] || target[key];  \n        } \n      };\n      const proxified = new Proxy(new target(...args), caseInsensitiveHandler ); \n      return Object.freeze(proxified);\n    },\n  }\n  const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);\n  const throwIf = (\n      assertion = false, \n      message = `Unspecified error`, \n      ErrorType = Error ) => \n      assertion && (() => { throw new ErrorType(message); })();\n  const hasFlag = (val, sub) => {\n    throwIf(!val || !sub, "valueIn: missing parameters", RangeError);\n    const andVal = (sub & val);\n    return andVal !== BigInt(0) && andVal === val;\n  };\n\n  function EnumCtor(values) {\n    throwIf(values.constructor !== Array || \n            values.length < 2 || \n        values.filter( v => v.constructor !== String ).length > 0,\n      `EnumFactory: expected Array of at least 2 strings`, TypeError);\n    const base = BigInt(1);\n    this.NONE = BigInt(0);\n    values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );\n  }\n\n  EnumCtor.prototype = {\n    get keys() { return Object.keys(this).slice(1); },\n    subset(sub) {\n      const arrayValues = this.keys;\n      return new ProxiedEnumCtor(\n        [...sub.toString(2)].reverse()\n          .reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )\n      );\n    },\n    getLabel(enumValue) {\n      const tryLabel = Object.entries(this).find( value => value[1] === enumValue );\n      return !enumValue || !tryLabel.length ? \n        "getLabel: no value parameter or value not in enum" :\n        tryLabel.shift();\n    },\n    hasFlag(val, sub = this) { return hasFlag(val, sub); },\n  };\n  \n  return arr => new ProxiedEnumCtor(arr);\n}\n
\n", + "upvotes": 341, + "upvoterUsernames": [], + "downvotes": 341, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90eb8", + "creator": "LEMUEL ADANE", + "createdAt": 1646959894000, + "text": "

You just need to make an immutable object by using Object.freeze(<your_object>):

\n
export const ColorEnum = Object.freeze({\n    // you can only change the property values here\n    // in the object declaration like in the Java enumaration\n    RED: 0,\n    GREEN: 1,\n    BLUE: 2,\n});\n\nColorEnum.RED = 22    // assigning here will throw an error\nColorEnum.VIOLET = 45 // even adding a new property will throw an error\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 6, + "commentItems": [ + { + "_id": "62f321cc082fcc3049e90e66", + "creator": "sdm350", + "createdAt": 1424814024000, + "text": "@matsko isn't that just an argument against using ==?", + "upvotes": 272, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [] + }, + { + "_id": "62f321cc082fcc3049e90e67", + "creator": "mcont", + "createdAt": 1428073090000, + "text": "0 == null returns false", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321cc082fcc3049e90e68", + "creator": "aaaaaa", + "createdAt": 1458765135000, + "text": "The double equality matrix is more confusing than microsoft word's auto-formatting", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f321cc082fcc3049e90e69", + "creator": "Greener", + "createdAt": 1464990566000, + "text": "Why isn't the var keyword used before the object name?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321cc082fcc3049e90e6a", + "creator": "Stijn de Witt", + "createdAt": 1467247240000, + "text": "@greener OP is showing how he is adding ColorEnum to the existing 'namespace' object my.namespace.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321cc082fcc3049e90e6b", + "creator": "vbullinger", + "createdAt": 1549657152000, + "text": "You say "+null == 0" as if that's a point to use triple =, @aaaaaa? Well... +null === 0, as well...", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1263517, + "uvac": 1263559 + } + }, + { + "_id": "62f321bb082fcc3049e8feff", + "title": "How to get the children of the $(this) selector?", + "title-lowercase": "how to get the children of the $(this) selector?", + "creator": "Alex", + "createdAt": 1227210245000, + "status": "open", + "text": "

I have a layout similar to this:

\n\n
<div id=\"...\"><img src=\"...\"></div>\n
\n\n

and would like to use a jQuery selector to select the child img inside the div on click.

\n\n

To get the div, I've got this selector:

\n\n
$(this)\n
\n\n

How can I get the child img using a selector?

\n", + "upvotes": 3524, + "upvoterUsernames": [], + "downvotes": 1160, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1935844, + "answers": 19, + "answerItems": [ + { + "_id": "62f321cc082fcc3049e90e53", + "creator": "Maxam", + "createdAt": 1227210692000, + "text": "

Try this code:

\n\n
$(this).children()[0]\n
\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328da082fcc3049e929ba", + "creator": "BoltClock", + "createdAt": 1371494436000, + "text": "@rémy: That's not even valid syntax. (Took all of 2 years for anyone to notice...)", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e55", + "creator": "philnash", + "createdAt": 1227216195000, + "text": "

You could also use

\n\n
$(this).find('img');\n
\n\n

which would return all imgs that are descendants of the div

\n", + "upvotes": 665, + "upvoterUsernames": [], + "downvotes": 180, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e54", + "creator": "Adam", + "createdAt": 1227210980000, + "text": "

Without knowing the ID of the DIV I think you could select the IMG like this:

\n\n
$(\"#\"+$(this).attr(\"id\")+\" img:first\")\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328da082fcc3049e929bf", + "creator": "Scott Evernden", + "createdAt": 1239237577000, + "text": "this probably actually works but it's kinda the Rube Goldberg answer :)", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e57", + "creator": "Roccivic", + "createdAt": 1308662745000, + "text": "

If your DIV tag is immediately followed by the IMG tag, you can also use:

\n\n
$(this).next();\n
\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e56", + "creator": "Simon", + "createdAt": 1227216449000, + "text": "

The jQuery constructor accepts a 2nd parameter called context which can be used to override the context of the selection.

\n\n
jQuery(\"img\", this);\n
\n\n

Which is the same as using .find() like this:

\n\n
jQuery(this).find(\"img\");\n
\n\n

If the imgs you desire are only direct descendants of the clicked element, you can also use .children():

\n\n
jQuery(this).children(\"img\");\n
\n", + "upvotes": 3279, + "upvoterUsernames": [], + "downvotes": 328, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e59", + "creator": "Rayron Victor", + "createdAt": 1342469271000, + "text": "

The direct children is

\n\n
$('> .child-class', this)\n
\n", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e58", + "creator": "rakslice", + "createdAt": 1311274051000, + "text": "

If you need to get the first img that's down exactly one level, you can do

\n\n
$(this).children(\"img:first\")\n
\n", + "upvotes": 253, + "upvoterUsernames": [], + "downvotes": 108, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5a", + "creator": "Lalit Kumar Maurya", + "createdAt": 1363939527000, + "text": "

You can find all img element of parent div like below

\n
$(this).find('img') or $(this).children('img')\n
\n

If you want a specific img element you can write like this

\n
$(this).children('img:nth(n)')  \n// where n is the child place in parent list start from 0 onwards\n
\n

Your div contains only one img element. So for this below is right

\n
 $(this).find("img").attr("alt")\n                  OR\n  $(this).children("img").attr("alt")\n
\n

But if your div contain more img element like below

\n
<div class="mydiv">\n    <img src="test.png" alt="3">\n    <img src="test.png" alt="4">\n</div>\n
\n

then you can't use upper code to find alt value of second img element. So you can try this:

\n
 $(this).find("img:last-child").attr("alt")\n                   OR\n $(this).children("img:last-child").attr("alt")\n
\n

This example shows a general idea that how you can find actual objects within the parent object.\nYou can use classes to differentiate your child's object. That is easy and fun. i.e.

\n
<div class="mydiv">\n    <img class='first' src="test.png" alt="3">\n    <img class='second' src="test.png" alt="4">\n</div>\n
\n

You can do this as below :

\n
 $(this).find(".first").attr("alt")\n
\n

and more specific as:

\n
 $(this).find("img.first").attr("alt")\n
\n

You can use find or children as above code. For more visit Children http://api.jquery.com/children/ and Find http://api.jquery.com/find/.\nSee example http://jsfiddle.net/lalitjs/Nx8a6/

\n", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5b", + "creator": "Thirumalai murugan", + "createdAt": 1372232536000, + "text": "

jQuery's each is one option:

\n\n
<div id=\"test\">\n    <img src=\"testing.png\"/>\n    <img src=\"testing1.png\"/>\n</div>\n\n$('#test img').each(function(){\n    console.log($(this).attr('src'));\n});\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5c", + "creator": "Dennis R", + "createdAt": 1406325400000, + "text": "

You can use Child Selecor to reference the child elements available within the parent.

\n\n
$(' > img', this).attr(\"src\");\n
\n\n

And the below is if you don't have reference to $(this) and you want to reference img available within a div from other function.

\n\n
 $('#divid > img').attr(\"src\");\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5d", + "creator": "Oskar", + "createdAt": 1422183103000, + "text": "

Ways to refer to a child in jQuery. I summarized it in the following jQuery:

\n\n
$(this).find(\"img\"); // any img tag child or grandchild etc...   \n$(this).children(\"img\"); //any img tag child that is direct descendant \n$(this).find(\"img:first\") //any img tag first child or first grandchild etc...\n$(this).children(\"img:first\") //the first img tag  child that is direct descendant \n$(this).children(\"img:nth-child(1)\") //the img is first direct descendant child\n$(this).next(); //the img is first direct descendant child\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5e", + "creator": "tetutato", + "createdAt": 1434845729000, + "text": "

Also this should work:

\n\n
$(\"#id img\")\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e5f", + "creator": "Mike Clark", + "createdAt": 1439551797000, + "text": "

You can use either of the following methods:

\n\n

1 find():

\n\n
$(this).find('img');\n
\n\n

2 children():

\n\n
$(this).children('img');\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e61", + "creator": "Sumit Lahiri", + "createdAt": 1488720453000, + "text": "

\r\n
\r\n
$(document).ready(function() {\r\n  // When you click the DIV, you take it with \"this\"\r\n  $('#my_div').click(function() {\r\n    console.info('Initializing the tests..');\r\n    console.log('Method #1: '+$(this).children('img'));\r\n    console.log('Method #2: '+$(this).find('img'));\r\n    // Here, i'm selecting the first ocorrence of <IMG>\r\n    console.log('Method #3: '+$(this).find('img:eq(0)'));\r\n  });\r\n});
\r\n
.the_div{\r\n  background-color: yellow;\r\n  width: 100%;\r\n  height: 200px;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n<div id=\"my_div\" class=\"the_div\">\r\n  <img src=\"...\">\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e60", + "creator": "RPichioli", + "createdAt": 1482948516000, + "text": "

Here's a functional code, you can run it (it's a simple demonstration).

\n\n

When you click the DIV you get the image from some different methods, in this situation \"this\" is the DIV.

\n\n

\r\n
\r\n
$(document).ready(function() {\r\n  // When you click the DIV, you take it with \"this\"\r\n  $('#my_div').click(function() {\r\n    console.info('Initializing the tests..');\r\n    console.log('Method #1: '+$(this).children('img'));\r\n    console.log('Method #2: '+$(this).find('img'));\r\n    // Here, i'm selecting the first ocorrence of <IMG>\r\n    console.log('Method #3: '+$(this).find('img:eq(0)'));\r\n  });\r\n});
\r\n
.the_div{\r\n  background-color: yellow;\r\n  width: 100%;\r\n  height: 200px;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n<div id=\"my_div\" class=\"the_div\">\r\n  <img src=\"...\">\r\n</div>
\r\n
\r\n
\r\n

\n\n

Hope it helps!

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e63", + "creator": "Hassan Fayyaz", + "createdAt": 1568877503000, + "text": "

You could use

\n\n
    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\">\n $(this).find('img');\n</script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e62", + "creator": "Jason Williams", + "createdAt": 1503497310000, + "text": "

You may have 0 to many <img> tags inside of your <div>.

\n\n

To find an element, use a .find().

\n\n

To keep your code safe, use a .each().

\n\n

Using .find() and .each() together prevents null reference errors in the case of 0 <img> elements while also allowing for handling of multiple <img> elements.

\n\n

\r\n
\r\n
// Set the click handler on your div\r\n$(\"body\").off(\"click\", \"#mydiv\").on(\"click\", \"#mydiv\", function() {\r\n\r\n  // Find the image using.find() and .each()\r\n  $(this).find(\"img\").each(function() {\r\n  \r\n        var img = this;  // \"this\" is, now, scoped to the image element\r\n        \r\n        // Do something with the image\r\n        $(this).animate({\r\n          width: ($(this).width() > 100 ? 100 : $(this).width() + 100) + \"px\"\r\n        }, 500);\r\n        \r\n  });\r\n  \r\n});
\r\n
#mydiv {\r\n  text-align: center;\r\n  vertical-align: middle;\r\n  background-color: #000000;\r\n  cursor: pointer;\r\n  padding: 50px;\r\n  \r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\r\n\r\n<div id=\"mydiv\">\r\n  <img src=\"\" width=\"100\" height=\"100\"/>\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e64", + "creator": "Kamil Kiełczewski", + "createdAt": 1588840731000, + "text": "

If your img is exactly first element inside div then try

\n\n
$(this.firstChild);\n
\n\n

\r\n
\r\n
$( \"#box\" ).click( function() {\r\n  let img = $(this.firstChild);\r\n  console.log({img});\r\n})
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n\r\n<div id=\"box\"><img src=\"https://picsum.photos/seed/picsum/300/150\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e65", + "creator": "Vishnu Prasanth G", + "createdAt": 1616742629000, + "text": "

With native javascript you can use

\n

if you've more than one image tag then use

\n

this.querySelectorAll("img")

\n

if only one image tag then us

\n

this.querySelector("img")

\n", + "upvotes": 624, + "upvoterUsernames": [], + "downvotes": 624, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1939368, + "uvac": 1939387 + } + }, + { + "_id": "62f321bb082fcc3049e8fee9", + "title": "Encode URL in JavaScript?", + "title-lowercase": "encode url in javascript?", + "creator": "nickf", + "createdAt": 1228185428000, + "status": "open", + "text": "

How do you safely encode a URL using JavaScript such that it can be put into a GET string?

\n\n
var myUrl = \"http://example.com/index.html?param=1&anotherParam=2\";\nvar myOtherUrl = \"http://example.com/index.html?url=\" + myUrl;\n
\n\n

I assume that you need to encode the myUrl variable on that second line?

\n", + "upvotes": 3919, + "upvoterUsernames": [], + "downvotes": 1172, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1757081, + "answers": 21, + "answerItems": [ + { + "_id": "62f321c7082fcc3049e90ab8", + "creator": "Buu", + "createdAt": 1228185837000, + "text": "

Check out the built-in function encodeURIComponent(str) and encodeURI(str).
\nIn your case, this should work:

\n
var myOtherUrl = \n       "http://example.com/index.html?url=" + encodeURIComponent(myUrl);\n
\n", + "upvotes": 4749, + "upvoterUsernames": [], + "downvotes": 1677, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3271c082fcc3049e923d1", + "creator": "hitautodestruct", + "createdAt": 1351424213000, + "text": "How about adding the explanation @cms gave? escape is also a valid option.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923d3", + "creator": "Ifnot", + "createdAt": 1362155754000, + "text": "according to @CMS encodeURI is not really safe for URL encoding.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923d5", + "creator": "node_saini", + "createdAt": 1476714665000, + "text": "@BuuNguyen this does not work for me. I still see the my & as &amp; in my terminal.", + "upvotes": 737, + "upvoterUsernames": [], + "downvotes": 737, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90aba", + "creator": "Mike Brennan", + "createdAt": 1306713283000, + "text": "

Stick with encodeURIComponent(). The function encodeURI() does not bother to encode many characters that have semantic importance in URLs (e.g. \"#\", \"?\", and \"&\"). escape() is deprecated, and does not bother to encode \"+\" characters, which will be interpreted as encoded spaces on the server (and, as pointed out by others here, does not properly URL-encode non-ASCII characters).

\n\n

There is a nice explanation of the difference between encodeURI() and encodeURIComponent() elsewhere. If you want to encode something so that it can safely be included as a component of a URI (e.g. as a query string parameter), you want to use encodeURIComponent().

\n", + "upvotes": 290, + "upvoterUsernames": [], + "downvotes": 89, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ab9", + "creator": "Christian C. Salvadó", + "createdAt": 1228186194000, + "text": "

You have three options:

\n\n

But in your case, if you want to pass a URL into a GET parameter of other page, you should use escape or encodeURIComponent, but not encodeURI.

\n

See Stack Overflow question Best practice: escape, or encodeURI / encodeURIComponent for further discussion.

\n", + "upvotes": 3206, + "upvoterUsernames": [], + "downvotes": 1558, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271c082fcc3049e923db", + "creator": "erickson", + "createdAt": 1228193745000, + "text": "The character encoding used with escape is variable. Stick with encodeURI and encodeURIComponent, which use UTF-8.", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923dd", + "creator": "opteronn", + "createdAt": 1267819815000, + "text": "Be careful. That escape converts non-ASCII characters into its Unicode escape sequences, like %uxxx.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923df", + "creator": "kevzettler", + "createdAt": 1296363908000, + "text": "I am using encodeURIComponent and noticing it will not encode pipe characters |", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923e1", + "creator": "nickf", + "createdAt": 1296473818000, + "text": "@kevzettler - why should it do that? The pipes aren't of semantic importance in a URI.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923e2", + "creator": "fiatjaf", + "createdAt": 1373060757000, + "text": "does anybody use non-ASCII characters in URIs?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923e4", + "creator": "Tseng", + "createdAt": 1378305835000, + "text": "@GiovanniP: People who allow German, French, Japanese, Chinese, Arabic characters as input and pass theses parameters via GET or POST.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923e6", + "creator": "fiatjaf", + "createdAt": 1379719119000, + "text": "ah, ok, I thought you were talking about the domain/path parts, don't know why I thought this.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3271c082fcc3049e923e7", + "creator": "John N", + "createdAt": 1414611023000, + "text": "encodeURIComponent() encoded the # and encodeURI() did not!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90ac2", + "creator": "serg", + "createdAt": 1483642188000, + "text": "

To prevent double encoding it's a good idea to decode the url before encoding (if you are dealing with user entered urls for example, which might be already encoded).

\n\n

Lets say we have abc%20xyz 123 as input (one space is already encoded):

\n\n
encodeURI(\"abc%20xyz 123\")            //   wrong: \"abc%2520xyz%20123\"\nencodeURI(decodeURI(\"abc%20xyz 123\")) // correct: \"abc%20xyz%20123\"\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac1", + "creator": "Mohith Maratt", + "createdAt": 1474991989000, + "text": "

You can use esapi library and encode your url using the below function. The function ensures that '/' are not lost to encoding while the remainder of the text contents are encoded:

\n\n
function encodeUrl(url)\n{\n    String arr[] = url.split(\"/\");\n    String encodedUrl = \"\";\n    for(int i = 0; i<arr.length; i++)\n    {\n        encodedUrl = encodedUrl + ESAPI.encoder().encodeForHTML(ESAPI.encoder().encodeForURL(arr[i]));\n        if(i<arr.length-1) encodedUrl = encodedUrl + \"/\";\n    }\n    return url;\n}\n
\n\n

https://www.owasp.org/index.php/ESAPI_JavaScript_Readme

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac3", + "creator": "Gerard ONeill", + "createdAt": 1485466303000, + "text": "

To encode a URL, as has been said before, you have two functions:

\n\n
encodeURI()\n
\n\n

and

\n\n
encodeURIComponent()\n
\n\n

The reason both exist is that the first preserves the URL with the risk of leaving too many things unescaped, while the second encodes everything needed.

\n\n

With the first, you could copy the newly escaped URL into address bar (for example) and it would work. However your unescaped '&'s would interfere with field delimiters, the '='s would interfere with field names and values, and the '+'s would look like spaces. But for simple data when you want to preserve the URL nature of what you are escaping, this works.

\n\n

The second is everything you need to do to make sure nothing in your string interfers with a URL. It leaves various unimportant characters unescaped so that the URL remains as human readable as possible without interference. A URL encoded this way will no longer work as a URL without unescaping it.

\n\n

So if you can take the time, you always want to use encodeURIComponent() -- before adding on name/value pairs encode both the name and the value using this function before adding it to the query string.

\n\n

I'm having a tough time coming up with reasons to use the encodeURI() -- I'll leave that to the smarter people.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac4", + "creator": "Willem van der Veen", + "createdAt": 1537691726000, + "text": "

What is URL encoding:

\n\n

A URL should be encoded when there are special characters located inside the URL. For example:

\n\n

\r\n
\r\n
console.log(encodeURIComponent('?notEncoded=&+'));
\r\n
\r\n
\r\n

\n\n

We can observe in this example that all characters except the string notEncoded are encoded with % signs. URL encoding is also known as percentage encoding because it escapes all special characters with a %. Then after this % sign every special character has a unique code

\n\n

Why do we need URL encoding:

\n\n

Certain characters have a special value in a URL string. For example, the ? character denotes the beginning of a query string. In order to succesfully locate a resource on the web, it is necesarry to distinguish between when a character is meant as a part of string or part of the url structure.

\n\n

How can we achieve URL encoding in JS:

\n\n

JS offers a bunch of build in utility function which we can use to easily encode URL's. These are two convenient options:

\n\n
    \n
  1. encodeURIComponent(): Takes a component of a URI as an argument and returns the encoded URI string.
  2. \n
  3. encodeURI(): Takes a URI as an argument and returns the encoded URI string.
  4. \n
\n\n

Example and caveats:

\n\n

Be aware of not passing in the whole URL (including scheme, e.g https://) into encodeURIComponent(). This can actually transform it into a not functional URL. For example:

\n\n

\r\n
\r\n
// for a whole URI don't use encodeURIComponent it will transform\r\n// the / characters and the URL won't fucntion properly\r\nconsole.log(encodeURIComponent(\"http://www.random.com/specials&char.html\"));\r\n\r\n// instead use encodeURI for whole URL's\r\nconsole.log(encodeURI(\"http://www.random.com/specials&char.html\"));
\r\n
\r\n
\r\n

\n\n

We can observe f we put the whole URL in encodeURIComponent that the foward slashes (/) are also converted to special characters. This will cause the URL to not function properly anymore.

\n\n

Therefore (as the name implies) use:

\n\n
    \n
  1. encodeURIComponent on a certain part of a URL which you want to encode.
  2. \n
  3. encodeURI on a whole URL which you want to encode.
  4. \n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac5", + "creator": "Jonathan Applebaum", + "createdAt": 1549708002000, + "text": "

Here is a LIVE DEMO of encodeURIComponent() and decodeURIComponent() JS built in functions:

\n\n
<!DOCTYPE html>\n<html>\n  <head>\n    <style>\n      textarea{\n        width:30%;\n        height:100px;\n      }\n    </style>\n    <script>\n      // encode string to base64\n      function encode()\n      {\n        var txt = document.getElementById(\"txt1\").value;\n        var result = btoa(txt);\n        document.getElementById(\"txt2\").value = result;\n      }\n      // decode base64 back to original string\n      function decode()\n      {\n        var txt = document.getElementById(\"txt3\").value;\n        var result = atob(txt);\n        document.getElementById(\"txt4\").value = result;\n      }\n    </script>\n  </head>\n  <body>\n    <div>\n      <textarea id=\"txt1\">Some text to decode\n      </textarea>\n    </div>\n    <div>\n      <input type=\"button\" id=\"btnencode\" value=\"Encode\" onClick=\"encode()\"/>\n    </div>\n    <div>\n      <textarea id=\"txt2\">\n      </textarea>\n    </div>\n    <br/>\n    <div>\n      <textarea id=\"txt3\">U29tZSB0ZXh0IHRvIGRlY29kZQ==\n      </textarea>\n    </div>\n    <div>\n      <input type=\"button\" id=\"btndecode\" value=\"Decode\" onClick=\"decode()\"/>\n    </div>\n    <div>\n      <textarea id=\"txt4\">\n      </textarea>\n    </div>\n  </body>\n</html>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac7", + "creator": "Qback", + "createdAt": 1573828250000, + "text": "

Modern solution (2021)

\n

Since the other answers were written, the URLSearchParams API has been introduced. It can be used like this:

\n
const queryParams = { param1: 'value1', param2: 'value2' }\nconst queryString = new URLSearchParams(queryParams).toString()\n// 'param1=value1&param2=value2'\n
\n

It also encodes non-URL characters.

\n

For your specific example, you would use it like this:

\n
\n
const myUrl = "http://example.com/index.html?param=1&anotherParam=2";\nconst myOtherUrl = new URL("http://example.com/index.html");\nmyOtherUrl.search = new URLSearchParams({url: myUrl});\nconsole.log(myOtherUrl.toString());\n
\n
\n

This solution is also mentioned here and here.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac6", + "creator": "Arthur", + "createdAt": 1570308497000, + "text": "

Use fixedEncodeURIComponent function to strictly comply with RFC 3986:

\n
function fixedEncodeURIComponent(str) {\n  return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {\n    return '%' + c.charCodeAt(0).toString(16);\n  });\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac8", + "creator": "HoldOffHunger", + "createdAt": 1589662172000, + "text": "

You should not use encodeURIComponent() directly.

\n

Take a look at RFC3986: Uniform Resource Identifier (URI): Generic Syntax

\n
\n

sub-delims = "!" / "$" / "&" / "'" / "(" / ")"\n/ "*" / "+" / "," / ";" / "="

\n

The purpose of reserved characters is to provide a set of delimiting characters that are distinguishable from other data within a URI.

\n
\n

These reserved characters from the URI definition in RFC3986 ARE NOT escaped by encodeURIComponent().

\n

MDN Web Docs: encodeURIComponent()

\n
\n

To be more stringent in adhering to RFC 3986 (which reserves !, ', (, ), and *), even though these characters have no formalized URI delimiting uses, the following can be safely used:

\n
\n

Use the MDN Web Docs function...

\n
function fixedEncodeURIComponent(str) {\n  return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {\n    return '%' + c.charCodeAt(0).toString(16);\n  });\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac9", + "creator": "Kamil Kiełczewski", + "createdAt": 1591951232000, + "text": "

Performance

\n\n

Today (2020.06.12) I perform speed test for chosen solutions on MacOs HighSierra 10.13.6 on browsers Chrome 83.0, Safari 13.1, Firefox 77.0. This results can be useful for massive urls encoding.

\n\n

Conclusions

\n\n\n\n

\"enter

\n\n

Details

\n\n

For solutions \nA\nB\nC\nD\nE\nF\nI perform two tests

\n\n\n\n

\r\n
\r\n
function A(url) {\r\n\treturn escape(url);\r\n}\r\n\r\nfunction B(url) {\r\n\treturn encodeURI(url);\r\n}\r\n\r\nfunction C(url) {\r\n\treturn encodeURIComponent(url);\r\n}\r\n\r\nfunction D(url) {\r\n\treturn new URLSearchParams({url}).toString();\r\n}\r\n\r\nfunction E(url){\r\n     return encodeURIComponent(url).replace(/[!'()]/g, escape).replace(/\\*/g, \"%2A\");\r\n}\r\n\r\nfunction F(url) {\r\n  return encodeURIComponent(url).replace(/[!'()*]/g, function(c) {\r\n    return '%' + c.charCodeAt(0).toString(16);\r\n  });\r\n}\r\n\r\n\r\n\r\n// ----------\r\n// TEST\r\n// ----------\r\n\r\nvar myUrl = \"http://example.com/index.html?param=1&anotherParam=2\";\r\n\r\n[A,B,C,D,E,F]\r\n  .forEach(f=> console.log(`${f.name} ?url=${f(myUrl).replace(/^url=/,'')}`));
\r\n
This snippet only presents code of choosen solutions
\r\n
\r\n
\r\n

\n\n

Example results for Chrome

\n\n

\"enter

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aca", + "creator": "gurpartap", + "createdAt": 1597791432000, + "text": "
var myOtherUrl = \n   "http://example.com/index.html?url=" + encodeURIComponent(myUrl).replace(/%20/g,'+');\n
\n

Don't forget the /g flag to replace all encoded ' '

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90acb", + "creator": "Pyzard", + "createdAt": 1607657095000, + "text": "

I always use this to encode stuff for URLs. This is completely safe because it will encode every single character even if it doesn't have to be encoded.

\n
function urlEncode(text) {\n    let encoded = '';\n    for (let char of text) {\n        encoded += '%' + char.charCodeAt(0).toString(16);\n    }\n    return encoded;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90acc", + "creator": "m4heshd", + "createdAt": 1625145072000, + "text": "

I think now in 2022 to be really safe, you should always consider constructing your URLs using URL() interface. It'll do most of the job for you. So coming to your code,

\n
const baseURL = 'http://example.com/index.html';\n\nconst myUrl = new URL(baseURL);\nmyUrl.searchParams.append('param', '1');\nmyUrl.searchParams.append('anotherParam', '2');\n\nconst myOtherUrl = new URL(baseURL);\nmyOtherUrl.searchParams.append('url', myUrl.href);\n\nconsole.log(myUrl.href);\n// Outputs: http://example.com/index.html?param=1&anotherParam=2\nconsole.log(myOtherUrl.href);\n// Outputs: http://example.com/index.html?url=http%3A%2F%2Fexample.com%2Findex.html%3Fparam%3D1%26anotherParam%3D2\nconsole.log(myOtherUrl.searchParams.get('url'));\n// Outputs: http://example.com/index.html?param=1&anotherParam=2\n
\n

Or..

\n
const params = new URLSearchParams(myOtherUrl.search);\n\nconsole.log(params.get('url'));\n// Outputs: http://example.com/index.html?param=1&anotherParam=2\n
\n

Something like this is assured not to fail.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90abb", + "creator": "Asif Ashraf", + "createdAt": 1340335681000, + "text": "

Nothing worked for me. All I was seeing was the HTML of the login page, coming back to the client side with code 200. (302 at first but the same Ajax request loading login page inside another Ajax request, which was supposed to be a redirect rather than loading plain text of the login page).

\n\n

In the login controller, I added this line:

\n\n
Response.Headers[\"land\"] = \"login\";\n
\n\n

And in the global Ajax handler, I did this:

\n\n
$(function () {\n    var $document = $(document);\n    $document.ajaxSuccess(function (e, response, request) {\n        var land = response.getResponseHeader('land');\n        var redrUrl = '/login?ReturnUrl=' + encodeURIComponent(window.location);\n        if(land) {\n            if (land.toString() === 'login') {\n                window.location = redrUrl;\n            }\n        }\n    });\n});\n
\n\n

Now I don't have any issue, and it works like a charm.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90abf", + "creator": "Sangeet Shah", + "createdAt": 1441867297000, + "text": "

Encode URL String\n

\n    var url = $(location).attr('href'); //get current url\n    //OR\n    var url = 'folder/index.html?param=#23dd&noob=yes'; //or specify one

\n\nvar encodedUrl = encodeURIComponent(url);\nconsole.log(encodedUrl);\n//outputs folder%2Findex.html%3Fparam%3D%2323dd%26noob%3Dyes\n\n\nfor more info go http://www.sitepoint.com/jquery-decode-url-string\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90abe", + "creator": "Narayan Yerrabachu", + "createdAt": 1368514124000, + "text": "

Similar kind of thing I tried with normal javascript

\n\n
function fixedEncodeURIComponent(str){\n     return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\\*/g, \"%2A\");\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90ac0", + "creator": "Adam Fischer", + "createdAt": 1443686606000, + "text": "

encodeURIComponent() is the way to go.

\n\n
var myOtherUrl = \"http://example.com/index.html?url=\" + encodeURIComponent(myUrl);\n
\n\n

BUT you should keep in mind that there are small differences from php version urlencode() and as @CMS mentioned, it will not encode every char. Guys at http://phpjs.org/functions/urlencode/ made js equivalent to phpencode():

\n\n
function urlencode(str) {\n  str = (str + '').toString();\n\n  // Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current\n  // PHP behavior, you would need to add \".replace(/~/g, '%7E');\" to the following.\n  return encodeURIComponent(str)\n    .replace('!', '%21')\n    .replace('\\'', '%27')\n    .replace('(', '%28')\n    .replace(')', '%29')\n    .replace('*', '%2A')\n    .replace('%20', '+');\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90abd", + "creator": "Maksym Kozlenko", + "createdAt": 1368415945000, + "text": "

I would suggest to use qs npm package

\n
qs.stringify({a:"1=2", b:"Test 1"}); // gets a=1%3D2&b=Test+1\n
\n

it is easier to use with JS object and it gives you proper URL encoding for all parameters

\n

If you are using jQuery I would go for $.param method. It URL encodes an object mapping fields to values, which is easier to read than calling an escape method on each value.

\n
$.param({a:"1=2", b:"Test 1"}) // gets a=1%3D2&b=Test+1\n
\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c3082fcc3049e92e79", + "creator": "Cyril Duchon-Doris", + "createdAt": 1483547768000, + "text": "Almost everyone uses jQuery and I feel more comfortable indeed with this instead of encoreURIComponent", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f329c3082fcc3049e92e7b", + "creator": "JohanTG", + "createdAt": 1618911445000, + "text": "qs great, compact and usefull package. Vote up for the qs on backend", + "upvotes": 197, + "upvoterUsernames": [], + "downvotes": 197, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90abc", + "creator": "Ryan Taylor", + "createdAt": 1355561252000, + "text": "

The best answer is to use encodeURIComponent on values in the query string (and nowhere else).

\n\n

However, I find that many APIs want to replace \" \" with \"+\" so I've had to use the following:

\n\n
const value = encodeURIComponent(value).replace('%20','+');\nconst url = 'http://example.com?lang=en&key=' + value\n
\n\n

escape is implemented differently in different browsers and encodeURI doesn't encode many characters (like # and even /) -- it's made to be used on a full URI/URL without breaking it – which isn't super helpful or secure.

\n\n

And as @Jochem points out below, you may want to use encodeURIComponent() on a (each) folder name, but for whatever reason these APIs don't seem to want + in folder names so plain old encodeURIComponent works great.

\n\n

Example:

\n\n
const escapedValue = encodeURIComponent(value).replace('%20','+');\nconst escapedFolder = encodeURIComponent('My Folder'); // no replace\nconst url = `http://example.com/${escapedFolder}/?myKey=${escapedValue}`;\n
\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c3082fcc3049e92e7e", + "creator": "njzk2", + "createdAt": 1401732711000, + "text": "I would replace in value rather than in the result of the encoding", + "upvotes": 350, + "upvoterUsernames": [], + "downvotes": 350, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c7082fcc3049e90ab5", + "creator": "Yanni", + "createdAt": 1309452038000, + "text": "See JavaScript urlencode function.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321c7082fcc3049e90ab6", + "creator": "phillihp", + "createdAt": 1347934399000, + "text": "You can use this tool here: phillihp.com/toolz/url-encode-decode", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c7082fcc3049e90ab7", + "creator": "Andrew", + "createdAt": 1520016762000, + "text": "encodeURIComponent()", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1761003, + "uvac": 1761024 + } + }, + { + "_id": "62f321bb082fcc3049e8feb1", + "title": "var functionName = function() {} vs function functionName() {}", + "title-lowercase": "var functionname = function() {} vs function functionname() {}", + "creator": "Richard Garside", + "createdAt": 1228303867000, + "status": "open", + "text": "

I've recently started maintaining someone else's JavaScript code. I'm fixing bugs, adding features and also trying to tidy up the code and make it more consistent.

\n

The previous developer used two ways of declaring functions and I can't work out if there is a reason behind it or not.

\n

The two ways are:

\n
var functionOne = function() {\n    // Some code\n};\n
\n\n
function functionTwo() {\n    // Some code\n}\n
\n

What are the reasons for using these two different methods and what are the pros and cons of each? Is there anything that can be done with one method that can't be done with the other?

\n", + "upvotes": 14436, + "upvoterUsernames": [], + "downvotes": 6979, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1164772, + "answers": 36, + "answerItems": [ + { + "_id": "62f321bd082fcc3049e9008a", + "creator": "Greg", + "createdAt": 1228304262000, + "text": "

The difference is that functionOne is a function expression and so only defined when that line is reached, whereas functionTwo is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

\n\n

For example, a function expression:

\n\n

\r\n
\r\n
// TypeError: functionOne is not a function\r\nfunctionOne();\r\n\r\nvar functionOne = function() {\r\n  console.log(\"Hello!\");\r\n};
\r\n
\r\n
\r\n

\n\n

And, a function declaration:

\n\n

\r\n
\r\n
// Outputs: \"Hello!\"\r\nfunctionTwo();\r\n\r\nfunction functionTwo() {\r\n  console.log(\"Hello!\");\r\n}
\r\n
\r\n
\r\n

\n\n

Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.

\n\n

\r\n
\r\n
'use strict';    \r\n{ // note this block!\r\n  function functionThree() {\r\n    console.log(\"Hello!\");\r\n  }\r\n}\r\nfunctionThree(); // ReferenceError
\r\n
\r\n
\r\n

\n", + "upvotes": 9652, + "upvoterUsernames": [], + "downvotes": 4180, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f322a2082fcc3049e911cf", + "creator": "vanowm", + "createdAt": 1648929445000, + "text": "@rails_has_elegance so what's the point of calling it "half hoisted" if it acts exactly the same as "not hoisted at all"?", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e9008b", + "creator": "Kafka", + "createdAt": 1229628642000, + "text": "

In computer science terms, we talk about anonymous functions and named functions. I think the most important difference is that an anonymous function is not bound to a name, hence the name anonymous function. In JavaScript it is a first class object dynamically declared at runtime.

\n

For more information on anonymous functions and lambda calculus, Wikipedia is a good start: Anonymous Functions.

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9008c", + "creator": "Sasha Firsov", + "createdAt": 1264278774000, + "text": "

In terms of code maintenance cost, named functions are more preferable:

\n\n\n\n

I suspect more PROS for named functions are follow. And what is listed as an advantage of named functions is a disadvantage for anonymous ones.

\n\n

Historically, anonymous functions appeared from the inability of JavaScript as a language to list members with named functions:

\n\n
{\n    member:function() { /* How do I make \"this.member\" a named function? */\n    }\n}\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9008d", + "creator": "thomasrutter", + "createdAt": 1271739289000, + "text": "

The two code snippets you've posted there will, for almost all purposes, behave the same way.

\n\n

However, the difference in behaviour is that with the first variant (var functionOne = function() {}), that function can only be called after that point in the code.

\n\n

With the second variant (function functionTwo()), the function is available to code that runs above where the function is declared.

\n\n

This is because with the first variant, the function is assigned to the variable foo at run time. In the second, the function is assigned to that identifier, foo, at parse time.

\n\n

More technical information

\n\n

JavaScript has three ways of defining functions.

\n\n
    \n
  1. Your first snippet shows a function expression. This involves using the \"function\" operator to create a function - the result of that operator can be stored in any variable or object property. The function expression is powerful that way. The function expression is often called an \"anonymous function\", because it does not have to have a name,
  2. \n
  3. Your second example is a function declaration. This uses the \"function\" statement to create a function. The function is made available at parse time and can be called anywhere in that scope. You can still store it in a variable or object property later.
  4. \n
  5. The third way of defining a function is the \"Function()\" constructor, which is not shown in your original post. It's not recommended to use this as it works the same way as eval(), which has its problems.
  6. \n
\n", + "upvotes": 261, + "upvoterUsernames": [], + "downvotes": 119, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9008e", + "creator": "Christian C. Salvadó", + "createdAt": 1281295931000, + "text": "

Speaking about the global context, both, the var statement and a FunctionDeclaration at the end will create a non-deleteable property on the global object, but the value of both can be overwritten.

\n\n

The subtle difference between the two ways is that when the Variable Instantiation process runs (before the actual code execution) all identifiers declared with var will be initialized with undefined, and the ones used by the FunctionDeclaration's will be available since that moment, for example:

\n\n
 alert(typeof foo); // 'function', it's already available\n alert(typeof bar); // 'undefined'\n function foo () {}\n var bar = function () {};\n alert(typeof bar); // 'function'\n
\n\n

The assignment of the bar FunctionExpression takes place until runtime.

\n\n

A global property created by a FunctionDeclaration can be overwritten without any problems just like a variable value, e.g.:

\n\n
 function test () {}\n test = null;\n
\n\n

Another obvious difference between your two examples is that the first function doesn't have a name, but the second has it, which can be really useful when debugging (i.e. inspecting a call stack).

\n\n

About your edited first example (foo = function() { alert('hello!'); };), it is an undeclared assignment, I would highly encourage you to always use the var keyword.

\n\n

With an assignment, without the var statement, if the referenced identifier is not found in the scope chain, it will become a deleteable property of the global object.

\n\n

Also, undeclared assignments throw a ReferenceError on ECMAScript 5 under Strict Mode.

\n\n

A must read:

\n\n\n\n

Note: This answer has been merged from another question, in which the major doubt and misconception from the OP was that identifiers declared with a FunctionDeclaration, couldn't be overwritten which is not the case.

\n", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 141, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9008f", + "creator": "Rob", + "createdAt": 1281296680000, + "text": "

An important reason is to add one and only one variable as the \"Root\" of your namespace...

\n\n
var MyNamespace = {}\nMyNamespace.foo= function() {\n\n}\n
\n\n

or

\n\n
var MyNamespace = {\n  foo: function() {\n  },\n  ...\n}\n
\n\n

There are many techniques for namespacing. It's become more important with the plethora of JavaScript modules available.

\n\n

Also see How do I declare a namespace in JavaScript?

\n", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90091", + "creator": "Joel Purra", + "createdAt": 1344093871000, + "text": "

@EugeneLazutkin gives an example where he names an assigned function to be able to use shortcut() as an internal reference to itself. John Resig gives another example - copying a recursive function assigned to another object in his Learning Advanced Javascript tutorial. While assigning functions to properties isn't strictly the question here, I recommend actively trying the tutorial out - run the code by clicking the button in the upper right corner, and double click the code to edit to your liking.

\n

Examples from the tutorial: recursive calls in yell():

\n

Tests fail when the original ninja object is removed. (page 13)

\n

\r\n
\r\n
function assert(predicate, message) { if(!predicate) { throw new Error(message); } }\n\nvar ninja = {\n  yell: function(n){\nreturn n > 0 ? ninja.yell(n-1) + \"a\" : \"hiy\";\n  }\n};\nassert( ninja.yell(4) == \"hiyaaaa\", \"A single object isn't too bad, either.\" ); \n\nvar samurai = { yell: ninja.yell };\nvar ninja = null;\n\ntry {\n  samurai.yell(4);\n} catch(e){\n  assert( false, \"Uh, this isn't good! Where'd ninja.yell go?\" );\n}
\r\n
\r\n
\r\n

\n

If you name the function that will be called recursively, the tests will pass. (page 14)

\n

\r\n
\r\n
function assert(predicate, message) { if(!predicate) { throw new Error(message); } }\n\nvar ninja = {\n  yell: function yell(n){\nreturn n > 0 ? yell(n-1) + \"a\" : \"hiy\";\n  }\n};\nassert( ninja.yell(4) == \"hiyaaaa\", \"Works as we would expect it to!\" );\n \nvar samurai = { yell: ninja.yell };\nvar ninja = {};\nassert( samurai.yell(4) == \"hiyaaaa\", \"The method correctly calls itself.\" );\n\nconsole.log(samurai.yell(4));
\r\n
\r\n
\r\n

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90090", + "creator": "Sean McMillan", + "createdAt": 1299179958000, + "text": "

Other commenters have already covered the semantic difference of the two variants above. I wanted to note a stylistic difference: Only the \"assignment\" variation can set a property of another object.

\n\n

I often build JavaScript modules with a pattern like this:

\n\n
(function(){\n    var exports = {};\n\n    function privateUtil() {\n            ...\n    }\n\n    exports.publicUtil = function() {\n            ...\n    };\n\n    return exports;\n})();\n
\n\n

With this pattern, your public functions will all use assignment, while your private functions use declaration.

\n\n

(Note also that assignment should require a semicolon after the statement, while declaration prohibits it.)

\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90092", + "creator": "Ingo Kegel", + "createdAt": 1350297767000, + "text": "

Another difference that is not mentioned in the other answers is that if you use the anonymous function

\n\n
var functionOne = function() {\n    // Some code\n};\n
\n\n

and use that as a constructor as in

\n\n
var one = new functionOne();\n
\n\n

then one.constructor.name will not be defined. Function.name is non-standard but is supported by Firefox, Chrome, other Webkit-derived browsers and IE 9+.

\n\n

With

\n\n
function functionTwo() {\n    // Some code\n}\ntwo = new functionTwo();\n
\n\n

it is possible to retrieve the name of the constructor as a string with two.constructor.name.

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90093", + "creator": "Herc", + "createdAt": 1354188529000, + "text": "

I use the variable approach in my code for a very specific reason, the theory of which has been covered in an abstract way above, but an example might help some people like me, with limited JavaScript expertise.

\n\n

I have code that I need to run with 160 independently-designed brandings. Most of the code is in shared files, but branding-specific stuff is in a separate file, one for each branding.

\n\n

Some brandings require specific functions, and some do not. Sometimes I have to add new functions to do new branding-specific things. I am happy to change the shared coded, but I don't want to have to change all 160 sets of branding files.

\n\n

By using the variable syntax, I can declare the variable (a function pointer essentially) in the shared code and either assign a trivial stub function, or set to null.

\n\n

The one or two brandings that need a specific implementation of the function can then define their version of the function and assign this to the variable if they want, and the rest do nothing. I can test for a null function before I execute it in the shared code.

\n\n

From people's comments above, I gather it may be possible to redefine a static function too, but I think the variable solution is nice and clear.

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90094", + "creator": "NullPoiиteя", + "createdAt": 1357411072000, + "text": "

The first one (function doSomething(x)) should be part of an object notation.

\n\n

The second one (var doSomething = function(x){ alert(x);}) is simply creating an anonymous function and assigning it to a variable, doSomething. So doSomething() will call the function.

\n\n

You may want to know what a function declaration and function expression is.

\n\n

A function declaration defines a named function variable without requiring variable assignment. Function declarations occur as standalone constructs and cannot be nested within non-function blocks.

\n\n
function foo() {\n    return 3;\n}\n
\n\n
\n

ECMA 5 (13.0) defines the syntax as
\n function Identifier ( FormalParameterListopt ) { FunctionBody }

\n
\n\n

In above condition the function name is visible within its scope and the scope of its parent (otherwise it would be unreachable).

\n\n

And in a function expression

\n\n

A function expression defines a function as a part of a larger expression syntax (typically a variable assignment ). Functions defined via functions expressions can be named or anonymous. Function expressions should not start with “function”.

\n\n
// Anonymous function expression\nvar a = function() {\n    return 3;\n}\n\n// Named function expression\nvar a = function foo() {\n    return 3;\n}\n\n// Self-invoking function expression\n(function foo() {\n    alert(\"hello!\");\n})();\n
\n\n
\n

ECMA 5 (13.0) defines the syntax as
\n function Identifieropt ( FormalParameterListopt ) { FunctionBody }

\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90095", + "creator": "eljenso", + "createdAt": 1360168160000, + "text": "

A function declaration and a function expression assigned to a variable behave the same once the binding is established.

\n\n

There is a difference however at how and when the function object is actually associated with its variable. This difference is due to the mechanism called variable hoisting in JavaScript.

\n\n

Basically, all function declarations and variable declarations are hoisted to the top of the function in which the declaration occurs (this is why we say that JavaScript has function scope).

\n\n\n\n

The order of hoisting is also important: function declarations take precedence over variable declarations with the same name, and the last function declaration takes precedence over previous function declarations with the same name.

\n\n

Some examples...

\n\n
var foo = 1;\nfunction bar() {\n  if (!foo) {\n    var foo = 10 }\n  return foo; }\nbar() // 10\n
\n\n

Variable foo is hoisted to the top of the function, initialized to undefined, so that !foo is true, so foo is assigned 10. The foo outside of bar's scope plays no role and is untouched.

\n\n
function f() {\n  return a; \n  function a() {return 1}; \n  var a = 4;\n  function a() {return 2}}\nf()() // 2\n\nfunction f() {\n  return a;\n  var a = 4;\n  function a() {return 1};\n  function a() {return 2}}\nf()() // 2\n
\n\n

Function declarations take precedence over variable declarations, and the last function declaration \"sticks\".

\n\n
function f() {\n  var a = 4;\n  function a() {return 1}; \n  function a() {return 2}; \n  return a; }\nf() // 4\n
\n\n

In this example a is initialized with the function object resulting from evaluating the second function declaration, and then is assigned 4.

\n\n
var a = 1;\nfunction b() {\n  a = 10;\n  return;\n  function a() {}}\nb();\na // 1\n
\n\n

Here the function declaration is hoisted first, declaring and initializing variable a. Next, this variable is assigned 10. In other words: the assignment does not assign to outer variable a.

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90096", + "creator": "Mbengue Assane", + "createdAt": 1364563584000, + "text": "

An illustration of when to prefer the first method to the second one is when you need to avoid overriding a function's previous definitions.

\n\n

With

\n\n
if (condition){\n    function myfunction(){\n        // Some code\n    }\n}\n
\n\n

, this definition of myfunction will override any previous definition, since it will be done at parse-time.

\n\n

While

\n\n
if (condition){\n    var myfunction = function (){\n        // Some code\n    }\n}\n
\n\n

does the correct job of defining myfunction only when condition is met.

\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90097", + "creator": "Pawel Furmaniak", + "createdAt": 1382719102000, + "text": "

If you would use those functions to create objects, you would get:

\n\n
var objectOne = new functionOne();\nconsole.log(objectOne.__proto__); // prints \"Object {}\" because constructor is an anonymous function\n\nvar objectTwo = new functionTwo();\nconsole.log(objectTwo.__proto__); // prints \"functionTwo {}\" because constructor is a named function\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90098", + "creator": "sla55er", + "createdAt": 1401956924000, + "text": "

The first example is a function declaration:

\n\n
function abc(){}\n
\n\n

The second example is a function expression:

\n\n
var abc = function() {};\n
\n\n

The main difference is how they are hoisted (lifted and declared). In the first example, the whole function declaration is hoisted. In the second example only the var 'abc' is hoisted, its value (the function) will be undefined, and the function itself remains at the position that it is declared.

\n\n

To put it simply:

\n\n
//this will work\nabc(param);\nfunction abc(){}\n\n//this would fail\nabc(param);\nvar abc = function() {}\n
\n\n

To study more about this topic I strongly recommend you this\nlink

\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90099", + "creator": "suhailvs", + "createdAt": 1407552338000, + "text": "

A better explanation to Greg's answer

\n
functionTwo();\nfunction functionTwo() {\n}\n
\n

Why no error? We were always taught that expressions are executed from top to bottom(??)

\n

Because:

\n
\n

Function declarations and variable declarations are always moved (hoisted) invisibly to the top of their containing scope by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. ben cherry

\n
\n

This means that code like this:

\n
functionOne();                  ---------------      var functionOne;\n                                | is actually |      functionOne();\nvar functionOne = function(){   | interpreted |-->\n};                              |    like     |      functionOne = function(){\n                                ---------------      };\n
\n

Notice that the assignment portion of the declarations were not hoisted. Only the name is hoisted.

\n

But in the case with function declarations, the entire function body will be hoisted as well:

\n
functionTwo();              ---------------      function functionTwo() {\n                            | is actually |      };\nfunction functionTwo() {    | interpreted |-->\n}                           |    like     |      functionTwo();\n                            ---------------\n
\n", + "upvotes": 186, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9009b", + "creator": "Tao", + "createdAt": 1435140487000, + "text": "

This is just two possible ways of declaring functions, and in the second way, you can use the function before declaration.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9009a", + "creator": "Leon Gaban", + "createdAt": 1430492815000, + "text": "

I'm adding my own answer just because everyone else has covered the hoisting part thoroughly.

\n\n

I've wondered about which way is better for a long while now, and thanks to http://jsperf.com now I know :)

\n\n

\"enter

\n\n

Function declarations are faster, and that's what really matters in web dev right? ;)

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a4082fcc3049e91250", + "creator": "d9k", + "createdAt": 1614619158000, + "text": "see answer about performance below, different results", + "upvotes": 1060, + "upvoterUsernames": [], + "downvotes": 1060, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e9009d", + "creator": "varna", + "createdAt": 1441794653000, + "text": "

I'm listing out the differences below:

\n\n
    \n
  1. A function declaration can be placed anywhere in the code. Even if it is invoked before the definition appears in code, it gets executed as function declaration is committed to memory or in a way it is hoisted up, before any other code in the page starts execution.

    \n\n

    Take a look at the function below:

    \n\n
    function outerFunction() {\n    function foo() {\n       return 1;\n    }\n    return foo();\n    function foo() {\n       return 2;\n    }\n}\nalert(outerFunction()); // Displays 2\n
    \n\n

    This is because, during execution, it looks like:-

    \n\n
    function foo() {  // The first function declaration is moved to top\n    return 1;\n}\nfunction foo() {  // The second function declaration is moved to top\n    return 2;\n}\nfunction outerFunction() {\n    return foo();\n}\nalert(outerFunction()); //So executing from top to bottom,\n                        //the last foo() returns 2 which gets displayed\n
    \n\n

    A function expression, if not defined before calling it, will result in an error. Also, here the function definition itself is not moved to the top or committed to memory like in the function declarations. But the variable to which we assign the function gets hoisted up and undefined gets assigned to it.

    \n\n

    Same function using function expressions:

    \n\n
    function outerFunction() {\n    var foo = function() {\n       return 1;\n    }\n    return foo();\n    var foo = function() {\n       return 2;\n    }\n}\nalert(outerFunction()); // Displays 1\n
    \n\n

    This is because during execution, it looks like:

    \n\n
    function outerFunction() {\n   var foo = undefined;\n   var foo = undefined;\n\n   foo = function() {\n      return 1;\n   };\n   return foo ();\n   foo = function() {   // This function expression is not reachable\n      return 2;\n   };\n}\nalert(outerFunction()); // Displays 1\n
  2. \n
  3. It is not safe to write function declarations in non-function blocks like if because they won't be accessible.

    \n\n
    if (test) {\n    function x() { doSomething(); }\n}\n
  4. \n
  5. Named function expression like the one below, may not work in Internet Explorer browsers prior to version 9.

    \n\n
    var today = function today() {return new Date()}\n
  6. \n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9009c", + "creator": "Rohan", + "createdAt": 1437464737000, + "text": "

Greg's Answer is good enough, but I still would like to add something to it that I learned just now watching Douglas Crockford's videos.

\n\n

Function expression:

\n\n
var foo = function foo() {};\n
\n\n

Function statement:

\n\n
function foo() {};\n
\n\n

The function statement is just a shorthand for var statement with a function value.

\n\n

So

\n\n
function foo() {};\n
\n\n

expands to

\n\n
var foo = function foo() {};\n
\n\n

Which expands further to:

\n\n
var foo = undefined;\nfoo = function foo() {};\n
\n\n

And they are both hoisted to the top of the code.

\n\n

\"Screenshot

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9009f", + "creator": "SuperNova", + "createdAt": 1462863946000, + "text": "

new Function() can be used to pass the function's body in a string. And hence this can be used to create dynamic functions. Also passing the script without executing the script.

\n\n
var func = new Function(\"x\", \"y\", \"return x*y;\");\nfunction secondFunction(){\n   var result;\n   result = func(10,20);\n   console.log ( result );\n}\n\nsecondFunction()\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9009e", + "creator": "Nitin9791", + "createdAt": 1451333883000, + "text": "

Both are different ways of defining a function. The difference is how the browser interprets and loads them into an execution context.

\n\n

The first case is of function expressions which loads only when the interpreter reaches that line of code. So if you do it like the following, you will get an error that the functionOne is not a function.

\n\n
functionOne();\nvar functionOne = function() {\n    // Some code\n};\n
\n\n

The reason is that on the first line no value is assigned to functionOne, and hence it is undefined. We are trying to call it as a function, and hence we are getting an error.

\n\n

On the second line we are assigning the reference of an anonymous function to functionOne.

\n\n

The second case is of function declarations that loads before any code is executed. So if you do like the following you won't get any error as the declaration loads before code execution.

\n\n
functionOne();\nfunction functionOne() {\n   // Some code\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a1", + "creator": "Alireza", + "createdAt": 1494338196000, + "text": "

They are pretty similar with some small differences, first one is a variable which assigned to an anonymous function (Function Declaration) and second one is the normal way to create a function in JavaScript(Anonymous function Declaration), both has usage, cons and pros:

\n\n

1. Function Expression

\n\n
var functionOne = function() {\n    // Some code\n};\n
\n\n
\n

A Function Expression defines a function as a part of a larger\n expression syntax (typically a variable assignment ). Functions\n defined via Functions Expressions can be named or anonymous. Function\n Expressions must not start with “function” (hence the parentheses\n around the self invoking example below).

\n
\n\n

Assign a variable to a function, means no Hoisting, as we know functions in JavaScript can Hoist, means they can be called before they get declared, while variables need to be declared before getting access to them, so means in this case we can not access the function before where it's declared, also it could be a way that you write your functions, for the functions which return another function, this kind of declaration could make sense, also in ECMA6 & above you can assign this to an arrow function which can be used to call anonymous functions, also this way of declaring is a better way to create Constructor functions in JavaScript.

\n\n

2. Function Declaration

\n\n
function functionTwo() {\n    // Some code\n}\n
\n\n
\n

A Function Declaration defines a named function variable without\n requiring variable assignment. Function Declarations occur as\n standalone constructs and cannot be nested within non-function blocks.\n It’s helpful to think of them as siblings of Variable Declarations.\n Just as Variable Declarations must start with “var”, Function\n Declarations must begin with “function”.

\n
\n\n

This is the normal way of calling a function in JavaScript, this function can be called before you even declare it as in JavaScript all functions get Hoisted, but if you have 'use strict' this won't Hoist as expected, it's a good way to call all normal functions which are not big in lines and neither are a constructor function.

\n\n

Also, if you need more info about how hoisting works in JavaScript, visit the link below:

\n\n

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a0", + "creator": "Anoop Rai", + "createdAt": 1471166035000, + "text": "

In JavaScript there are two ways to create functions:

\n\n
    \n
  1. Function declaration:

    \n\n
    function fn(){\n  console.log(\"Hello\");\n}\nfn();\n
    \n\n

    This is very basic, self-explanatory, used in many languages and standard across C family of languages. We declared a function defined it and executed it by calling it.

    \n\n

    What you should be knowing is that functions are actually objects in JavaScript; internally we have created an object for above function and given it a name called fn or the reference to the object is stored in fn. Functions are objects in JavaScript; an instance of function is actually an object instance.

  2. \n
  3. Function expression:

    \n\n
    var fn=function(){\n  console.log(\"Hello\");\n}\nfn();\n
    \n\n

    JavaScript has first-class functions, that is, create a function and assign it to a variable just like you create a string or number and assign it to a variable. Here, the fn variable is assigned to a function. The reason for this concept is functions are objects in JavaScript; fn is pointing to the object instance of the above function. We have initialized a function and assigned it to a variable. It's not executing the function and assigning the result.

  4. \n
\n\n

Reference: JavaScript function declaration syntax: var fn = function() {} vs function fn() {}

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a3", + "creator": "user2693928", + "createdAt": 1520509772000, + "text": "

I prefer defining function as variable:

\n\n
let first = function(x){\n   return x[0];\n}\n
\n\n

Instead of:

\n\n
function first(){\n    ....\n}\n
\n\n

Because i can use expressions and decorators when defining the function. For example:

\n\n
let safe = function(f){\n  try {f()...}\n}\nlet last = safe(function(x){return x[0]}).\n
\n\n

Also with ES6 its much shorter:

\n\n
 let last = x => x[0]\n ...........\n function last(x){\n     return x[0];\n }\n......\n\nlet last = safe(x => x[0]);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a2", + "creator": "Panos Kal.", + "createdAt": 1506573263000, + "text": "

About performance:

\n\n

New versions of V8 introduced several under-the-hood optimizations and so did SpiderMonkey.

\n\n

There is almost no difference now between expression and declaration.
Function expression appears to be faster now.

\n\n

Chrome 62.0.3202\n\"Chrome

\n\n

FireFox 55\n\"Firefox

\n\n

Chrome Canary 63.0.3225\n\"Chrome

\n\n


\n\n
\n

Anonymous function expressions appear to have better performance\n against Named function expression.

\n
\n\n


\n\n

Firefox\n\"Firefox\nChrome Canary\n\"Chrome\nChrome\n\"Chrome

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a4082fcc3049e91259", + "creator": "Ronny Sherer", + "createdAt": 1602680370000, + "text": "The results differences are too small to be considered as a difference. If you'll run the test 100 times, you will get 100 results.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f322a4082fcc3049e9125b", + "creator": "Panos Kal.", + "createdAt": 1602686643000, + "text": "@RonnySherer, are you familiar with jsperf? Tests were made after running more than 10 million times!", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322a4082fcc3049e9125d", + "creator": "Ronny Sherer", + "createdAt": 1603292080000, + "text": "The virtual environment is on a server which might do some other stuff. I did some tests. The results are never exactly the same.", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900a5", + "creator": "Kean Amaral", + "createdAt": 1531608973000, + "text": "

This is called a Function Expression:

\n\n
var getRectArea = function(width, height) {\n    return width * height;\n};\n\nconsole.log(\"Area of Rectangle: \" + getRectArea(3,4));\n// This should return the following result in the console: \n// Area of Rectangle: 12\n
\n\n

This is called a Function Declaration:

\n\n
var w = 5;\nvar h = 6;\n\nfunction RectArea(width, height) {  //declaring the function\n  return area = width * height;\n}                                   //note you do not need ; after }\n\nRectArea(w,h);                      //calling or executing the function\nconsole.log(\"Area of Rectangle: \" + area);\n// This should return the following result in the console: \n// Area of Rectangle: 30\n
\n\n

Hope this helps explain what is the difference between Function Expression and Function Declaration and how to use them. Thanks.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a4", + "creator": "Santosh Pillai", + "createdAt": 1526343743000, + "text": "

Expression in JS: Something that returns a value
\nExample: Try out following in chrome console:

\n\n
a = 10\noutput : 10\n\n(1 + 3)\noutput = 4\n
\n\n

Declaration/Statement: Something that does not return a value
\nExample:

\n\n
if (1 > 2) {\n // do something. \n}\n
\n\n

here (1>2) is an expression but the 'if' statament is not. Its not returning anything.

\n\n


\n\n

Similarly, we have Function Declaration/Statement vs Function Expression
\nLets take an example:

\n\n
// test.js\n\nvar a = 10;\n\n// function expression\nvar fun_expression = function() {\n   console.log(\"Running function Expression\");\n}\n\n// funciton expression\n\nfunction fun_declaration() {\n   console.log(\"Running function Statement\");\n}\n
\n\n

Important: \nWhat happens when JavaScript engines runs the above js file.

\n\n\n\n

Now suppose we update the js to.

\n\n
// test.js\n\nconsole.log(a)  //output: udefined (No error)\nconsole.log(fun_expression)  // output: undefined (No error)\nconsole.log(fun_expression()) // output: Error. As we trying to invoke undefined. \nconsole.log(fun_declaration()) // output: running function statement  (As fun_declaration is already hoisted in the memory). \n\nvar a = 10;\n\n// function expression\nvar fun_expression = function() {\n   console.log('Running function expression')\n}\n\n// function declaration\n\nfunction fun_declaration() {\n   console.log('running function declaration')\n}\n\nconsole.log(a)   // output: 10\nconsole.log(fun_expression()) //output: Running function expression\nconsole.log(fun_declaration()) //output: running function declaration\n
\n\n

The output mentioned above in the comments, should be useful to understand the different between function expression and function statement/declaration.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a7", + "creator": "Shakespear", + "createdAt": 1545065935000, + "text": "

Named Functions Vs. Anonymous Functions

\n

The first function syntax is Anonymous Function Expression:

\n
var functionOne = function() {\n  // do something...\n};\n
\n

While, the second one is Function Declaration:

\n
function functionTwo () {\n  // do something...\n}\n
\n

The main difference between both is the function name since Anonymous Functions have no name to call.\nAnonymous functions are quick and easy to declare, and many libraries and tools tend to encourage this idiomatic style of code. However, anonymous functions have some drawbacks:

\n\n

Naming Function Expression

\n

Providing a name for your function expression quite effectively addresses all these drawbacks, and has no tangible downsides. The best practice is to always name your function expressions:

\n
setTimeout(function timeHandler() { // <-- look, a name here!\n  console.log("I've waited 1 second");\n}, 1000);\n
\n

Naming IIFEs (Immediate Invoked Function Expression)

\n
(function IIFE(str) { // <-- look, always name IIFEs!\n  console.log(str); // "Hello!"\n})('Hello!');\n
\n

For functions assigned to a variable, naming the function, in this case, is not very common and may cause confusion, in this case, the arrow function may be a better choice.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a6", + "creator": "Nitesh Ranjan", + "createdAt": 1539889027000, + "text": "

One important point to note is :-

\n\n

let there are two functions :-

\n\n
sum(1,2);\n\nconst sum = function(first, second) {\n  return first + second;\n}\n
\n\n

In above case, it will give error that sum is not defined, but

\n\n
sum(1,2);\n\nfunction sum(first, second) {\n  return first + second;\n}\n
\n\n

This function will not any error as Hoisting will take place in this case.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ab", + "creator": "shreyasm-dev", + "createdAt": 1599601150000, + "text": "

You can't use the .bind() method on function declarations, but you can on function expressions.

\n

Function declaration:

\n

\r\n
\r\n
function x() {\n  console.log(this)\n}.bind('string')\n\nx()
\r\n
\r\n
\r\n

\n

Function expression:

\n

\r\n
\r\n
var x = function() {\n  console.log(this)\n}.bind('string')\n\nx()
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900aa", + "creator": "Robin Hossain", + "createdAt": 1583225036000, + "text": "

The var functionOne = function() {} defines at run-time and the function functionTwo() {} defines at parse-time.

\n\n
// Run-Time function declaration \nfunctionOne(); // Calling functionOne function here will give an Error\nvar functionOne = function () {\n  // Some code\n};\n\n// Parse-Time function declaration \nfunctionTwo(); // Calling functionTwo function will not give an Error\nfunction functionTwo() {\n  // Some code...\n}\n
\n\n

The explanation between Run-time vs Parse-time\njavascript run-time vs parse-time

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a9", + "creator": "Willem van der Veen", + "createdAt": 1561025196000, + "text": "

Difference function declaration and function expression:

\n\n

Javascript has first class functions. This means that they can be treated just like any other variable. Functions can be passed as arguments in a function, be returned from a function, and can be stored in variables.

\n\n

However storing function in a variable (function expression) isn't the only way to create a function, this can also be done via a function declaration. Here are the key differences:

\n\n
    \n
  1. Function expressions can be anonymous whereas a function declaration must have a name.
  2. \n
  3. Both have a name property which is used to identify the function. A function expression's name property is the name of the variable which it is bound to, whereas the name of a function declaration is simply the given name.
  4. \n
  5. Function declarations are hoisted whereas, function expressions are not. Only the variable is hoisted to have the value of undefined.
  6. \n
\n\n

Here is an example:

\n\n

\r\n
\r\n
try {\r\n  functionOne();\r\n} catch (e) {\r\n  console.log('i cant run because im not hoisted');\r\n}\r\n\r\nfunctionTwo();\r\n\r\n// function expression, does not get hoisted\r\nlet functionOne = function randomName() {\r\n    // Some code\r\n};\r\n\r\n// function declaration, gets hoisted\r\nfunction functionTwo() {\r\n   console.log('I get hoisted');\r\n}\r\n\r\ntry {\r\n  randomName(); // this isn't the proper name, it is functionOne\r\n} catch (e) {\r\n  console.log('You cant call me with randomName my name is function one');\r\n}
\r\n
\r\n
\r\n

\n\n

:

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ac", + "creator": "root", + "createdAt": 1627741561000, + "text": "

During a breakpoint in the debugger/DevTools, if you use the format function functionName() {} in the console, you can't use functionName() in the console subsequently (it says "not defined"), whereas after var functionName = function() {}, you can use the function.

\n

See this question.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ad", + "creator": "S.G", + "createdAt": 1634819607000, + "text": "

\r\n
\r\n
try {\n  console.log(\"Success: \", add(1, 1));\n} catch(e) {\n  console.log(\"ERROR: \" + e);\n}\n\nvar add=function(a, b){\n  return a + b;\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900a8", + "creator": "H.Ostwal", + "createdAt": 1546411844000, + "text": "

Another difference between both function is functionOne can be used as a variable that can hold multiple functions within and functionTwo holds some block of code that gets executed all when called. Please check below :

\n\n
   var functionOne = (function() {\n      return {\n\n         sayHello: function(){\n                console.log('say hello')\n\n         },\n         redirectPage:function(_url){\n                window.location.href = _url;\n         }\n\n      }\n})();\n
\n\n

You have a choice which function to be called. e.g functionOne.sayHello or functionOne. redirectPage. And if you call functionTwo then whole block of code will get executed.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1179208, + "uvac": 1179244 + } + }, + { + "_id": "62f321bb082fcc3049e8fee2", + "title": "How to append something to an array?", + "title-lowercase": "how to append something to an array?", + "creator": "interstar", + "createdAt": 1228782005000, + "status": "open", + "text": "

How do I append an object (such as a string or number) to an array in JavaScript?

\n", + "upvotes": 3300, + "upvoterUsernames": [], + "downvotes": 409, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 4589142, + "answers": 29, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e90981", + "creator": "rjmunro", + "createdAt": 1312371989000, + "text": "

If arr is an array, and val is the value you wish to add use:

\n\n
arr.push(val);\n
\n\n

E.g.

\n\n

\r\n
\r\n
var arr = ['a', 'b', 'c'];\r\narr.push('d');\r\nconsole.log(arr);
\r\n
\r\n
\r\n

\n", + "upvotes": 87, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90980", + "creator": "Jens Roland", + "createdAt": 1285800047000, + "text": "

Some quick benchmarking (each test = 500k appended elements and the results are averages of multiple runs) showed the following:

\n\n

Firefox 3.6 (Mac):

\n\n\n\n

Safari 5.0 (Mac):

\n\n\n\n

Google Chrome 6.0 (Mac):

\n\n\n\n

I like the arr.push() syntax better, but I think I'd be better off with the arr[arr.length] Version, at least in raw speed. I'd love to see the results of an IE run though.

\n\n
\n\n

My benchmarking loops:

\n\n
function arrpush_small() {\n    var arr1 = [];\n    for (a = 0; a < 100; a++)\n    {\n        arr1 = [];\n        for (i = 0; i < 5000; i++)\n        {\n            arr1.push('elem' + i);\n        }\n    }\n}\n\nfunction arrlen_small() {\n    var arr2 = [];\n    for (b = 0; b < 100; b++)\n    {\n        arr2 = [];\n        for (j = 0; j < 5000; j++)\n        {\n            arr2[arr2.length] = 'elem' + j;\n        }\n    }\n}\n\n\nfunction arrpush_large() {\n    var arr1 = [];\n    for (i = 0; i < 500000; i++)\n    {\n        arr1.push('elem' + i);\n    }\n}\n\nfunction arrlen_large() {\n    var arr2 = [];\n    for (j = 0; j < 500000; j++)\n    {\n        arr2[arr2.length] = 'elem' + j;\n    }\n}\n
\n", + "upvotes": 624, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c2082fcc3049e9216d", + "creator": "Justin", + "createdAt": 1367280614000, + "text": "Would love to see concat() in the same test!", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f326c2082fcc3049e9216e", + "creator": "Paul Draper", + "createdAt": 1401134844000, + "text": "I wonder how much this has changed in the past four years. (jsperf would be handy right now.)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9097f", + "creator": "MK_Dev", + "createdAt": 1228782927000, + "text": "

If you're only appending a single variable, then push() works just fine. If you need to append another array, use concat():

\n

\r\n
\r\n
var ar1 = [1, 2, 3];\nvar ar2 = [4, 5, 6];\n\nvar ar3 = ar1.concat(ar2);\n\nalert(ar1);\nalert(ar2);\nalert(ar3);
\r\n
\r\n
\r\n

\n

The concat does not affect ar1 and ar2 unless reassigned, for example:

\n

\r\n
\r\n
var ar1 = [1, 2, 3];\nvar ar2 = [4, 5, 6];\n\nar1 = ar1.concat(ar2);\nalert(ar1);
\r\n
\r\n
\r\n

\n

There is a lot of great information on JavaScript Reference.

\n", + "upvotes": 1357, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90982", + "creator": "Mαzen", + "createdAt": 1313196739000, + "text": "

Use concat:

\n\n

\r\n
\r\n
a = [1, 2, 3];\r\nb = [3, 4, 5];\r\na = a.concat(b);\r\nconsole.log(a);
\r\n
\r\n
\r\n

\n", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90984", + "creator": "Omnimike", + "createdAt": 1346304284000, + "text": "

I think it's worth mentioning that push can be called with multiple arguments, which will be appended to the array in order. For example:

\n

\r\n
\r\n
var arr = ['first'];\narr.push('second', 'third');\nconsole.log(arr);
\r\n
\r\n
\r\n

\n

As a result of this you can use push.apply to append an array to another array like so:

\n

\r\n
\r\n
var arr = ['first'];\narr.push('second', 'third');\narr.push.apply(arr, ['forth', 'fifth']);\nconsole.log(arr);
\r\n
\r\n
\r\n

\n

Annotated ES5 has more info on exactly what push and apply do.

\n

2016 update: with spread, you don't need that apply anymore, like:

\n

\r\n
\r\n
var arr = ['first'];\narr.push('second', 'third');\n\narr.push(...['fourth', 'fifth']);\nconsole.log(arr) ;
\r\n
\r\n
\r\n

\n", + "upvotes": 443, + "upvoterUsernames": [], + "downvotes": 122, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c3082fcc3049e92173", + "creator": "Gabriel Littman", + "createdAt": 1408560320000, + "text": "best answer for appending an array of items while not creating a new array.", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f326c3082fcc3049e92175", + "creator": "Juan Mendes", + "createdAt": 1408671860000, + "text": "@GabrielLittman Specially because appending to an array, IMHO, should modify the array, not create a new one.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c3082fcc3049e92176", + "creator": "Michael", + "createdAt": 1424130066000, + "text": "I like the push.apply version, would rather use Array.prototype.push.apply() though.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90983", + "creator": "yoel halb", + "createdAt": 1345057331000, + "text": "

If you know the highest index (such as stored in a variable "i") then you can do

\n
myArray[i + 1] = someValue;\n
\n

However, if you don't know then you can either use

\n
myArray.push(someValue);\n
\n

as other answers suggested, or you can use

\n
myArray[myArray.length] = someValue;\n
\n

Note that the array is zero based so .length returns the highest index plus one.

\n

Also note that you don't have to add in order and you can actually skip values, as in

\n
myArray[myArray.length + 1000] = someValue;\n
\n

In which case the values in between will have a value of undefined.

\n

It is therefore a good practice when looping through a JavaScript to verify that a value actually exists at that point.

\n

This can be done by something like the following:

\n
if(myArray[i] === "undefined"){ continue; }\n
\n

If you are certain that you don't have any zeros in the array then you can just do:

\n
if(!myArray[i]){ continue; }\n
\n

Of course, make sure in this case that you don't use as the condition myArray[i] (as some people over the Internet suggest based on the end that as soon as i is greater than the highest index, it will return undefined which evaluates to false).

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90986", + "creator": "Pawan Singh", + "createdAt": 1409318541000, + "text": "

If you want to append two arrays -

\n\n
var a = ['a', 'b'];\nvar b = ['c', 'd'];\n
\n\n

then you could use:

\n\n
var c = a.concat(b);\n
\n\n

And if you want to add record g to array (var a=[]) then you could use:

\n\n
a.push('g');\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90985", + "creator": "Fizer Khan", + "createdAt": 1395073666000, + "text": "

You can use the push and apply functions to append two arrays.

\n

\r\n
\r\n
var array1 = [11, 32, 75];\nvar array2 = [99, 67, 34];\n\nArray.prototype.push.apply(array1, array2);\nconsole.log(array1);
\r\n
\r\n
\r\n

\n

It will append array2 to array1. Now array1 contains [11, 32, 75, 99, 67, 34].\nThis code is much simpler than writing for loops to copy each and every items in the array.

\n", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c3082fcc3049e9217a", + "creator": "amit bakle", + "createdAt": 1455272102000, + "text": "This is what i required, maintain original array reference", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f326c3082fcc3049e9217c", + "creator": "miike3459", + "createdAt": 1556842209000, + "text": "I'd use array1.concat(array2) in this situation. The call to push seems unnecessary.", + "upvotes": 1521, + "upvoterUsernames": [], + "downvotes": 1521, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90987", + "creator": "9ete", + "createdAt": 1411065639000, + "text": "

Let the array length property do the work:

\n
myarray[myarray.length] = 'new element value added to the end of the array';\n
\n

myarray.length returns the number of strings in the array.\nJavaScript is zero-based, so the next element key of the array will be the current length of the array.

\n

Example:

\n
var myarray = [0, 1, 2, 3],\n    myarrayLength = myarray.length; // myarrayLength is set to 4\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90988", + "creator": "Maarten Peels", + "createdAt": 1431006957000, + "text": "

The push() method adds new items to the end of an array, and returns the new length. Example:

\n\n
var fruits = [\"Banana\", \"Orange\", \"Apple\", \"Mango\"];\nfruits.push(\"Kiwi\");\n\n// The result of fruits will be:\nBanana, Orange, Apple, Mango, Kiwi\n
\n\n

The exact answer to your question is already answered, but let's look at some other ways to add items to an array.

\n\n

The unshift() method adds new items to the beginning of an array, and returns the new length. Example:

\n\n
var fruits = [\"Banana\", \"Orange\", \"Apple\", \"Mango\"];\nfruits.unshift(\"Lemon\", \"Pineapple\");\n\n// The result of fruits will be:\nLemon, Pineapple, Banana, Orange, Apple, Mango\n
\n\n

And lastly, the concat() method is used to join two or more arrays. Example:

\n\n
var fruits = [\"Banana\", \"Orange\"];\nvar moreFruits = [\"Apple\", \"Mango\", \"Lemon\"];\nvar allFruits = fruits.concat(moreFruits);\n\n// The values of the children array will be:\nBanana, Orange, Apple, Mango, Lemon\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098a", + "creator": "Danil Gaponov", + "createdAt": 1437489517000, + "text": "
\n

Just want to add a snippet for non-destructive addition of an element.

\n
\n\n
var newArr = oldArr.concat([newEl]);\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90989", + "creator": "CodingIntrigue", + "createdAt": 1433858568000, + "text": "

With the new ES6 spread operator, joining two arrays using push becomes even easier:

\n\n

\r\n
\r\n
var arr = [1, 2, 3, 4, 5];\r\nvar arr2 = [6, 7, 8, 9, 10];\r\narr.push(...arr2);\r\nconsole.log(arr);
\r\n
\r\n
\r\n

\n\n

This adds the contents of arr2 onto the end of arr.

\n\n

Babel REPL Example

\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098b", + "creator": "Karl", + "createdAt": 1446023349000, + "text": "

concat(), of course, can be used with two-dimensional arrays as well. No looping required.

\n
var a = [\n    [1, 2],\n    [3, 4] ];\n\nvar b = [\n    ["a", "b"],\n    ["c", "d"] ];\n\nb = b.concat(a);\n\nalert(b[2][1]); // Result: 2\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098c", + "creator": "Downhillski", + "createdAt": 1448817158000, + "text": "

JavaScript with the ECMAScript 5 (ES5) standard which is supported by most browsers now, you can use apply() to append array1 to array2.

\n
var array1 = [3, 4, 5];\nvar array2 = [1, 2];\n\nArray.prototype.push.apply(array2, array1);\n\nconsole.log(array2); // [1, 2, 3, 4, 5]\n
\n

JavaScript with ECMAScript 6 (ES6) standard which is supported by Chrome, Firefox, Internet Explorer, and Edge, you can use the spread operator:

\n
"use strict";\nlet array1 = [3, 4, 5];\nlet array2 = [1, 2];\n\narray2.push(...array1);\n\nconsole.log(array2); // [1, 2, 3, 4, 5]\n
\n

The spread operator will replace array2.push(...array1); with array2.push(3, 4, 5); when the browser is thinking the logic.

\n

Bonus point

\n

If you'd like to create another variable to store all the items from both arrays, you can do this:

\n

ES5 var combinedArray = array1.concat(array2);

\n

ES6 const combinedArray = [...array1, ...array2]

\n

The spread operator (...) is to spread out all items from a collection.

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098d", + "creator": "Satyapriya Mishra", + "createdAt": 1455733908000, + "text": "

If you want to append a single value into an array, simply use the push method. It will add a new element at the end of the array.

\n\n

But if you intend to add multiple elements then store the elements in a new array and concat the second array with the first array...either way you wish.

\n\n
arr=['a','b','c'];\narr.push('d');\n//now print the array in console.log and it will contain 'a','b','c','d' as elements.\nconsole.log(array);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098e", + "creator": "Emil Reña Enriquez", + "createdAt": 1458289484000, + "text": "

If you want to combine two arrays without the duplicate you may try the code below:

\n
array_merge = function (arr1, arr2) {\n  return arr1.concat(arr2.filter(function(item){\n    return arr1.indexOf(item) < 0;\n  }))\n}\n
\n

Usage:

\n
array1 = ['1', '2', '3']\narray2 = ['2', '3', '4', '5']\ncombined_array = array_merge(array1, array2)\n
\n

Output:

\n

[1,2,3,4,5]

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9098f", + "creator": "JmLavoier", + "createdAt": 1475187031000, + "text": "

If you are using ES6 you can use spread operator to do it.

\n
var arr = [\n    "apple",\n    "banana",\n    "cherry"\n];\n\nvar arr2 = [\n    "dragonfruit",\n    "elderberry",\n    "fig"\n];\n\narr.push(...arr2);\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90990", + "creator": "Taufiq Rahman", + "createdAt": 1480673336000, + "text": "

There are a couple of ways to append an array in JavaScript:

\n\n

1) The push() method adds one or more elements to the end of an array and returns the new length of the array.

\n\n
var a = [1, 2, 3];\na.push(4, 5);\nconsole.log(a);\n
\n\n

Output:

\n\n
[1, 2, 3, 4, 5]\n
\n\n

2) The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array:

\n\n
var a = [1, 2, 3];\na.unshift(4, 5);\nconsole.log(a); \n
\n\n

Output:

\n\n
[4, 5, 1, 2, 3]\n
\n\n

3) The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

\n\n
var arr1 = [\"a\", \"b\", \"c\"];\nvar arr2 = [\"d\", \"e\", \"f\"];\nvar arr3 = arr1.concat(arr2);\nconsole.log(arr3);\n
\n\n

Output:

\n\n
[ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ]\n
\n\n

4) You can use the array's .length property to add an element to the end of the array:

\n\n
var ar = ['one', 'two', 'three'];\nar[ar.length] = 'four';\nconsole.log( ar ); \n
\n\n

Output:

\n\n
 [\"one\", \"two\", \"three\", \"four\"]\n
\n\n

5) The splice() method changes the content of an array by removing existing elements and/or adding new elements:

\n\n
var myFish = [\"angel\", \"clown\", \"mandarin\", \"surgeon\"];\nmyFish.splice(4, 0, \"nemo\");\n//array.splice(start, deleteCount, item1, item2, ...)\nconsole.log(myFish);\n
\n\n

Output:

\n\n
[\"angel\", \"clown\", \"mandarin\", \"surgeon\",\"nemo\"]\n
\n\n

6) You can also add a new element to an array simply by specifying a new index and assigning a value:

\n\n
var ar = ['one', 'two', 'three'];\nar[3] = 'four'; // add new element to ar\nconsole.log(ar);\n
\n\n

Output:

\n\n
[\"one\", \"two\",\"three\",\"four\"]\n
\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90991", + "creator": "José Antonio Postigo", + "createdAt": 1487767578000, + "text": "

Now, you can take advantage of ES6 syntax and just do

\n
let array = [1, 2];\nconsole.log([...array, 3]);\n
\n

keeping the original array immutable.

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90992", + "creator": "Alireza", + "createdAt": 1494764298000, + "text": "

We don't have an append function for Array in JavaScript, but we have push and unshift. Imagine you have the array below:

\n
var arr = [1, 2, 3, 4, 5];\n
\n

And we like to append a value to this array. We can do arr.push(6), and it will add 6 to the end of the array:

\n
arr.push(6); // Returns [1, 2, 3, 4, 5, 6];\n
\n

Also we can use unshift, look at how we can apply this:

\n
arr.unshift(0); // Returns [0, 1, 2, 3, 4, 5];\n
\n

They are main functions to add or append new values to the arrays.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90993", + "creator": "Jayme", + "createdAt": 1497353302000, + "text": "

You .push() that value in. \nExample: array.push(value);

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90995", + "creator": "Sanu Uthaiah Bollera", + "createdAt": 1516183896000, + "text": "

You can use the push method.

\n
Array.prototype.append = function(destArray){\n    destArray = destArray || [];\n    this.push.call(this, ...destArray);\n    return this;\n}\nvar arr = [1,2,5,67];\nvar arr1 = [7,4,7,8];\nconsole.log(arr.append(arr1)); // [7, 4, 7, 8, 1, 4, 5, 67, 7]\nconsole.log(arr.append("Hola")) // [1, 2, 5, 67, 7, 4, 7, 8, "H", "o", "l", "a"]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90994", + "creator": "Hoque MD Zahidul", + "createdAt": 1508841854000, + "text": "

You can do it using JavaScript Spread Operator Syntax:

\n
// Initialize the array\n\nvar arr = [\n    "Hi",\n    "Hello",\n    "Bangladesh"\n];\n\n// Append a new value to the array\n\narr = [...arr, "Feni"]; \n\n// Or you can add a variable value\n\nvar testValue = "Cool";\n\narr = [...arr, testValue ];\n\nconsole.log(arr);\n\n// Final output [ 'Hi', 'Hello', 'Bangladesh', 'Feni', 'Cool' ]\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c4082fcc3049e92189", + "creator": "Peter Mortensen", + "createdAt": 1628955333000, + "text": "Which feature of ES6 in particular? What is the (official) name of the feature?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90997", + "creator": "Harun Or Rashid", + "createdAt": 1527673701000, + "text": "

push() adds a new element to the end of an array.

\n

pop() removes an element from the end of an array.

\n

To append an object (such as a string or number) to an array, use:

\n
array.push(toAppend);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90996", + "creator": "Lior Elrom", + "createdAt": 1527372068000, + "text": "

Append a value to an array

\n\n

Since Array.prototype.push adds one or more elements to the end of an array and returns the new length of the array, sometimes we want just to get the new up-to-date array so we can do something like so:

\n\n
const arr = [1, 2, 3];\nconst val = 4;\n\narr.concat([val]); // [1, 2, 3, 4]\n
\n\n

Or just:

\n\n
[...arr, val] // [1, 2, 3, 4]\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c5082fcc3049e9218b", + "creator": "monikapatelIT", + "createdAt": 1528408150000, + "text": "Correct with ES6 for rest operator.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90998", + "creator": "Flavio Copes", + "createdAt": 1529421987000, + "text": "

Append a single item

\n\n

To append a single item to an array, use the push() method provided by the Array object:

\n\n
const fruits = ['banana', 'pear', 'apple']\nfruits.push('mango')\nconsole.log(fruits)\n
\n\n

push() mutates the original array.

\n\n

To create a new array instead, use the concat() Array method:

\n\n
const fruits = ['banana', 'pear', 'apple']\nconst allfruits = fruits.concat('mango')\nconsole.log(allfruits)\n
\n\n

Notice that concat() does not actually add an item to the array, but creates a new array, which you can assign to another variable, or reassign to the original array (declaring it as let, as you cannot reassign a const):

\n\n
const fruits = ['banana', 'pear', 'apple']\nconst allfruits = fruits.concat('mango')\nconsole.log(allfruits)\n
\n\n
let fruits = ['banana', 'pear', 'apple']\nfruits = fruits.concat('mango')\n
\n\n

Append multiple items

\n\n

To append a multiple item to an array, you can use push() by calling it with multiple arguments:

\n\n
const fruits = ['banana', 'pear', 'apple']\nfruits.push('mango', 'melon', 'avocado')\nconsole.log(fruits)\n
\n\n

You can also use the concat() method you saw before, passing a list of items separated by a comma:

\n\n
const fruits = ['banana', 'pear', 'apple']\nconst allfruits = fruits.concat('mango', 'melon', 'avocado')\nconsole.log(allfruits)\n
\n\n

or an array:

\n\n
const fruits = ['banana', 'pear', 'apple']\nconst allfruits = fruits.concat(['mango', 'melon', 'avocado'])\nconsole.log(allfruits)\n
\n\n

Remember that as described previously this method does not mutate the original array, but it returns a new array.

\n\n
\n

Originally posted at

\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90999", + "creator": "Srikrushna", + "createdAt": 1530212492000, + "text": "

Append a single element

\n
// Append to the end\narrName.push('newName1');\n\n// Prepend to the start\narrName.unshift('newName1');\n\n// Insert at index 1\narrName.splice(1, 0,'newName1');\n// 1: index number, 0: number of element to remove, newName1: new element\n\n\n// Replace index 3 (of exists), add new element otherwise.\narrName[3] = 'newName1';\n
\n

Append multiple elements

\n
// Insert from index number 1\narrName.splice(1, 0,'newElemenet1', 'newElemenet2', 'newElemenet3');\n// 1: index number from where insert starts,\n// 0: number of element to remove,\n//newElemenet1,2,3: new elements\n
\n

Append an array

\n
// Join two or more arrays\narrName.concat(newAry1, newAry2);\n//newAry1,newAry2: Two different arrays which are to be combined (concatenated) to an existing array\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c5082fcc3049e9218e", + "creator": "Mihail Malostanidis", + "createdAt": 1534090349000, + "text": "Why not arrName.push('newElemenet1, newElemenet2, newElemenet3')?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c5082fcc3049e92190", + "creator": "Mihail Malostanidis", + "createdAt": 1534104304000, + "text": "Oh, I thought the assumption was that arrName was of length 1.", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 98, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9099a", + "creator": "Ir Calif", + "createdAt": 1533635326000, + "text": "

Appending items on an array

\n
let fruits = ["orange", "banana", "apple", "lemon"]; /* Array declaration */\n\nfruits.push("avacado"); /* Adding an element to the array */\n\n/* Displaying elements of the array */\n\nfor(var i=0; i < fruits.length; i++){\n  console.log(fruits[i]);\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9099b", + "creator": "Aditya", + "createdAt": 1534913592000, + "text": "

You can use the push() if you want to add values,\ne.g. arr.push("Test1", "Test2");.

\n

If you have array you can use concat(), e.g. Array1.concat(Array2).

\n

If you have just one element to add, you can also try the length method, e.g. array[aray.length] = 'test';.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 4592442, + "uvac": 4592471 + } + }, + { + "_id": "62f321bb082fcc3049e8feb7", + "title": "Which equals operator (== vs ===) should be used in JavaScript comparisons?", + "title-lowercase": "which equals operator (== vs ===) should be used in javascript comparisons?", + "creator": "bcasp", + "createdAt": 1229005189000, + "status": "open", + "text": "

I'm using JSLint to go through JavaScript, and it's returning many suggestions to replace == (two equals signs) with === (three equals signs) when doing things like comparing idSele_UNVEHtype.value.length == 0 inside of an if statement.

\n\n

Is there a performance benefit to replacing == with ===?

\n\n

Any performance improvement would be welcomed as many comparison operators exist.

\n\n

If no type conversion takes place, would there be a performance gain over ==?

\n", + "upvotes": 10560, + "upvoterUsernames": [], + "downvotes": 4907, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2028528, + "answers": 45, + "answerItems": [ + { + "_id": "62f321be082fcc3049e9014a", + "creator": "Andreas Grech", + "createdAt": 1229006038000, + "text": "

Using the == operator (Equality)

\n
true == 1; //true, because 'true' is converted to 1 and then compared\n"2" == 2;  //true, because "2" is converted to 2 and then compared\n
\n

Using the === operator (Identity)

\n
true === 1; //false\n"2" === 2;  //false\n
\n

This is because the equality operator == does type coercion, meaning that the interpreter implicitly tries to convert the values before comparing.

\n

On the other hand, the identity operator === does not do type coercion, and thus does not convert the values when comparing.

\n", + "upvotes": 1587, + "upvoterUsernames": [], + "downvotes": 358, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9014b", + "creator": "Sean", + "createdAt": 1229006695000, + "text": "

There is unlikely to be any performance difference between the two operations in your usage. There is no type-conversion to be done because both parameters are already the same type. Both operations will have a type comparison followed by a value comparison.

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9014c", + "creator": "Doctor Jones", + "createdAt": 1229007531000, + "text": "

The === operator is called a strict comparison operator, it does differ from the == operator.

\n\n

Lets take 2 vars a and b.

\n\n

For \"a == b\" to evaluate to true a and b need to be the same value.

\n\n

In the case of \"a === b\" a and b must be the same value and also the same type for it to evaluate to true.

\n\n

Take the following example

\n\n
var a = 1;\nvar b = \"1\";\n\nif (a == b) //evaluates to true as a and b are both 1\n{\n    alert(\"a == b\");\n}\n\nif (a === b) //evaluates to false as a is not the same type as b\n{\n    alert(\"a === b\");\n}\n
\n\n

In summary; using the == operator might evaluate to true in situations where you do not want it to so using the === operator would be safer.

\n\n

In the 90% usage scenario it won't matter which one you use, but it is handy to know the difference when you get some unexpected behaviour one day.

\n", + "upvotes": 139, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9014d", + "creator": "Constantin", + "createdAt": 1229437756000, + "text": "

In a typical script there will be no performance difference. More important may be the fact that thousand \"===\" is 1 KB heavier than thousand \"==\" :) JavaScript profilers can tell you if there is a performance difference in your case.

\n\n

But personally I would do what JSLint suggests. This recommendation is there not because of performance issues, but because type coercion means ('\\t\\r\\n' == 0) is true.

\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9014e", + "creator": "Simon Scarfe", + "createdAt": 1230203855000, + "text": "

I tested this in Firefox with Firebug using code like this:

\n

\r\n
\r\n
console.time(\"testEquality\");\nvar n = 0;\nwhile (true) {\n  n++;\n  if (n == 100000)\n    break;\n}\nconsole.timeEnd(\"testEquality\");
\r\n
\r\n
\r\n

\n

and

\n

\r\n
\r\n
console.time(\"testTypeEquality\");\nvar n = 0;\nwhile (true) {\n  n++;\n  if (n === 100000)\n    break;\n}\nconsole.timeEnd(\"testTypeEquality\");
\r\n
\r\n
\r\n

\n

My results (tested five times each and averaged):

\n
==: 115.2\n===: 114.4\n
\n

So I'd say that the miniscule difference (this is over 100000 iterations, remember) is negligible. Performance isn't a reason to do ===. Type safety (well, as safe as you're going to get in JavaScript), and code quality is.

\n", + "upvotes": 170, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9014f", + "creator": "Thomas Hansen", + "createdAt": 1230551684000, + "text": "

The problem is that you might easily get into trouble since JavaScript have a lot of implicit conversions meaning...

\n\n
var x = 0;\nvar isTrue = x == null;\nvar isFalse = x === null;\n
\n\n

Which pretty soon becomes a problem. The best sample of why implicit conversion is \"evil\" can be taken from this code in MFC / C++ which actually will compile due to an implicit conversion from CString to HANDLE which is a pointer typedef type...

\n\n
CString x;\ndelete x;\n
\n\n

Which obviously during runtime does very undefined things...

\n\n

Google for implicit conversions in C++ and STL to get some of the arguments against it...

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f1082fcc3049e91329", + "creator": "Garrett", + "createdAt": 1389572719000, + "text": "0 == null is false.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90150", + "creator": "Philippe Leybaert", + "createdAt": 1244229096000, + "text": "

In the answers here, I didn't read anything about what equal means. Some will say that === means equal and of the same type, but that's not really true. It actually means that both operands reference the same object, or in case of value types, have the same value.

\n

So, let's take the following code:

\n
var a = [1,2,3];\nvar b = [1,2,3];\nvar c = a;\n\nvar ab_eq = (a === b); // false (even though a and b are the same type)\nvar ac_eq = (a === c); // true\n
\n

The same here:

\n
var a = { x: 1, y: 2 };\nvar b = { x: 1, y: 2 };\nvar c = a;\n\nvar ab_eq = (a === b); // false (even though a and b are the same type)\nvar ac_eq = (a === c); // true\n
\n

Or even:

\n
var a = { };\nvar b = { };\nvar c = a;\n\nvar ab_eq = (a === b); // false (even though a and b are the same type)\nvar ac_eq = (a === c); // true\n
\n

This behavior is not always obvious. There's more to the story than being equal and being of the same type.

\n

The rule is:

\n

For value types (numbers):
\na === b returns true if a and b have the same value and are of the same type

\n

For reference types:
\na === b returns true if a and b reference the exact same object

\n

For strings:
\na === b returns true if a and b are both strings and contain the exact same characters

\n
\n

Strings: the special case...

\n

Strings are not value types, but in Javascript they behave like value types, so they will be "equal" when the characters in the string are the same and when they are of the same length (as explained in the third rule)

\n

Now it becomes interesting:

\n
var a = "12" + "3";\nvar b = "123";\n\nalert(a === b); // returns true, because strings behave like value types\n
\n

But how about this?:

\n
var a = new String("123");\nvar b = "123";\n\nalert(a === b); // returns false !! (but they are equal and of the same type)\n
\n

I thought strings behave like value types? Well, it depends who you ask... In this case a and b are not the same type. a is of type Object, while b is of type string. Just remember that creating a string object using the String constructor creates something of type Object that behaves as a string most of the time.

\n", + "upvotes": 1215, + "upvoterUsernames": [], + "downvotes": 565, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90151", + "creator": "vsync", + "createdAt": 1273669092000, + "text": "

=== checks same sides are equal in type as well as value.

\n
\n

Example:

\n
'1' === 1 // will return "false" because `string` is not a `number`\n
\n

Common example:

\n
0 == ''  // will be "true", but it's very common to want this check to be "false"\n
\n

Another common example:

\n
null == undefined // returns "true", but in most cases a distinction is necessary\n
\n
\n

Many times an untyped check would be handy because you do not care if the value is either undefined, null, 0 or ""

\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90152", + "creator": "Dimitar", + "createdAt": 1273669104000, + "text": "

In JavaScript it means of the same value and type.

\n\n

For example,

\n\n
4 == \"4\" // will return true\n
\n\n

but

\n\n
4 === \"4\" // will return false \n
\n", + "upvotes": 115, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90154", + "creator": "Pop Catalin", + "createdAt": 1273669149000, + "text": "

It means equality without type coercion\ntype coercion means JavaScript do not automatically convert any other data types to string data types

\n\n
0==false   // true,although they are different types\n\n0===false  // false,as they are different types\n\n2=='2'    //true,different types,one is string and another is integer but \n            javaScript convert 2 to string by using == operator \n\n2==='2'  //false because by using === operator ,javaScript do not convert \n           integer to string \n\n2===2   //true because both have same value and same types \n
\n", + "upvotes": 111, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90153", + "creator": "Shiki", + "createdAt": 1273669119000, + "text": "

In PHP and JavaScript, it is a strict equality operator. Which means, it will compare both type and values.

\n", + "upvotes": 179, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90156", + "creator": "Niraj CHoubey", + "createdAt": 1273669417000, + "text": "

=== operator checks the values as well as the types of the variables for equality.

\n\n

== operator just checks the value of the variables for equality.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90155", + "creator": "Paul Butcher", + "createdAt": 1273669153000, + "text": "

From the core javascript reference

\n\n
\n

=== Returns true if the operands are strictly equal (see above)\n with no type conversion.

\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90157", + "creator": "Daniel", + "createdAt": 1273670361000, + "text": "

It's a strict check test.

\n\n

It's a good thing especially if you're checking between 0 and false and null.

\n\n

For example, if you have:

\n\n
$a = 0;\n
\n\n

Then:

\n\n
$a==0; \n$a==NULL;\n$a==false;\n
\n\n

All returns true and you may not want this. Let's suppose you have a function that can return the 0th index of an array or false on failure. If you check with \"==\" false, you can get a confusing result.

\n\n

So with the same thing as above, but a strict test:

\n\n
$a = 0;\n\n$a===0; // returns true\n$a===NULL; // returns false\n$a===false; // returns false\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f2082fcc3049e91332", + "creator": "Ry-", + "createdAt": 1367809637000, + "text": "In JavaScript, this is completely wrong and wrongly incomplete. 0 != null. -1", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90158", + "creator": "CuongHuyTo", + "createdAt": 1316183111000, + "text": "

The equal comparison operator == is confusing and should be avoided.

\n\n

If you HAVE TO live with it, then remember the following 3 things:

\n\n
    \n
  1. It is not transitive: (a == b) and (b == c) does not lead to (a == c)
  2. \n
  3. It's mutually exclusive to its negation: (a == b) and (a != b) always hold opposite Boolean values, with all a and b.
  4. \n
  5. In case of doubt, learn by heart the following truth table:
  6. \n
\n\n

EQUAL OPERATOR TRUTH TABLE IN JAVASCRIPT

\n\n\n\n

** STRANGE: note that any two values on the first column are not equal in that sense.**

\n\n
''       == 0 == false   // Any two values among these 3 ones are equal with the == operator\n'0'      == 0 == false   // Also a set of 3 equal values, note that only 0 and false are repeated\n'\\t'     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n'\\r'     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n'\\n'     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n'\\t\\r\\n' == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n\nnull == undefined  // These two \"default\" values are not-equal to any of the listed values above\nNaN                // NaN is not equal to any thing, even to itself.\n
\n", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015a", + "creator": "mar10", + "createdAt": 1367072138000, + "text": "

As a rule of thumb, I would generally use === instead of == (and !== instead of !=).

\n

Reasons are explained in in the answers above and also Douglas Crockford is pretty clear about it (JavaScript: The Good Parts).

\n

However there is one single exception:\n== null is an efficient way to check for 'is null or undefined':

\n
if( value == null ){\n    // value is either null or undefined\n}\n
\n

For example jQuery 1.9.1 uses this pattern 43 times, and the JSHint syntax checker even provides the eqnull relaxing option for this reason.

\n

From the jQuery style guide:

\n
\n

Strict equality checks (===) should be used in favor of ==. The only\nexception is when checking for undefined and null by way of null.

\n
\n
// Check for both undefined and null values, for some important reason. \nundefOrNull == null;\n
\n

EDIT 2021-03:

\n

Nowadays most browsers\nsupport the Nullish coalescing operator (??)\nand the Logical nullish assignment (??=), which allows a more concise way to\nassign a default value if a variable is null or undefined, for example:

\n
if (a.speed == null) {\n  // Set default if null or undefined\n  a.speed = 42;\n}\n
\n

can be written as any of these forms

\n
a.speed ??= 42;\na.speed ?? a.speed = 42;\na.speed = a.speed ?? 42;\n
\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90159", + "creator": "ashes", + "createdAt": 1338882784000, + "text": "

JSLint sometimes gives you unrealistic reasons to modify stuff. === has exactly the same performance as == if the types are already the same.

\n\n

It is faster only when the types are not the same, in which case it does not try to convert types but directly returns a false.

\n\n

So, IMHO, JSLint maybe used to write new code, but useless over-optimizing should be avoided at all costs.

\n\n

Meaning, there is no reason to change == to === in a check like if (a == 'test') when you know it for a fact that a can only be a String.

\n\n

Modifying a lot of code that way wastes developers' and reviewers' time and achieves nothing.

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015b", + "creator": "user2496033", + "createdAt": 1372824505000, + "text": "

JavaScript === vs == .

\n\n
0==false   // true\n0===false  // false, because they are of a different type\n1==\"1\"     // true, auto type coercion\n1===\"1\"    // false, because they are of a different type\n
\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015c", + "creator": "Harry He", + "createdAt": 1378715505000, + "text": "

The top 2 answers both mentioned == means equality and === means identity. Unfortunately, this statement is incorrect.

\n\n

If both operands of == are objects, then they are compared to see if they are the same object. If both operands point to the same object, then the equal operator returns true. Otherwise,\nthe two are not equal.

\n\n
var a = [1, 2, 3];  \nvar b = [1, 2, 3];  \nconsole.log(a == b)  // false  \nconsole.log(a === b) // false  \n
\n\n

In the code above, both == and === get false because a and b are not the same objects.

\n\n

That's to say: if both operands of == are objects, == behaves same as ===, which also means identity. The essential difference of this two operators is about type conversion. == has conversion before it checks equality, but === does not.

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015d", + "creator": "user2601995", + "createdAt": 1380750883000, + "text": "

Equality comparison:

\n\n

Operator ==

\n\n

Returns true, when both operands are equal. The operands are converted to the same type before being compared.

\n\n
>>> 1 == 1\ntrue\n>>> 1 == 2\nfalse\n>>> 1 == '1'\ntrue\n
\n\n

Equality and type comparison:

\n\n

Operator ===

\n\n

Returns true if both operands are equal and of the same type. It's generally \nbetter and safer if you compare this way, because there's no behind-the-scenes type conversions.

\n\n
>>> 1 === '1'\nfalse\n>>> 1 === 1\ntrue\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015e", + "creator": "Mr.G", + "createdAt": 1395230891000, + "text": "

*Operators === vs == *

\n\n
1 == true    =>    true\ntrue == true    =>    true\n1 === true    =>    false\ntrue === true    =>    true\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90160", + "creator": "vivek_nk", + "createdAt": 1397467705000, + "text": "

null and undefined are nothingness, that is,

\n\n
var a;\nvar b = null;\n
\n\n

Here a and b do not have values. Whereas, 0, false and '' are all values. One thing common beween all these are that they are all falsy values, which means they all satisfy falsy conditions.

\n\n

So, the 0, false and '' together form a sub-group. And on other hand, null & undefined form the second sub-group. Check the comparisons in the below image. null and undefined would equal. The other three would equal to each other. But, they all are treated as falsy conditions in JavaScript.

\n\n

\"Enter

\n\n

This is same as any object (like {}, arrays, etc.), non-empty string & Boolean true are all truthy conditions. But, they are all not equal.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9015f", + "creator": "Christian Hagelid", + "createdAt": 1395879486000, + "text": "

Here is a handy comparison table that shows the conversions that happen and the differences between == and ===.

\n\n

As the conclusion states:

\n\n
\n

\"Use three equals unless you fully understand the conversions that take\n place for two-equals.\"

\n
\n\n

http://dorey.github.io/JavaScript-Equality-Table/

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90162", + "creator": "garakchy", + "createdAt": 1407516763000, + "text": "

JavaScript has both strict and type–converting comparisons. A strict comparison (e.g., ===) is only true if the operands are of the same type. The more commonly used abstract comparison (e.g. ==) converts the operands to the same Type before making the comparison.

\n\n\n\n

For reference: Comparison operators (Mozilla Developer Network)

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90161", + "creator": "SNag", + "createdAt": 1399267285000, + "text": "

Here's an interesting visualisation of the equality comparison between == and ===.

\n

Source: https://github.com/dorey/JavaScript-Equality-Table (demo, unified demo)

\n
\n

var1 === var2

\n

When using === for JavaScript equality testing, everything is as is.
\nNothing gets converted before being evaluated.

\n

\"Equality

\n

var1 == var2

\n

When using == for JavaScript equality testing, some funky conversions take place.

\n

\"Equality

\n

Summary of equality in Javascript

\n

\"Equality

\n
\n

Conclusion:

\n

Always use ===, unless you fully understand the funky conversions that take place with ==.

\n", + "upvotes": 1269, + "upvoterUsernames": [], + "downvotes": 410, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f3082fcc3049e9133d", + "creator": "imkzh", + "createdAt": 1638261484000, + "text": "At least == comparisons are commutative (i.e. (a==b) === (b==a)) XD", + "upvotes": 2028, + "upvoterUsernames": [], + "downvotes": 2028, + "downvoterUsernames": [] + }, + { + "_id": "62f322f3082fcc3049e9133f", + "creator": "SNag", + "createdAt": 1650478529000, + "text": "@Feuermurmel: Can you give an example of when == is non-transitive?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f322f3082fcc3049e91341", + "creator": "Feuermurmel", + "createdAt": 1651054682000, + "text": "Oh, sorry for the late response. @SNag Definitely. Take a = [], b = false and c = [0].", + "upvotes": 517, + "upvoterUsernames": [], + "downvotes": 517, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90163", + "creator": "Aniket Thakur", + "createdAt": 1415944971000, + "text": "

Yes! It does matter.

\n\n

=== operator in javascript checks value as well as type where as == operator just checks the value (does type conversion if required).

\n\n

\"enter

\n\n

You can easily test it. Paste following code in an HTML file and open it in browser

\n\n
<script>\n\nfunction onPageLoad()\n{\n    var x = \"5\";\n    var y = 5;\n    alert(x === 5);\n};\n\n</script>\n\n</head>\n\n<body onload='onPageLoad();'>\n
\n\n

You will get 'false' in alert. Now modify the onPageLoad() method to alert(x == 5); you will get true.

\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90164", + "creator": "Sake Salverda", + "createdAt": 1417205209000, + "text": "

If you are making a web application or a secured page you should always use (only when possible)

\n\n
===\n
\n\n

because it will will check if it is the same content and if it is the same type!

\n\n

so when someone enters:

\n\n
var check = 1;\nif(check == '1') {\n    //someone continued with a string instead of number, most of the time useless for your webapp, most of the time entered by a user who does not now what he is doing (this will sometimes let your app crash), or even worse it is a hacker searching for weaknesses in your webapp!\n}\n
\n\n

but with

\n\n
var check = 1;\nif(check === 1) {\n    //some continued with a number (no string) for your script\n} else {\n    alert('please enter a real number');\n}\n
\n\n

a hacker will never get deeper in the system to find bugs and hack your app or your users

\n\n

my point it is that the

\n\n
===\n
\n\n

will add more security to your scripts

\n\n

of course you can also check if the entered number is valid, is a string, etc.. with other if statements inside the first example, but this is for at least me more easier to understand and use

\n\n

The reason I posted this is that the word 'more secure' or 'security' has never been said in this conversation (if you look at iCloud.com it uses 2019 times === and 1308 times ==, this also means that you sometimes have the use == instead of === because it will otherwise block your function, but as said in the begin you should use === as much as possible)

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90165", + "creator": "Amit", + "createdAt": 1426827948000, + "text": "

Simply

\n

== means comparison between operands with type coercion

\n

and

\n

=== means comparison between operands without type coercion.

\n

Type coercion in JavaScript means automatically converting data types to other data types.

\n

For example:

\n
123 == "123"  // Returns true, because JS coerces string "123" to number 123\n              // and then goes on to compare `123 == 123`.\n\n123 === "123" // Returns false, because JS does not coerce values of different types here.\n
\n", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90166", + "creator": "hopper", + "createdAt": 1427665702000, + "text": "

=== cares if the objects are the same. Therefore, new String(\"Hello world\") === \"Hello world\" returns false. However, == does not care about if the objects are the same; it just simply converts one argument into the other's type: if conversion is not possible, return false. Then new String(\"Hello world\") == \"Hello world\" returns true instead of false.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90167", + "creator": "Vikas", + "createdAt": 1431614700000, + "text": "

A simple example is

\n\n
2 == '2'  -> true, values are SAME because of type conversion.\n\n2 === '2'  -> false, values are NOT SAME because of no type conversion.\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90168", + "creator": "Akshay Khale", + "createdAt": 1437571722000, + "text": "

The javascript is a weakly typed language i.e. without any data-types as there in C,c++ eg. int, boolean, float etc. thus a variable can hold any type of value, that why these special comparison operators are there

\n\n

Eg

\n\n
var i = 20;var j = \"20\";\n
\n\n

if we apply comparison operators these variables result will be

\n\n
i==j //result is true\n
\n\n

or

\n\n
j != i//result is false\n
\n\n

for that we need a special comparison operators which checks for the value as well as for the data type of the variable

\n\n

if we do

\n\n
i===j //result is false\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016a", + "creator": "Samar Panda", + "createdAt": 1441461196000, + "text": "

Javascript execution flow diagram for strict equality / Comparison '==='

\n\n

\"Javascript

\n\n

Javascript execution flow diagram for non strict equality / comparison '=='

\n\n

\"Javascript

\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90169", + "creator": "CodeFarmer", + "createdAt": 1438839864000, + "text": "

My reasoning process using emacs org-mode and node.js to run a test.

\n\n
| use ==     | '' | '0' | false | 'false' | undefined | null | ' \\t\\r\\n ' |\n| ''         | x  | f   | t     | f       | f         | f    | f          |\n| '0'        |    | x   | t     | f       | f         | f    | f          |\n| false      |    |     | x     | f       | f         | f    | t          |\n| 'false'    |    |     |       | x       | f         | f    | f          |\n| undefined  |    |     |       |         | x         | t    | f          |\n| null       |    |     |       |         |           | x    | f          |\n| ' \\t\\r\\n ' |    |     |       |         |           |      | x          | \n\n\n\n| use ===    | '' | '0' | false | 'false' | undefined | null | ' \\t\\r\\n ' |\n| ''         | x  | f   | f     | f       | f         | f    | f          |\n| '0'        |    | x   | f     | f       | f         | f    | f          |\n| false      |    |     | x     | f       | f         | f    | f          |\n| 'false'    |    |     |       | x       | f         | f    | f          |\n| undefined  |    |     |       |         | x         | f    | f          |\n| null       |    |     |       |         |           | x    | f          |\n| ' \\t\\r\\n ' |    |     |       |         |           |      | x          |\n
\n\n

My test script below: run > node xxx.js

\n\n
var rowItems = ['', '0', false, 'false', undefined, null, ' \\t\\r\\n ']\nvar colItems = rowItems\n\nfor(var i = 0; i < rowItems.length; i++) {\n    for (var j = 0; j < colItems.length; j++) {\n        var r = (rowItems[i] === colItems[j]) ? true : false;\n        console.log(rowItems[i] + \" = \" + colItems[j] + \" \" + r + \" [\" + i + \"] ==> [\" + j + \"]\")\n    };\n}\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016c", + "creator": "Alex Gray", + "createdAt": 1463270801000, + "text": "

One unmentioned reason to use === - is in the case that you are co-existing with / cross-compiling to/from coffee-script. From The Little Book on CoffeeScript...

\n
\n

The weak equality comparison in JavaScript has some confusing behavior and is often the source of confusing bugs.

\n

The solution is to instead use the strict equality operator, which consists of three equal signs: ===. It works exactly like the normal equality operator, but without any type coercion. It's recommended to always use the strict equality operator, and explicitly convert types if needs be.

\n
\n

If you are regularly converting to and from coffee-script, you should just use ===. In fact, the coffee-script compiler will force you to...

\n
\n

CoffeeScript solves this by simply replacing all weak comparisons with strict ones, in other words converting all == comparators into ===. You can't do a a weak equality comparison in CoffeeScript, and you should explicitly convert types before comparing them if necessary.

\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016b", + "creator": "Dmitri Pavlutin", + "createdAt": 1451913998000, + "text": "

Yes, there is a big difference between equality == and identity === operators.
\nUsually the identity operator performs faster, because no types conversion is done. But if the values are of the same type, you'll see no difference.
\nCheck my post The legend of JavaScript equality operator, which explains the details, including the types conversion & comparison algorithms, with a lot of examples.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016d", + "creator": "Alexandr", + "createdAt": 1467379748000, + "text": "

always use '===' and you will avoid thousand of mistakes. nowadays using triple equality is more preferable by different style guides, because it compares taking into account type of operands.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016e", + "creator": "yanguya995", + "createdAt": 1467392402000, + "text": "

Javascript is loosely typed just like php is,

\n\n
var x = \"20\";\nvar y =20;\n\nif (x===y) // false\n
\n\n

This will always give you a false because even though the values of the variables are the same, the data types are not

\n\n

One is string the the other is int

\n\n
If(x==y)//true\n
\n\n

This however just checks if the content is the same, regardless of the data types...

\n\n

I dont want to say the values are equal because a string value cannot be equal to an int value logically

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9016f", + "creator": "Orri Scott", + "createdAt": 1467723236000, + "text": "
var a = new String(\"123\");\nvar b = \"123\";\n\nalert(a === b); // returns false !! (but they are equal and of the same type)\n
\n\n

Saw this in one of the answers.\na and b are not really the same type in this case, if you will check typeof(a)\n you will get 'object' and typeof(b) is 'string'.

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90170", + "creator": "Rotimi", + "createdAt": 1493035893000, + "text": "

First, some terminology about Javascript string equals: Double equals is officially known as the abstract equality comparison operator while triple equals is termed the strict equality comparison operator. The difference between them can be summed up as follows: Abstract equality will attempt to resolve the data types via type coercion before making a comparison. Strict equality will return false if the types are different. Consider the following example:

\n\n
console.log(3 == \"3\"); // true\nconsole.log(3 === \"3\"); // false.\nconsole.log(3 == \"3\"); // true\nconsole.log(3 === \"3\"); // false.\n
\n\n

Using two equal signs returns true because the string “3” is converted to the number 3 before the comparison is made. Three equal signs sees that the types are different and returns false. Here’s another:

\n\n
console.log(true == '1'); // true\nconsole.log(true === '1'); // false\nconsole.log(true == '1'); // true\nconsole.log(true === '1'); // false\n
\n\n

Again, the abstract equality comparison performs a type conversion. In this case both the boolean true and the string ‘1’ are converted to the number 1 and the result is true. Strict equality returns false.

\n\n

If you understand that you are well on your way to distinguishing between == and ===. However, there’s some scenarios where the behavior of these operators is non intuitive. Let’s take a look at some more examples:

\n\n
console.log(undefined == null); // true\nconsole.log(undefined === null); // false. Undefined and null are distinct types and are not interchangeable.\nconsole.log(undefined == null); // true     \nconsole.log(undefined === null); // false. Undefined and null are distinct types and are not interchangeable.\n\nconsole.log(true == 'true'); // false. A string will not be converted to a boolean and vice versa.\nconsole.log(true === 'true'); // false\nconsole.log(true == 'true'); // false. A string will not be converted to a boolean and vice versa.\nconsole.log(true === 'true'); // false\n
\n\n

The example below is interesting because it illustrates that string literals are different from string objects.

\n\n
console.log(\"This is a string.\" == new String(\"This is a string.\")); // true\nconsole.log(\"This is a string.\" === new String(\"This is a string.\")); // false\nconsole.log(\"This is a string.\" == new String(\"This is a string.\")); // true\nconsole.log(\"This is a string.\" === new String(\"This is a string.\")); // false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90171", + "creator": "Sharad Kale", + "createdAt": 1493355320000, + "text": "

== operator just compares the values not datatype.

\n

=== operator compare the values with comparison of its datatype.

\n

eg :

\n
1 == "1" //true\n\n1 === "1" //false\n
\n

This operator ("===") used in languages which performs automatic type cast eg. PHP, Javascript.
"===" operator helps to prevent unexpected comparison caused by automatic typecast.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f5082fcc3049e91351", + "creator": "mrmr68", + "createdAt": 1495713883000, + "text": "for '==' dataType is not important", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 128, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90172", + "creator": "Alireza", + "createdAt": 1494159032000, + "text": "

Use === if you want to compare couple of things in JavaScript, it's called strict equality, it means this will return true if only both type and value are the same, so there wouldn't be any unwanted type correction for you, if you using ==, you basically don't care about the type and in many cases you could face issues with loose equality comparison.

\n\n

Strict equality using ===

\n\n
\n

Strict equality compares two values for equality. Neither value is\n implicitly converted to some other value before being compared. If the\n values have different types, the values are considered unequal.\n Otherwise, if the values have the same type and are not numbers,\n they're considered equal if they have the same value. Finally, if both\n values are numbers, they're considered equal if they're both not NaN\n and are the same value, or if one is +0 and one is -0.

\n
\n\n
var num = 0;\nvar obj = new String('0');\nvar str = '0';\n\nconsole.log(num === num); // true\nconsole.log(obj === obj); // true\nconsole.log(str === str); // true\n\nconsole.log(num === obj); // false\nconsole.log(num === str); // false\nconsole.log(obj === str); // false\nconsole.log(null === undefined); // false\nconsole.log(obj === null); // false\nconsole.log(obj === undefined); // false\n
\n\n


Loose equality using ==

\n\n
\n

Loose equality compares two values for equality, after converting both\n values to a common type. After conversions (one or both sides may\n undergo conversions), the final equality comparison is performed\n exactly as === performs it. Loose equality is symmetric: A == B\n always has identical semantics to B == A for any values of A and B\n (except for the order of applied conversions).

\n
\n\n
var num = 0;\nvar obj = new String('0');\nvar str = '0';\n\nconsole.log(num == num); // true\nconsole.log(obj == obj); // true\nconsole.log(str == str); // true\n\nconsole.log(num == obj); // true\nconsole.log(num == str); // true\nconsole.log(obj == str); // true\nconsole.log(null == undefined); // true\n\n// both false, except in rare cases\nconsole.log(obj == null);\nconsole.log(obj == undefined);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90173", + "creator": "RïshïKêsh Kümar", + "createdAt": 1498386093000, + "text": "
\n

Different's Between = , = = , = = =

\n
\n\n\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90174", + "creator": "Narendra Kalekar", + "createdAt": 1501578833000, + "text": "

The reason it suggest to replace == with === is that the === operator is more reliable than ==. In our context reliable means === also goes for type checking. Considering the best programming practices we should always choose more reliable feature over less reliable one. Again whenever we think about exactly equal to operator most of the time, we are by default consider the type should be same. As === provides the same, we should go for it.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90175", + "creator": "Neil Meyer", + "createdAt": 1510829805000, + "text": "

Strict equality is for the most part better

\n\n

The fact that Javascript is a loosely typed language needs to be in the front of your mind constantly as you work with it. As long as the data structure is the same there really is no reason as to not use strict equality, with regular equality you often have an implicit conversion of values that happens automatically, this can have far-reaching effects on your code. It is very easy to have problems with this conversion seeing as they happen automatically.

\n\n

With strict equality there is no automatic implicit conversion as the values must already be of the correct data structure.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90176", + "creator": "Bekim Bacaj", + "createdAt": 1511725121000, + "text": "

The dilemma of \"Should I use == or === in JavaScript comparison\" is equal or analogous to a question of: \"Should I use a 'spoon' or a 'fork' for eating.

\n\n

The only reasonable answer to this question is that

\n\n
    \n
  1. You should use Dynamic Type comparison e.g.:== for loose Type comparisons.
  2. \n
  3. You should use Static Type comparison e.g.:=== for strong Type comparisons.
  4. \n
\n\n

That's because they are not the same. They don't have the same purpose and are not meant to be used for the same purpose.

\n\n

Of course both 'forks' and 'spoons' are meant for 'eating', but you will chose to use them accordingly to what you've been served to eat.

\n\n

Meaning: you'll resolve to using a 'spoon' i.e.: == for having a 'soup', and / or the 'fork' i.e.: === for picking.

\n\n

Asking if it is better to use a \"fork\" or a \"spoon\" for \"eating\" - is equall to asking if it is better to use a static [===] versus dynamic [==] eq., op. in JS. Both questions are equally wrong and reflect a very narrow or shallow understanding of the subject in question.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2039088, + "uvac": 2039133 + } + }, + { + "_id": "62f321bb082fcc3049e8fec5", + "title": "How do I copy to the clipboard in JavaScript?", + "title-lowercase": "how do i copy to the clipboard in javascript?", + "creator": "Santiago Corredoira", + "createdAt": 1230642545000, + "status": "open", + "text": "

How do I copy text to the clipboard (multi-browser)?

\n

Related: How does Trello access the user's clipboard?

\n", + "upvotes": 4451, + "upvoterUsernames": [], + "downvotes": 273, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2608917, + "answers": 67, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e9047c", + "creator": "bandi", + "createdAt": 1230644025000, + "text": "

Reading and modifying the clipboard from a webpage raises security and privacy concerns. However, in Internet Explorer, it is possible to do it. I found this example snippet:

\n\n

\r\n
\r\n
    <script type=\"text/javascript\">\r\n        function select_all(obj) {\r\n            var text_val=eval(obj);\r\n            text_val.focus();\r\n            text_val.select();\r\n            r = text_val.createTextRange();\r\n            if (!r.execCommand) return; // feature detection\r\n            r.execCommand('copy');\r\n        }\r\n    </script>\r\n    <input value=\"http://www.sajithmr.com\"\r\n     onclick=\"select_all(this)\" name=\"url\" type=\"text\" />
\r\n
\r\n
\r\n

\n", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32467082fcc3049e917c4", + "creator": "RozzA", + "createdAt": 1335290558000, + "text": "plz explain what execCommand(\\\\’copy\\\\’); does, if not copy to clipboard for IE ? @mrBorna", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9047b", + "creator": "Stormenet", + "createdAt": 1230643602000, + "text": "

As far as I know that only works in Internet Explorer.

\n\n

See also Dynamic Tools - JavaScript Copy To Clipboard, but it requires the user to change the configuration first and even then it doesn't seems to work.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32467082fcc3049e917c8", + "creator": "fnkr", + "createdAt": 1368352025000, + "text": "This is not multi-browser like requested.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9047d", + "creator": "DDM", + "createdAt": 1230644331000, + "text": "

In browsers other than Internet Explorer you need to use a small Flash object to manipulate the clipboard, e.g.

\n\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32468082fcc3049e917cb", + "creator": "Mottie", + "createdAt": 1253460053000, + "text": "This is outdated now... check out the suggestion by GvS", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917cd", + "creator": "TheEmirOfGroofunkistan", + "createdAt": 1255027913000, + "text": "The suggestion by GvS uses a flash movie? Isn't that the same idea?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9047e", + "creator": "Chase Seibert", + "createdAt": 1230647618000, + "text": "

The other methods will copy plain text to the clipboard. To copy HTML (i.e., you can paste results into a WYSIWYG editor), you can do the following in Internet Explorer only. This is is fundamentally different from the other methods, as the browser actually visibly selects the content.

\n
// Create an editable DIV and append the HTML content you want copied\nvar editableDiv = document.createElement("div");\nwith (editableDiv) {\n    contentEditable = true;\n}\neditableDiv.appendChild(someContentElement);\n\n// Select the editable content and copy it to the clipboard\nvar r = document.body.createTextRange();\nr.moveToElementText(editableDiv);\nr.select();\nr.execCommand("Copy");\n\n// Deselect, so the browser doesn't leave the element visibly selected\nr.moveToElementText(someHiddenDiv);\nr.select();\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9047f", + "creator": "PhiLho", + "createdAt": 1230648456000, + "text": "

It looks like you took the code from Greasemonkey\\JavaScript Copy to Clipboard button or the original source of this snippet...

\n

This code was for Greasemonkey, hence the unsafeWindow. And I guess the syntax error in Internet Explorer comes from the const keyword which is specific to Firefox (replace it with var).

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32468082fcc3049e917d1", + "creator": "ProfK", + "createdAt": 1551331131000, + "text": "The const keyword is not specific to Firefox, but to a specific version of JavaScript, which not all browsers implement properly.", + "upvotes": 360, + "upvoterUsernames": [], + "downvotes": 360, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90480", + "creator": "matthuhiggins", + "createdAt": 1268856759000, + "text": "

As of Flash 10, you can only copy to clipboard if the action originates from user interaction with a Flash object. (Read the related section from Adobe's Flash 10 announcement.)

\n

The solution is to overlay a Flash object above the Copy button, or whatever element initiates the copy. ZeroClipboard is currently the best library with this implementation. Experienced Flash developers may just want to make their own library.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90482", + "creator": "xiniu", + "createdAt": 1295284185000, + "text": "

I had the same problem building a custom grid edit from (something like Excel) and compatibility with Excel. I had to support selecting multiple cells, copying and pasting.

\n\n

Solution: create a textarea where you will be inserting data for the user to copy (for me when the user is selecting cells), set focus on it (for example, when user press Ctrl) and select the whole text.

\n\n

So, when the user hit Ctrl + C he/she gets copied cells he/she selected. After testing just resizing the textarea to one pixel (I didn't test if it will be working on display:none). It works nicely on all browsers, and it is transparent to the user.

\n\n

Pasting - you could do same like this (differs on your target) - keep focus on textarea and catch paste events using onpaste (in my project I use textareas in cells to edit).

\n\n

I can't paste an example (commercial project), but you get the idea.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90481", + "creator": "Brent Matzelle", + "createdAt": 1287326435000, + "text": "

If you want a really simple solution (takes less than 5 minutes to integrate) and looks good right out of the box, then Clippy is a nice alternative to some of the more complex solutions.

\n

It was written by a cofounder of GitHub. Example Flash embed code below:

\n
<object\n    classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"\n    width="110"\n    height="14"\n    id="clippy">\n\n    <param name="movie" value="/flash/clippy.swf"/>\n    <param name="allowScriptAccess" value="always"/>\n    <param name="quality" value="high"/>\n    <param name="scale" value="noscale"/>\n    <param NAME="FlashVars" value="text=#{text}"/>\n    <param name="bgcolor" value="#{bgcolor}"/>\n    <embed\n        src="/flash/clippy.swf"\n        width="110"\n        height="14"\n        name="clippy"\n        quality="high"\n        allowScriptAccess="always"\n        type="application/x-shockwave-flash"\n        pluginspage="http://www.macromedia.com/go/getflashplayer"\n        FlashVars="text=#{text}"\n        bgcolor="#{bgcolor}"/>\n</object>\n
\n

Remember to replace #{text} with the text you need copied, and #{bgcolor} with a color.

\n", + "upvotes": 163, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32468082fcc3049e917d6", + "creator": "Radek", + "createdAt": 1306149542000, + "text": "For anyone interested, check Clippy being used on GitHub when copying the URL for the repo.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90483", + "creator": "SteamDev", + "createdAt": 1304461021000, + "text": "

From one of the projects I've been working on, a jQuery copy-to-clipboard plugin that utilizes the ZeroClipboard library.

\n

It is easier to use than the native Zero Clipboard plugin if you're a heavy jQuery user.

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32468082fcc3049e917d9", + "creator": "RozzA", + "createdAt": 1335290371000, + "text": "92kb isn't all that big really, it works fast & you can use text() instead of innerHTML() if you like..", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917da", + "creator": "Max Nanasy", + "createdAt": 1366337529000, + "text": "@John You complain about jQuery not being JavaScripty enough in an answer that uses Flash ;)", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917dc", + "creator": "Orbiting Eden", + "createdAt": 1371582785000, + "text": "innerHTML is better than alternatives in most cases. Get off your high horse! It is faster, more efficient and doesn't require a page re-render.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917de", + "creator": "Adrien Be", + "createdAt": 1403091658000, + "text": "Here is a minimalistic usage of ZeroClipboard JS library: jsfiddle.net/N36uH", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917df", + "creator": "bhan", + "createdAt": 1428102286000, + "text": "@AdrienBe none of your examples work for me in either Firefox or Chrome", + "upvotes": 1220, + "upvoterUsernames": [], + "downvotes": 1220, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917e1", + "creator": "Gui Imamura", + "createdAt": 1437659833000, + "text": "@AdrienBe They don't work here either. No errors on console, just warnings. Most of them are from jshint.js", + "upvotes": 433, + "upvoterUsernames": [], + "downvotes": 433, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917e3", + "creator": "Adrien Be", + "createdAt": 1437701674000, + "text": "@GuiImamura and bhan: sorry guys I not able to help for now. Try the demo on the ZeroClipboard page and see if you can use that code to get started.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917e4", + "creator": "MightyPork", + "createdAt": 1458597282000, + "text": "The page linked is now dead, with only some scam-vertising junk", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90485", + "creator": "Oliver Bock", + "createdAt": 1313137052000, + "text": "

This is an expansion of Chase Seibert's answer, with the advantage that it will work for IMAGE and TABLE elements, not just DIVs on Internet Explorer 9.

\n
if (document.createRange) {\n    // Internet Explorer 9 and modern browsers\n    var r = document.createRange();\n    r.setStartBefore(to_copy);\n    r.setEndAfter(to_copy);\n    r.selectNode(to_copy);\n    var sel = window.getSelection();\n    sel.addRange(r);\n    document.execCommand('Copy');  // Does nothing on Firefox\n} else {\n    // Internet Explorer 8 and earlier. This stuff won't work\n    // on Internet Explorer 9.\n    // (unless forced into a backward compatibility mode,\n    // or selecting plain divs, not img or table).\n    var r = document.body.createTextRange();\n    r.moveToElementText(to_copy);\n    r.select()\n    r.execCommand('Copy');\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90486", + "creator": "mrBorna", + "createdAt": 1313522567000, + "text": "

It seems I misread the question, but for reference, you can extract a range of the DOM (not to the clipboard; compatible with all modern browsers), and combine it with the oncopy, onpaste, and onbeforepaste events to get the clipboard behaviour. Here's the code to achieve this:

\n
function clipBoard(sCommand) {\n  var oRange = contentDocument.createRange();\n  oRange.setStart(startNode, startOffset);\n  oRange.setEnd(endNode, endOffset);\n\n  /* This is where the actual selection happens.\n     in the above, startNode and endNode are\n     DOM nodes defining the beginning and\n     end of the "selection" respectively.\n\n     startOffset and endOffset are constants\n     that are defined as follows:\n\n         END_TO_END: 2\n         END_TO_START: 3\n         NODE_AFTER: 1\n         NODE_BEFORE: 0\n         NODE_BEFORE_AND_AFTER: 2\n         NODE_INSIDE: 3\n         START_TO_END: 1\n         START_TO_START: 0\n\n     And it would be used like oRange.START_TO_END\n  */\n\n  switch(sCommand) {\n\n    case "cut":\n      this.oFragment = oRange.extractContents();\n      oRange.collapse();\n      break;\n\n    case "copy":\n      this.oFragment = oRange.cloneContents();\n      break;\n\n    case "paste":\n      oRange.deleteContents();\n      var cloneFragment = this.oFragment.cloneNode(true)\n      oRange.insertNode(cloneFragment);\n      oRange.collapse();\n      break;\n  }\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90484", + "creator": "Jarek Milewski", + "createdAt": 1305792366000, + "text": "

Automatic copying to the clipboard may be dangerous, and therefore most browsers (except Internet Explorer) make it very difficult. Personally, I use the following simple trick:

\n
function copyToClipboard(text) {\n  window.prompt("Copy to clipboard: Ctrl+C, Enter", text);\n}\n
\n

The user is presented with the prompt box, where the text to be copied is already selected. Now it's enough to press Ctrl + C and Enter (to close the box) -- and voila!

\n

Now the clipboard copy operation is safe, because the user does it manually (but in a pretty straightforward way). Of course, it works in all browsers.

\n

\r\n
\r\n
<button id=\"demo\" onclick=\"copyToClipboard(document.getElementById('demo').innerHTML)\">This is what I want to copy</button>\n\n<script>\n  function copyToClipboard(text) {\n    window.prompt(\"Copy to clipboard: Ctrl+C, Enter\", text);\n  }\n</script>
\r\n
\r\n
\r\n

\n", + "upvotes": 1465, + "upvoterUsernames": [], + "downvotes": 109, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32468082fcc3049e917e9", + "creator": "Denilson Sá Maia", + "createdAt": 1315103561000, + "text": "But there is a limit on the amount of characters displayed in that dialog, and thus there is a limit on the amount of data to be copied.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917eb", + "creator": "Aram Kocharyan", + "createdAt": 1319360216000, + "text": "Clever, but this only supports single line.", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917ed", + "creator": "arkon", + "createdAt": 1349133817000, + "text": "First of all, slick solution =] But couldn't this functionality be replicated without js? (Not as simply of course.)", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917ef", + "creator": "RasTheDestroyer", + "createdAt": 1378320715000, + "text": "If your text is over 2000 characters it will be truncated, but for smaller text samples it works great", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917f1", + "creator": "Marcus Pope", + "createdAt": 1379546350000, + "text": "@RasTheDestroyer - Truncation at 2k chars seems to be a Chrome issue, but it's good to know regardless", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917f3", + "creator": "Dibish", + "createdAt": 1386932898000, + "text": "Its so simple way to do it..Thanks..But the text is not pr selected..is there any way to preselect the text?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917f5", + "creator": "borisdiakur", + "createdAt": 1387364668000, + "text": "Doesn't work in Firefox 25.0 on OS X. I get the prompt, but the text field is empty. In Chrome it works.", + "upvotes": 4205, + "upvoterUsernames": [], + "downvotes": 4205, + "downvoterUsernames": [] + }, + { + "_id": "62f32468082fcc3049e917f7", + "creator": "gab06", + "createdAt": 1599606411000, + "text": "For mobile, in Chrome, when I select the text in the prompt it apparently doesn't allow me to copy, just cut.", + "upvotes": 203, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90487", + "creator": "Matthew Scragg", + "createdAt": 1366756323000, + "text": "

I like this one:

\n\n
<input onclick=\"this.select();\" type='text' value='copy me' />\n
\n\n

If a user doesn't know how to copy text in their OS, then it's likely they don't know how to paste either. So just have it automatically selected, leaving the rest to the user.

\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90488", + "creator": "UserBSS1", + "createdAt": 1372231701000, + "text": "

If the copied link has to be pasted on the same site, then a simple solution is to highlight the text before pressing the simple HTML copy button and then on pressing it, the text content is stored in a session. And wherever it is to be pasted, there is a paste button.

\n\n

**I know, it's not a persistent and universal solution, but it's something :)

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e917fb", + "creator": "UserBSS1", + "createdAt": 1375867665000, + "text": "@Jimbo, solution is below: for session based clipboard", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e917fd", + "creator": "StanE", + "createdAt": 1415502922000, + "text": "Remember that the session is limited to 5 MiB (afaik).", + "upvotes": 397, + "upvoterUsernames": [], + "downvotes": 397, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90489", + "creator": "UserBSS1", + "createdAt": 1375791024000, + "text": "

Here is the simple Ajax/session based clipboard for the same website.

\n

Note that the session must be enabled & valid and this solution works for the same site. I tested it on CodeIgniter, but I ran into session/Ajax problem, but this solved that problem too. If you don't want to play with sessions, use a database table.

\n

JavaScript/jQuery

\n
<script type="text/javascript">\n    $(document).ready(function() {\n\n        $("#copy_btn_id").click(function(){\n\n            $.post("<?php echo base_url();?>ajax/foo_copy/"+$(this).val(), null,\n                function(data){\n                    // Copied successfully\n                }, "html"\n            );\n        });\n\n        $("#paste_btn_id").click(function() {\n\n           $.post("<?php echo base_url();?>ajax/foo_paste/", null,\n               function(data) {\n                   $('#paste_btn_id').val(data);\n               }, "html"\n           );\n        });\n    });\n</script>\n
\n

HTML content

\n
<input type='text' id='copy_btn_id' onclick='this.select();'  value='myvalue' />\n<input type='text' id='paste_btn_id' value='' />\n
\n

PHP code

\n
<?php\n    class Ajax extends CI_Controller {\n\n        public function foo_copy($val){\n            $this->session->set_userdata(array('clipboard_val' => $val));\n        }\n\n        public function foo_paste(){\n            echo $this->session->userdata('clipboard_val');\n            exit();\n        }\n    }\n?>\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e917ff", + "creator": "Luke A. Leber", + "createdAt": 1431123232000, + "text": "This is the only answer on here that doesn't involve coercing the browser into doing something that it wasn't designed to do. Solid +1.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9048a", + "creator": "dvallejo", + "createdAt": 1379109369000, + "text": "

My bad. This only works in Internet Explorer.

\n

Here's yet another way to copy text:

\n
<p>\n    <a onclick="window.clipboardData.setData('text', document.getElementById('Test').innerText);">Copy</a>\n</p>\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9048b", + "creator": "supNate", + "createdAt": 1382355579000, + "text": "

In case you're reading text from the clipboard in a Chrome extension, with 'clipboardRead' permission allowed, you can use the below code:

\n\n
function readTextFromClipboardInChromeExtension() {\n    var ta = $('<textarea/>');\n    $('body').append(ta);\n    ta.focus();\n    document.execCommand('paste');\n    var text = ta.val();\n    ta.blur();\n    ta.remove();\n    return text;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9048c", + "creator": "Justin", + "createdAt": 1385066485000, + "text": "

ZeroClipboard is the best cross-browser solution I've found:

\n
<div id="copy" data-clipboard-text="Copy Me!">Click to copy</div>\n<script src="ZeroClipboard.js"></script>\n<script>\n  var clip = new ZeroClipboard( document.getElementById('copy') );\n</script>\n
\n

If you need non-Flash support for iOS you just add a fall-back:

\n
clip.on( 'noflash', function ( client, args ) {\n    $("#copy").click(function(){\n        var txt = $(this).attr('data-clipboard-text');\n        prompt ("Copy link, then click OK.", txt);\n    });\n});\n
\n

http://zeroclipboard.org/

\n

https://github.com/zeroclipboard/ZeroClipboard

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e91803", + "creator": "Raptor", + "createdAt": 1390818598000, + "text": "cross-browser with Flash ? not working in iOS and Android 4.4", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e91805", + "creator": "Justin", + "createdAt": 1390844314000, + "text": "See updated answer. This allows less steps for flash-users and a fall-back for everyone else.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e91807", + "creator": "vsync", + "createdAt": 1414409137000, + "text": "it has a billion lines of code. it's absolutely ridicules. better not to do it at all than including such a monster in a project", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9048d", + "creator": "drzaus", + "createdAt": 1404311308000, + "text": "

In Chrome you can use copy('the text or variable etc'). While this isn't cross-browser (and doesn't work in a snippet?), you could add it to the other cross-browser answers.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e91808", + "creator": "EricP", + "createdAt": 1412187800000, + "text": "This only works when entered in the chrome command line. When I add it to my code, the copy function is not defined.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e9180a", + "creator": "drzaus", + "createdAt": 1412345331000, + "text": "@JoeCoder thank you for explicitly pointing that out, as I meant that to be apparent from the link, but in retrospect it's not very obvious.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9048e", + "creator": "Richard Shurtz", + "createdAt": 1417638666000, + "text": "

I have recently written a technical blog post on this very problem (I work at Lucidchart and we recently did an overhaul on our clipboard).

\n

Copying plain text to the clipboard is relatively simple, assuming you attempt to do it during a system copy event (user presses Ctrl + C or uses the browser's menu).

\n
var isIe = (navigator.userAgent.toLowerCase().indexOf("msie")    != -1 ||\n            navigator.userAgent.toLowerCase().indexOf("trident") != -1);\n\ndocument.addEventListener('copy', function(e) {\n    var textToPutOnClipboard = "This is some text";\n    if (isIe) {\n        window.clipboardData.setData('Text', textToPutOnClipboard);\n    } else {\n        e.clipboardData.setData('text/plain', textToPutOnClipboard);\n    }\n    e.preventDefault();\n});\n
\n

Putting text on the clipboard not during a system copy event is much more difficult. It looks like some of these other answers reference ways to do it via Flash, which is the only cross-browser way to do it (so far as I understand).

\n

Other than that, there are some options on a browser-by-browser basis.

\n

This is the most simple in Internet Explorer, where you can access the clipboardData object at anytime from JavaScript via:

\n
window.clipboardData\n
\n

(When you attempt to do this outside of a system cut, copy, or paste event, however, Internet Explorer will prompt the user to grant the web application clipboard permission.)

\n

In Chrome, you can create a Chrome extension that will give you clipboard permissions (this is what we do for Lucidchart). Then for users with your extension installed you'll just need to fire the system event yourself:

\n
document.execCommand('copy');\n
\n

It looks like Firefox has some options that allow users to grant permissions to certain sites to access the clipboard, but I haven't tried any of these personally.

\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e9180c", + "creator": "Brock Adams", + "createdAt": 1486186357000, + "text": "A problem with this is that it hijacks other normal copy events.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e9180e", + "creator": "odinho - Velmont", + "createdAt": 1543836705000, + "text": "NB! This browser sniffing is BAD. Do feature sniffing. You're making it hard for IE to update.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9048f", + "creator": "sinister", + "createdAt": 1431002178000, + "text": "

For the security reason you can't do that. You must choose Flash for copying to the clipboard.

\n\n

I suggest this one: http://zeroclipboard.org/

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90490", + "creator": "Bart Burg", + "createdAt": 1436451865000, + "text": "

This is a bit of a combination between the other answers.

\n\n
var copyToClipboard = function(textToCopy){\n    $(\"body\")\n        .append($('<textarea name=\"fname\" class=\"textToCopyInput\"/>' )\n        .val(textToCopy))\n        .find(\".textToCopyInput\")\n        .select();\n      try {\n        var successful = document.execCommand('copy');\n        var msg = successful ? 'successful' : 'unsuccessful';\n        alert('Text copied to clipboard!');\n      } catch (err) {\n          window.prompt(\"To copy the text to clipboard: Ctrl+C, Enter\", textToCopy);\n      }\n     $(\".textToCopyInput\").remove();\n}\n
\n\n

It uses jQuery, but it doesn't have to of course. You can change that if you want. I just had jQuery to my disposal. You can also add some CSS to make sure the input doesn't show. For instance something like:

\n\n
.textToCopyInput{opacity: 0; position: absolute;}\n
\n\n

Or of course you could also do some inline styling

\n\n
.append($('<textarea name=\"fname\" style=\"opacity: 0;  position: absolute;\" class=\"textToCopyInput\"/>' )\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32469082fcc3049e91812", + "creator": "user1108948", + "createdAt": 1454698186000, + "text": "How to copy directly from a variable data .i.e.: var str = "word"; ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e91814", + "creator": "Voyager", + "createdAt": 1556175055000, + "text": "Variable msg is unused", + "upvotes": 686, + "upvoterUsernames": [], + "downvotes": 686, + "downvoterUsernames": [] + }, + { + "_id": "62f32469082fcc3049e91816", + "creator": "Voyager", + "createdAt": 1556179667000, + "text": "Better to use '<textarea class="textToCopyInput"/></textarea>' in case textToCopy contains \\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90491", + "creator": "a coder", + "createdAt": 1439307217000, + "text": "

clipboard.js is a small, non-Flash, utility that allows copying of text or HTML data to the clipboard. It's very easy to use, just include the .js and use something like this:

\n\n
<button id='markup-copy'>Copy Button</button>\n\n<script>\ndocument.getElementById('markup-copy').addEventListener('click', function() {\n  clipboard.copy({\n    'text/plain': 'Markup text. Paste me into a rich text editor.',\n    'text/html': '<i>here</i> is some <b>rich text</b>'\n  }).then(\n    function(){console.log('success'); },\n    function(err){console.log('failure', err);\n  });\n\n});\n</script>\n
\n\n

clipboard.js is also on GitHub.

\n\n
\n

Note: This has been deprecated now. Migrate to here.

\n
\n", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90492", + "creator": "kosmos", + "createdAt": 1441713112000, + "text": "

In addition to Dean Taylor's updated answer (July 2015), I wrote a jQuery method looking like his example.

\n\n

jsFiddle

\n\n
/**\n* Copies the current selected text to the SO clipboard\n* This method must be called from an event to work with `execCommand()`\n* @param {String} text Text to copy\n* @param {Boolean} [fallback] Set to true shows a prompt\n* @return Boolean Returns `true` if the text was copied or the user clicked on accept (in prompt), `false` otherwise\n*/\nvar CopyToClipboard = function(text, fallback){\n    var fb = function () {\n        $t.remove();\n        if (fallback !== undefined && fallback) {\n            var fs = 'Please, copy the following text:';\n            if (window.prompt(fs, text) !== null) return true;\n        }\n        return false;\n    };\n    var $t = $('<textarea />');\n    $t.val(text).css({\n        width: '100px',\n        height: '40px'\n    }).appendTo('body');\n    $t.select();\n    try {\n        if (document.execCommand('copy')) {\n            $t.remove();\n            return true;\n        }\n        fb();\n    }\n    catch (e) {\n        fb();\n    }\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90494", + "creator": "ryanpcmcquen", + "createdAt": 1444450380000, + "text": "

I was going to use clipboard.js, but it doesn't have any mobile solution in place (yet) ... so I wrote a super small library:

\n

Cheval

\n

This will either copy the text (desktop, Android, and Safari 10+), or at the very least, select the text (older versions of iOS). Minified it is just over 1 kB. In desktop Safari (before version 10) it tells the user to "Press Command + C to copy". You also don't need to write any JavaScript to use it.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90493", + "creator": "Yaroslav Yakovlev", + "createdAt": 1444037350000, + "text": "

Update 2015: currently there's a way to use document.execCommand to work with the clipboard.

\n

clipboard.js provides a cross-browser way to work with the clipboard (browser support).

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90496", + "creator": "Jeff Baker", + "createdAt": 1446695433000, + "text": "

Since Chrome 42+ and Firefox 41+ now support the document.execCommand('copy') command, I created a couple of functions for a cross-browser copy-to-clipboard ability using a combination of Tim Down's old answer and Google Developer's answer:

\n

\r\n
\r\n
function selectElementContents(el) {\n    // Copy textarea, pre, div, etc.\n    if (document.body.createTextRange) {\n        // Internet Explorer\n        var textRange = document.body.createTextRange();\n        textRange.moveToElementText(el);\n        textRange.select();\n        textRange.execCommand(\"Copy\");\n    }\n    else if (window.getSelection && document.createRange) {\n        // Non-Internet Explorer\n        var range = document.createRange();\n        range.selectNodeContents(el);\n        var sel = window.getSelection();\n        sel.removeAllRanges();\n        sel.addRange(range);\n        try {\n            var successful = document.execCommand('copy');\n            var msg = successful ? 'successful' : 'unsuccessful';\n            console.log('Copy command was ' + msg);\n        }\n        catch (err) {\n            console.log('Oops, unable to copy');\n        }\n    }\n} // end function selectElementContents(el)\n\nfunction make_copy_button(el) {\n    var copy_btn = document.createElement('input');\n    copy_btn.type = \"button\";\n    el.parentNode.insertBefore(copy_btn, el.nextSibling);\n    copy_btn.onclick = function() {\n        selectElementContents(el);\n    };\n\n    if (document.queryCommandSupported(\"copy\") || parseInt(navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)[2]) >= 42) {\n        // Copy works with Internet Explorer 4+, Chrome 42+, Firefox 41+, Opera 29+\n        copy_btn.value = \"Copy to Clipboard\";\n    }\n    else {\n        // Select only for Safari and older Chrome, Firefox and Opera\n        copy_btn.value = \"Select All (then press Ctrl + C to Copy)\";\n    }\n}\n/* Note: document.queryCommandSupported(\"copy\") should return \"true\" on browsers that support copy,\n    but there was a bug in Chrome versions 42 to 47 that makes it return \"false\".  So in those\n    versions of Chrome feature detection does not work!\n    See https://code.google.com/p/chromium/issues/detail?id=476508\n*/\n\nmake_copy_button(document.getElementById(\"markup\"));
\r\n
<pre id=\"markup\">\n  Text that can be copied or selected with cross browser support.\n</pre>
\r\n
\r\n
\r\n

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246a082fcc3049e9181c", + "creator": "Jeff Baker", + "createdAt": 1447899077000, + "text": "You are correct @ChristianEngel . I have removed the second one. I don't know how it got in there.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246a082fcc3049e9181e", + "creator": "ASHISH KUMAR", + "createdAt": 1624025238000, + "text": "Hello jeff what if I want to customize "copy to clipboard". Please help!", + "upvotes": 299, + "upvoterUsernames": [], + "downvotes": 299, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90495", + "creator": "CodecPM", + "createdAt": 1446031026000, + "text": "

I have used clipboard.js.

\n

We can get it on npm:

\n
npm install clipboard --save\n
\n

And also on Bower

\n
bower install clipboard --save\n
\n

\r\n
\r\n
new ClipboardJS(\"#btn1\");\n\ndocument.querySelector(\"#btn2\").addEventListener(\"click\", () => document.querySelector(\"#btn1\").dataset.clipboardText = Math.random());
\r\n
<script src=\"https://cdn.jsdelivr.net/npm/clipboard@2.0.8/dist/clipboard.min.js\"></script>\n\n<button id=\"btn1\" data-clipboard-text=\"Text to copy goes here\">\n    Copy to clipboard\n</button>\n<button id=\"btn2\">Click here to change data-clipboard-text</button>\n\n<br /><br />\n\n<input type=\"text\" placeholder=\"Paste here to see clipboard\" />
\r\n
\r\n
\r\n

\n

More usage & examples are at https://zenorocha.github.io/clipboard.js/.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246a082fcc3049e91820", + "creator": "BENARD Patrick", + "createdAt": 1452007168000, + "text": "I was afraid that it wasn't compatible with dynamic content, but it is ;-) I think it's the better solution, NOW, than the old one of 2008.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90497", + "creator": "callmemath", + "createdAt": 1464188779000, + "text": "

After searching a solution that supports Safari and other browsers (Internet Explorer 9 and later),

\n\n

I use the same as GitHub: ZeroClipboard

\n\n

Example:

\n\n

http://zeroclipboard.org/index-v1.x.html

\n\n

HTML

\n\n
<html>\n  <body>\n    <button id=\"copy-button\" data-clipboard-text=\"Copy Me!\" title=\"Click to copy me.\">Copy to Clipboard</button>\n    <script src=\"ZeroClipboard.js\"></script>\n    <script src=\"main.js\"></script>\n  </body>\n</html>\n
\n\n

JavaScript

\n\n
var client = new ZeroClipboard(document.getElementById(\"copy-button\"));\n\nclient.on(\"ready\", function (readyEvent) {\n    // alert( \"ZeroClipboard SWF is ready!\" );\n\n    client.on(\"aftercopy\", function (event) {\n        // `this` === `client`\n        // `event.target` === the element that was clicked\n        event.target.style.display = \"none\";\n        alert(\"Copied text to clipboard: \" + event.data[\"text/plain\"]);\n    });\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90498", + "creator": "Codebeat", + "createdAt": 1481559722000, + "text": "

There are many answers already, however like to add one (jQuery). Works great on any browser, also mobile ones (i.e., prompts about security, but when you accept it just works fine).

\n\n
function appCopyToClipBoard(sText)\n{\n    var oText = false,\n        bResult = false;\n    try\n    {\n        oText = document.createElement(\"textarea\");\n        $(oText).addClass('clipboardCopier').val(sText).insertAfter('body').focus();\n        oText.select();\n        document.execCommand(\"Copy\");\n        bResult = true;\n    }\n    catch(e) {\n    }\n\n    $(oText).remove();\n    return bResult;\n}\n
\n\n

In your code:

\n\n
if (!appCopyToClipBoard('Hai there! This is copied to the clipboard.'))\n{\n    alert('Sorry, copy to clipboard failed.');\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90499", + "creator": "NewToIOS", + "createdAt": 1483928537000, + "text": "

I had to copy the non-input boxes text (text within any div/span tag) from the page and came up with following code. The only trick is to have a hidden field, but as type TEXT. It won't work with type hidden.

\n\n
function copyToClipboard(sID) {\n    var aField = document.getElementById(\"hiddenField\");\n\n    aField.hidden = false;\n    aField.value  = document.getElementById(sID).textContent;\n    aField.select();\n    document.execCommand(\"copy\");\n    alert(\"Following text has been copied to the clipboard.\\n\\n\" + aField.value);\n    aField.hidden = true;\n}\n
\n\n

And in the HTML add the following:

\n\n
input type=\"text\" id=\"hiddenField\" style=\"width:5px;border:0\" />\n...\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9049a", + "creator": "Vasileios Pallas", + "createdAt": 1484737022000, + "text": "

I found the following solution:

\n\n

I have the text in a hidden input. Because setSelectionRange doesn't work on hidden inputs, I changed temporarily the type to text, copied the text, and then made it hidden again. If you want to copy the text from an element, you can pass it to the function and save its content in the target variable.

\n\n
jQuery('#copy').on('click', function () {\n    copyToClipboard();\n});\n\nfunction copyToClipboard() {\n    var target = jQuery('#hidden_text');\n\n    // Make it visible, so can be focused\n    target.attr('type', 'text');\n    target.focus();\n    // Select all the text\n    target[0].setSelectionRange(0, target.val().length);\n\n    // Copy the selection\n    var succeed;\n    try {\n        succeed = document.execCommand(\"copy\");\n    }\n    catch (e) {\n        succeed = false;\n    }\n\n    // Hide input again\n    target.attr('type', 'hidden');\n\n    return succeed;\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9049d", + "creator": "Dominic", + "createdAt": 1500997893000, + "text": "

I've put together what I think is the best one.

\n\n

Here it is:

\n
const copyToClipboard = (function initClipboardText() {\n  const textarea = document.createElement('textarea');\n\n  // Move it off-screen.\n  textarea.style.cssText = 'position: absolute; left: -99999em';\n\n  // Set to readonly to prevent mobile devices opening a keyboard when\n  // text is .select()'ed.\n  textarea.setAttribute('readonly', true);\n\n  document.body.appendChild(textarea);\n\n  return function setClipboardText(text) {\n    textarea.value = text;\n\n    // Check if there is any content selected previously.\n    const selected = document.getSelection().rangeCount > 0 ?\n      document.getSelection().getRangeAt(0) : false;\n\n    // iOS Safari blocks programmatic execCommand copying normally, without this hack.\n    // https://stackoverflow.com/questions/34045777/copy-to-clipboard-using-javascript-in-ios\n    if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {\n      const editable = textarea.contentEditable;\n      textarea.contentEditable = true;\n      const range = document.createRange();\n      range.selectNodeContents(textarea);\n      const sel = window.getSelection();\n      sel.removeAllRanges();\n      sel.addRange(range);\n      textarea.setSelectionRange(0, 999999);\n      textarea.contentEditable = editable;\n    }\n    else {\n      textarea.select();\n    }\n\n    try {\n      const result = document.execCommand('copy');\n\n      // Restore previous selection.\n      if (selected) {\n        document.getSelection().removeAllRanges();\n        document.getSelection().addRange(selected);\n      }\n\n      return result;\n    }\n    catch (err) {\n      console.error(err);\n      return false;\n    }\n  };\n})();\n
\n

Usage: copyToClipboard('some text')

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246b082fcc3049e91826", + "creator": "Khom Nazid", + "createdAt": 1649813122000, + "text": "Doesn't work in Opera etc.", + "upvotes": 275, + "upvoterUsernames": [], + "downvotes": 275, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9049c", + "creator": "Gruffy", + "createdAt": 1500029201000, + "text": "

I have put together the solution presented by @dean-taylor here along with some other select / unselect code from elsewhere into a jQuery plugin available on NPM:

\n\n

https://www.npmjs.com/package/jquery.text-select

\n\n

Install:

\n\n

npm install --save jquery.text-select

\n\n

Usage:

\n\n
<script>\n    $(document).ready(function(){\n        $(\"#selectMe\").selectText(); // Hightlight / select the text\n        $(\"#selectMe\").selectText(false); // Clear the selection\n\n        $(\"#copyMe\").copyText(); // Copy text to clipboard\n    });\n</script>\n
\n\n

Futher info on methods / events can be found at the NPM registry page above.

\n", + "upvotes": 502, + "upvoterUsernames": [], + "downvotes": 502, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9049b", + "creator": "Dino Reic", + "createdAt": 1499944607000, + "text": "

I compiled a few functions in a simple solution to cover all cases, with prompt fallback if needed.

\n\n
window.copyToClipboard = function(text) {\n  // Internet Explorer specific\n  if (window.clipboardData && window.clipboardData.setData) {\n    return clipboardData.setData(\"Text\", text);\n  }\n\n  // All other modern browsers\n  target = document.createElement(\"textarea\");\n  target.style.position = \"absolute\";\n  target.style.left = \"-9999px\";\n  target.style.top = \"0\";\n  target.textContent = text;\n  document.body.appendChild(target);\n  target.focus();\n  target.setSelectionRange(0, target.value.length);\n\n  // Copy the selection of fall back to prompt\n  try {\n    document.execCommand(\"copy\");\n    target.remove();\n    console.log('Copied to clipboard: \"'+text+'\"');\n  }\n  catch(e) {\n    console.log(\"Can't copy string on this browser. Try to use Chrome, Firefox or Opera.\")\n    window.prompt(\"Copy to clipboard: Ctrl+C, Enter\", text);\n  }\n}\n
\n\n

Test it here: https://jsfiddle.net/jv0avz65/

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9049f", + "creator": "nikksan", + "createdAt": 1505380833000, + "text": "

Here is my take on that one...

\n\n
function copy(text) {\n    var input = document.createElement('input');\n    input.setAttribute('value', text);\n    document.body.appendChild(input);\n    input.select();\n    var result = document.execCommand('copy');\n    document.body.removeChild(input);\n    return result;\n }\n
\n\n

@korayem: Note that using html input field won't respect line breaks \\n and will flatten any text into a single line.

\n\n

As mentioned by @nikksan in the comments, using textarea will fix the problem as follows:

\n\n
function copy(text) {\n    var input = document.createElement('textarea');\n    input.innerHTML = text;\n    document.body.appendChild(input);\n    input.select();\n    var result = document.execCommand('copy');\n    document.body.removeChild(input);\n    return result;\n}\n
\n", + "upvotes": 182, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246b082fcc3049e9182b", + "creator": "sof-03", + "createdAt": 1526984234000, + "text": "@nikksan how to copy the string with \\n?", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e9182d", + "creator": "nikksan", + "createdAt": 1526987933000, + "text": "@sof-03 use textarea instead of input and add \\r\\n for a line break", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e9182f", + "creator": "Honsa Stunna", + "createdAt": 1527690416000, + "text": "Not working in Microsoft Edge 42.17134.1.0 on Win10x64", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91831", + "creator": "user875234", + "createdAt": 1542293462000, + "text": "I have copied your answer. It works in chrome and that's all I need.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91833", + "creator": "Arya", + "createdAt": 1566625021000, + "text": "This is the simplest solution that works with Firefox v68.0.2 (64-bit).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91835", + "creator": "synkro", + "createdAt": 1636578155000, + "text": "Best solution, tested in Waterfox (Firefox) and Chrome.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91837", + "creator": "andreszs", + "createdAt": 1639689820000, + "text": "Working in latest Chrome and Firefox as of today. Surely it will be dropped soon, because it Just Works™", + "upvotes": 788, + "upvoterUsernames": [], + "downvotes": 788, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e9049e", + "creator": "mjs", + "createdAt": 1504279230000, + "text": "

This is the best. So much winning.

\n\n
var toClipboard = function(text) {\n    var doc = document;\n\n    // Create temporary element\n    var textarea = doc.createElement('textarea');\n    textarea.style.position = 'absolute';\n    textarea.style.opacity  = '0';\n    textarea.textContent    = text;\n\n    doc.body.appendChild(textarea);\n\n    textarea.focus();\n    textarea.setSelectionRange(0, textarea.value.length);\n\n    // Copy the selection\n    var success;\n    try {\n        success = doc.execCommand(\"copy\");\n    }\n    catch(e) {\n        success = false;\n    }\n\n    textarea.remove();\n\n    return success;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246b082fcc3049e91839", + "creator": "liubiantao", + "createdAt": 1505458016000, + "text": "textarea.style.position = 'fixed'; will be better", + "upvotes": 1014, + "upvoterUsernames": [], + "downvotes": 1014, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e9183b", + "creator": "mjs", + "createdAt": 1599912503000, + "text": "@liubiantao why is that?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904a0", + "creator": "user2080225", + "createdAt": 1508022910000, + "text": "

This was the only thing I ever got working, after looking up various ways all around the Internet. This is a messy topic. There are lots of solutions posted around the world and most of them do not work. This worked for me:

\n\n

NOTE: This code will only work when executed as direct synchronous code to something like an 'onClick' method. If you call in an asynchronous response to Ajax or in any other asynchronous way it will not work.

\n\n
copyToClipboard(text) {\n    var copyText = document.createElement(\"input\");\n    copyText.type = \"text\";\n    document.body.appendChild(copyText);\n    copyText.style = \"display: inline; width: 1px;\";\n    copyText.value = text;\n    copyText.focus();\n    document.execCommand(\"SelectAll\");\n    document.execCommand(\"Copy\");\n    copyText.remove();\n}\n
\n\n

I do realize this code will show a 1-pixel wide component visibly on the screen for a millisecond, but decided not to worry about that, which is something that others can address if a real problem.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a1", + "creator": "Mau", + "createdAt": 1508380417000, + "text": "

To copy a selected text ('Text To Copy') to your clipboard, create a Bookmarklet (browser bookmark that executes JavaScript) and execute it (click on it). It will create a temporary textarea.

\n\n

Code from GitHub:

\n\n

https://gist.github.com/stefanmaric/2abf96c740191cda3bc7a8b0fc905a7d

\n\n
(function (text) {\n  var node = document.createElement('textarea');\n  var selection = document.getSelection();\n\n  node.textContent = text;\n  document.body.appendChild(node);\n\n  selection.removeAllRanges();\n  node.select();\n  document.execCommand('copy');\n\n  selection.removeAllRanges();\n  document.body.removeChild(node);\n})('Text To Copy');\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a2", + "creator": "Alexander Mills", + "createdAt": 1515704848000, + "text": "

This was the only thing that worked for me:

\n\n
let textarea = document.createElement('textarea');\ntextarea.setAttribute('type', 'hidden');\ntextarea.textContent = 'the string you want to copy';\ndocument.body.appendChild(textarea);\ntextarea.select();\ndocument.execCommand('copy');\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a3", + "creator": "Adeel Imran", + "createdAt": 1516379429000, + "text": "

Using the JavaScript feature using try/catch you can even have better error handling in doing so like this:

\n\n
copyToClipboard() {\n    let el = document.getElementById('Test').innerText\n    el.focus(); // el.select();\n    try {\n        var successful = document.execCommand('copy');\n        if (successful) {\n            console.log('Copied Successfully! Do whatever you want next');\n        }\n        else {\n            throw ('Unable to copy');\n        }\n    }\n    catch (err) {\n        console.warn('Oops, Something went wrong ', err);\n    }\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246b082fcc3049e91840", + "creator": "Boaz", + "createdAt": 1519860402000, + "text": "What is ES7 here?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91842", + "creator": "Adeel Imran", + "createdAt": 1519898401000, + "text": "try/catch block", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3246b082fcc3049e91843", + "creator": "Adeel Imran", + "createdAt": 1519901121000, + "text": "Sorry sir, that was my lack of knowledge. I have corrected my post.", + "upvotes": 1367, + "upvoterUsernames": [], + "downvotes": 1367, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904a4", + "creator": "Alexandru Sirbu", + "createdAt": 1516920121000, + "text": "

Copy text from HTML input to the clipboard:

\n\n

\r\n
\r\n
 function myFunction() {\r\n   /* Get the text field */\r\n   var copyText = document.getElementById(\"myInput\");\r\n\r\n   /* Select the text field */\r\n   copyText.select();\r\n\r\n   /* Copy the text inside the text field */\r\n   document.execCommand(\"Copy\");\r\n\r\n   /* Alert the copied text */\r\n   alert(\"Copied the text: \" + copyText.value);\r\n }
\r\n
 <!-- The text field -->\r\n <input type=\"text\" value=\"Hello Friend\" id=\"myInput\">\r\n\r\n <!-- The button used to copy the text -->\r\n<button onclick=\"myFunction()\">Copy text</button>
\r\n
\r\n
\r\n

\n\n

Note: The document.execCommand() method is not supported in Internet Explorer 9 and earlier.

\n\n

Source: W3Schools - Copy Text to Clipboard

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a5", + "creator": "Chrillewoodz", + "createdAt": 1519647954000, + "text": "

Here's an elegant solution for Angular 5.x+:

\n\n

Component:

\n\n
import {\n  ChangeDetectionStrategy,\n  ChangeDetectorRef,\n  Component,\n  ElementRef,\n  EventEmitter,\n  Input,\n  OnInit,\n  Output,\n  Renderer2,\n  ViewChild\n} from '@angular/core';\n\n@Component({\n  selector: 'copy-to-clipboard',\n  templateUrl: './copy-to-clipboard.component.html',\n  styleUrls: ['./copy-to-clipboard.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\n\nexport class CopyToClipboardComponent implements OnInit {\n  @ViewChild('input') input: ElementRef;\n  @Input() size = 'md';\n  @Input() theme = 'complement';\n  @Input() content: string;\n  @Output() copied: EventEmitter<string> = new EventEmitter<string>();\n  @Output() error: EventEmitter<string> = new EventEmitter<string>();\n\n  constructor(private renderer: Renderer2) {}\n\n  ngOnInit() {}\n\n  copyToClipboard() {\n\n    const rootElement = this.renderer.selectRootElement(this.input.nativeElement);\n\n    // iOS Safari blocks programmtic execCommand copying normally, without this hack.\n    // https://stackoverflow.com/questions/34045777/copy-to-clipboard-using-javascript-in-ios\n    if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {\n\n      this.renderer.setAttribute(this.input.nativeElement, 'contentEditable', 'true');\n\n      const range = document.createRange();\n\n      range.selectNodeContents(this.input.nativeElement);\n\n      const sel = window.getSelection();\n\n      sel.removeAllRanges();\n      sel.addRange(range);\n\n      rootElement.setSelectionRange(0, 999999);\n    } else {\n      rootElement.select();\n    }\n\n    try {\n      document.execCommand('copy');\n      this.copied.emit();\n    } catch (err) {\n      this.error.emit(err);\n    }\n  };\n}\n
\n\n

Template:

\n\n
<button class=\"btn btn-{{size}} btn-{{theme}}\" type=\"button\" (click)=\"copyToClipboard()\">\n  <ng-content></ng-content>\n</button>\n\n<input #input class=\"hidden-input\" [ngModel]=\"content\">\n
\n\n

Styles:

\n\n
.hidden-input {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 1px; \n  height: 1px;\n  padding: 0;\n  border: 0;\n  box-shadow: none;\n  outline: none;\n  background: transparent;\n}\n
\n", + "upvotes": 4710, + "upvoterUsernames": [], + "downvotes": 4710, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a6", + "creator": "Adithya Sai", + "createdAt": 1525515295000, + "text": "

Here is my solution:

\n\n
var codeElement =\n    document.getElementsByClassName(\"testelm\") &&\n        document.getElementsByClassName(\"testelm\").length ?\n    document.getElementsByClassName(\"testelm\")[0] :\n    \"\";\nif (codeElement != \"\") {\n    var e = document.createRange();\n    e.selectNodeContents(codeElement);\n    var selection = window.getSelection();\n    selection.removeAllRanges();\n    selection.addRange(e);\n    document.execCommand(\"Copy\");\n    selection.removeAllRanges();\n}\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904a7", + "creator": "Grim", + "createdAt": 1530471166000, + "text": "

I use this very successfully (without jQuery or any other framework).

\n
function copyToClp(txt){\n    var m = document;\n    txt = m.createTextNode(txt);\n    var w = window;\n    var b = m.body;\n    b.appendChild(txt);\n    if (b.createTextRange) {\n        var d = b.createTextRange();\n        d.moveToElementText(txt);\n        d.select();\n        m.execCommand('copy');\n    } \n    else {\n        var d = m.createRange();\n        var g = w.getSelection;\n        d.selectNodeContents(txt);\n        g().removeAllRanges();\n        g().addRange(d);\n        m.execCommand('copy');\n        g().removeAllRanges();\n    }\n    txt.remove();\n}\n
\n

Warning

\n

Tabs are converted to spaces (at least in Chrome).

\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246c082fcc3049e91847", + "creator": "Luke_", + "createdAt": 1633697700000, + "text": "Doesnt work on firefox, i got an error saying that there was a lack of user activation", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91849", + "creator": "Grim", + "createdAt": 1633707180000, + "text": "@Luke_ Is firefox right? Did you call it without a direct user's click?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904a8", + "creator": "KhoPhi", + "createdAt": 1533562959000, + "text": "

In 2018, here's how you can go about it:

\n\n
async copySomething(text?) {\n  try {\n    const toCopy = text || location.href;\n    await navigator.clipboard.writeText(toCopy);\n    console.log('Text or Page URL copied');\n  }\n  catch (err) {\n    console.error('Failed to copy: ', err);\n  }\n}\n
\n\n

It is used in my Angular 6+ code like so:

\n\n
<button mat-menu-item (click)=\"copySomething()\">\n    <span>Copy link</span>\n</button>\n
\n\n

If I pass in a string, it copies it. If nothing, it copies the page's URL.

\n\n

More gymnastics to the clipboard stuff can be done too. See more information here:

\n\n

Unblocking Clipboard Access

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246c082fcc3049e9184b", + "creator": "Joe Warner", + "createdAt": 1533583318000, + "text": "you've linked to localhost", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9184d", + "creator": "arjunattam", + "createdAt": 1534761904000, + "text": "Please be aware that this does not work in Safari (version 11.1.2)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e9184f", + "creator": "kano", + "createdAt": 1541406904000, + "text": "Helpful content; unhelpful attitude.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91851", + "creator": "ramin", + "createdAt": 1544386669000, + "text": "I'm getting for both functions readText, writeText a Promise in rejected state", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246c082fcc3049e91853", + "creator": "TimH - Codidact", + "createdAt": 1545334519000, + "text": "According to the link provided, "navigator.clipboard is only supported for pages served over HTTPS"", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904a9", + "creator": "Nishant Singh", + "createdAt": 1542798735000, + "text": "

This can be done just by using a combination of getElementbyId, Select(), blur() and the copy command.

\n\n

Note

\n\n

The select() method selects all the text in a <textarea> element or an <input> element with a text field. This might not work on a button.

\n\n

Usage

\n\n
let copyText = document.getElementById('input-field');\ncopyText.select()\ndocument.execCommand(\"copy\");\ncopyReferal.blur()\ndocument.getElementbyId('help-text').textContent = 'Copied'\n
\n\n

The blur() method will remove the ugly highlighted portion instead of that you can show at beautiful message that your content was copied successfully.

\n", + "upvotes": 243, + "upvoterUsernames": [], + "downvotes": 243, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904aa", + "creator": "Alireza", + "createdAt": 1548508989000, + "text": "

Using document.execCommand will do the job for you...

\n\n

Using that, you can also do cut, copy and paste...

\n\n

This is one simple clipboard copy functionality which copies everything from input text...

\n\n

\r\n
\r\n
function copyInputText() {\r\n  var copyText = document.querySelector(\"#input\");\r\n  copyText.select();\r\n  document.execCommand(\"copy\");\r\n}\r\n\r\ndocument.querySelector(\"#copy\").addEventListener(\"click\", copyInputText);
\r\n
<input id=\"input\" type=\"text\" />\r\n<button id=\"copy\">Copy</button>
\r\n
\r\n
\r\n

\n\n

For more information, see Interact with the clipboard (add-on).

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904ac", + "creator": "Lars Gross", + "createdAt": 1566204224000, + "text": "

Here is an easy example ;)

\n\n
<!DOCTYPE html>\n<html>\n    <body>\n        <input type=\"text\"\n               value=\"Hello, World!\"\n               id=\"myInput\">\n        <button onclick=\"myFunction()\">Copy text</button>\n\n        <p>The document.execCommand() method is not supported\n           in Internet&nbsp;Explorer&nbsp;8 and earlier.</p>\n\n        <script>\n            function myFunction() {\n                var copyText = document.getElementById(\"myInput\");\n                copyText.select();\n                document.execCommand(\"copy\");\n                alert(\"Copied the text: \" + copyText.value);\n            }\n        </script>\n    </body>\n</html>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904ab", + "creator": "Renga", + "createdAt": 1565087511000, + "text": "

I have tried many solutions. If it works in modern browsers, it won't in Internet Explorer. If it works in Internet Explorer, it won't on iOS. I finally groomed them all and arrived at the below fix that works in all the browsers, iOS, webview, and Android.

\n\n

Note: I also covered the scenario where the user denies permission to the clipboard. Additionally, the message \"link copied\" will be displayed even if the user copies manually.

\n\n
<div class=\"form-group col-md-12\">\n    <div class=\"input-group col-md-9\">\n        <input name=\"copyurl\"\n               type=\"text\"\n               class=\"form-control br-0 no-focus\"\n               id=\"invite-url\"\n               placeholder=\"http://www.invitelink.com/example\"\n               readonly>\n        <span class=\"input-group-addon\" id=\"copy-link\" title=\"Click here to copy the invite link\">\n            <i class=\"fa fa-clone txt-18 text-success\" aria-hidden=\"true\"></i>\n        </span>\n    </div>\n    <span class=\"text-success copy-success hidden\">Link copied.</span>\n</div>\n
\n\n

Script:

\n\n
var addEvent =  window.attachEvent || window.addEventListener;\nvar event = 'copy';\nvar $inviteUrl = $('#invite-url');\n\n$('#copy-link').on('click', function(e) {\n    if ($inviteUrl.val()) {\n        if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {\n            var el = $inviteUrl.get(0);\n            var editable = el.contentEditable;\n            var readOnly = el.readOnly;\n            el.contentEditable = true;\n            el.readOnly = false;\n            var range = document.createRange();\n            range.selectNodeContents(el);\n            var sel = window.getSelection();\n            sel.removeAllRanges();\n            sel.addRange(range);\n            el.setSelectionRange(0, 999999);\n            el.contentEditable = editable;\n            el.readOnly = readOnly;\n            document.execCommand('copy');\n            $inviteUrl.blur();\n        }\n        else {\n            $inviteUrl.select();\n            document.execCommand(\"copy\");\n        }\n    }\n});\n\naddEvent(event, function(event) {\n    if ($inviteUrl.val() && event.target.id == 'invite-url') {\n        var $copyLink = $('#copy-link i');\n        $copyLink.removeClass('fa-clone');\n        $copyLink.addClass('fa-check');\n        $('.copy-success').removeClass('hidden');\n        setTimeout(function() {\n            $copyLink.removeClass('fa-check');\n            $copyLink.addClass('fa-clone');\n            $('.copy-success').addClass('hidden');\n        }, 2000);\n    }\n});\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904ad", + "creator": "Crashalot", + "createdAt": 1572469229000, + "text": "

This is a standalone class and ensures no flashing could occur from the temporary textarea by placing it off-screen.

\n

This works in Safari (desktop), Firefox, and Chrome.

\n
// ================================================================================\n// ClipboardClass\n// ================================================================================\nvar ClipboardClass = (function() {\n\n    function copyText(text) {\n        // Create a temporary element off-screen to hold text.\n        var tempElem = $('<textarea style="position: absolute; top: -8888px; left: -8888px">');\n        $("body").append(tempElem);\n\n        tempElem.val(text).select();\n        document.execCommand("copy");\n        tempElem.remove();\n    }\n\n\n    // ============================================================================\n    // Class API\n    // ============================================================================\n    return {\n        copyText: copyText\n    };\n\n})();\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904ae", + "creator": "Araston", + "createdAt": 1573498303000, + "text": "
function copytoclipboard(element) {\n\n    var $temp = $("<input>");\n    $("body").append($temp);\n    $temp.val('0' + element).select();\n    document.execCommand("copy");\n    $temp.remove();\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246c082fcc3049e9185a", + "creator": "Umut Can Arda", + "createdAt": 1634810857000, + "text": "thank you, you are life saver", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904af", + "creator": "SanTasso", + "createdAt": 1591001281000, + "text": "

\r\n
\r\n
    $(\"td\").click(function (e) {\r\n        var clickedCell = $(e.target).closest(\"td\");\r\n        navigator.clipboard.writeText(clickedCell.text());\r\n        alert(clickedCell.text());\r\n    });
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n<table>\r\n<tr>\r\n<td>First<td>\r\n</tr>\r\n<tr>\r\n<td>Second<td>\r\n</tr>\r\n<tr>\r\n<td>Third<td>\r\n</tr>\r\n<tr>\r\n<td>Fourth<td>\r\n</tr>\r\n</table>
\r\n
\r\n
\r\n

\n\n

I've read all the answers, as of June 1st, 2020, I've beeen struggling to solve this when I finally found documentation:

\n\n
$(\"td\").click(function (e) {\n    var clickedCell = $(e.target).closest(\"td\");\n    navigator.clipboard.writeText(clickedCell.text());\n});\n
\n\n

It will write the clicked cell text to the browser clipboard.

\n\n

You can change the selectors \"td\" for anything you want, you can add console.log for debugging and/or alert functions.

\n\n

Here is documentation:\nhttps://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246d082fcc3049e918be", + "creator": "lance.dolan", + "createdAt": 1604971878000, + "text": "No IE compatibility", + "upvotes": 1374, + "upvoterUsernames": [], + "downvotes": 1374, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904b0", + "creator": "Игорь Хлебников", + "createdAt": 1602605775000, + "text": "
document.querySelector('#some_your_textfield_id').select();\ndocument.execCommand('copy');\n
\n

The first line is to select the text that you want to copy.

\n

The second line is to copy the selected text.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904b1", + "creator": "YeetYeet", + "createdAt": 1604524834000, + "text": "

This solution was found here. The document.execCommand("copy"); is not supported on Internet Explorer 8 and earlier.\n
\n
\n

\r\n
\r\n
const copyBtn =  document.getElementById(\"copyBtn\");\nconst input = document.getElementById(\"input\");\n\nfunction copyText() {\n  const value = input.value;\n  \n  input.select(); // selects the input variable as the text to be copied\n  input.setSelectionRange(0, 99999); // this is used to set the selection range for mobile devices\n  \n  document.execCommand(\"copy\"); // copies the selected text\n  \n  alert(\"Copied the text \" + value); // displays the copied text in a prompt\n}\n\ncopyBtn.onmousedown = function () {\n  copyText();\n}
\r\n
<input type=\"text\" id=\"input\" placeholder=\"Type text to copy... \"/>\n<button id=\"copyBtn\">\n  Copy\n</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904b2", + "creator": "Vince", + "createdAt": 1607549634000, + "text": "

Simple, ready-to-use and not obsolete solution:

\n
function copyToClipboard(elementIdToCopy, elementIdToNotifyOutcome) {\n    const contentToCopy = document.getElementById(elementIdToCopy).innerHTML;\n    const elementToNotifyOutcome = document.getElementById(elementIdToNotifyOutcome);\n\n    navigator.clipboard.writeText(contentToCopy).then(function() {\n        elementToNotifyOutcome.classList.add('success');\n        elementToNotifyOutcome.innerHTML = 'Copied!';\n    }, function() {\n        elementToNotifyOutcome.classList.add('failure');\n        elementToNotifyOutcome.innerHTML = 'Sorry, did not work.';\n    });\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904b3", + "creator": "Ritesh Tiwari", + "createdAt": 1608122428000, + "text": "

The below function can be used to copy into the clipboard:

\n
copyToclipboard = (event, text) => {\n    var container = event.currentTarget;\n    let tempInnerHtml = container.innerHTML;\n    container.innerHTML = text;\n    window.getSelection().removeAllRanges();\n    let range = document.createRange();\n    range.selectNode(container);\n    window.getSelection().addRange(range);\n    document.execCommand('copy');\n    window.getSelection().removeAllRanges();\n    container.innerHTML = tempInnerHtml;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904b4", + "creator": "Rahul Daksh", + "createdAt": 1616568611000, + "text": "

This could be the solution to your problem

\n
function CopyToNotepad(id){\n    var r = document.createRange();\n    r.selectNode(document.getElementById(id));\n    window.getSelection().removeAllRanges();\n    window.getSelection().addRange(r);\n    document.execCommand('copy');\n    window.getSelection().removeAllRanges();\n}\n
\n

Note: id should be the parent element id to which you want to copy the content. Eg: suppose you want to copy every content inside the list, then id should be used as follows:

\n
<ul id="dummy_id">\n<li>copy content 1 </li>\n<li>copy content 2 </li>\n<li>copy content 3 </li>\n<li>copy content 4 </li>\n<li>copy content 5 </li>\n</ul>\n
\n

then function call should be like this

\n

CopyToNotepad(dummy_id)

\n

Thanks. Sure this could solve your problem!

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246d082fcc3049e918c5", + "creator": "Vivaan", + "createdAt": 1630350176000, + "text": "Who would want to copy it to the "website's keyboard"", + "upvotes": 336, + "upvoterUsernames": [], + "downvotes": 336, + "downvoterUsernames": [] + }, + { + "_id": "62f3246d082fcc3049e918c7", + "creator": "Vivaan", + "createdAt": 1630350207000, + "text": "here it is about the devices' keyboard", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904b5", + "creator": "HO LI Pin", + "createdAt": 1621508391000, + "text": "

This code tested @ 2021 May . Work on Chrome , IE , Edge. 'message' parameter on below is the string value you want to copy.

\n
<script type="text/javascript">\n    function copyToClipboard(message) {\n        var textArea = document.createElement("textarea");\n        textArea.value = message;\n        textArea.style.opacity = "0"; \n        document.body.appendChild(textArea);\n        textArea.focus();\n        textArea.select();\n\n\n        try {\n            var successful = document.execCommand('copy');\n            var msg = successful ? 'successful' : 'unsuccessful';\n            alert('Copying text command was ' + msg);\n        } catch (err) {\n            alert('Unable to copy value , error : ' + err.message);\n        }\n\n        document.body.removeChild(textArea);\n    }\n\n</script>\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246d082fcc3049e918c9", + "creator": "Hannes Schneidermayer", + "createdAt": 1649945591000, + "text": "I like this one the most", + "upvotes": 261, + "upvoterUsernames": [], + "downvotes": 261, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904b7", + "creator": "Ali Yaghoubi", + "createdAt": 1634804294000, + "text": "

Try to use this function

\n
\nconst copyToClipboard = (\n  value,\n  successfully = () => null,\n  failure = () => null\n) => {\n  const clipboard = navigator.clipboard;\n  if (clipboard !== undefined && clipboard !== "undefined") {\n    navigator.clipboard.writeText(value).then(successfully, failure);\n  } else {\n    if (document.execCommand) {\n      const el = document.createElement("input");\n      el.value = value;\n      document.body.append(el);\n\n      el.select();\n      el.setSelectionRange(0, value.length);\n\n      if (document.execCommand("copy")) {\n        successfully();\n      }\n\n      el.remove();\n    } else {\n      failure();\n    }\n  }\n};\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904b8", + "creator": "Tauseef Arshad", + "createdAt": 1639937403000, + "text": "

Best Way to Copy the text inside the text field.\nUse navigator.clipboard.writeText.

\n
<input type="text" value="Hello World" id="myId">\n<button onclick="myFunction()" >Copy text</button>\n\n<script>\nfunction myFunction() {\n  var copyText = document.getElementById("myId");\n  copyText.select();\n  copyText.setSelectionRange(0, 99999);\n  navigator.clipboard.writeText(copyText.value);\n}\n\n</script>\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246d082fcc3049e918cd", + "creator": "PrashSE", + "createdAt": 1640275422000, + "text": "document.execCommand('Copy'); command doesn't work's always, and above approach solved it", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904b6", + "creator": "Justin", + "createdAt": 1634517563000, + "text": "

Adding on to Dean Taylor's answer, here is a short answer and a long one, so you can just copy the function that suits your needs:

\n

Short Answer:

\n

Just use navigator.clipboard.writeText(str)

\n

See the clipboard API on caniuse.com

\n

Long Answer:

\n
    // Copies a string to clipboard\n    // Uses navigator API if available, else uses execCommand (deprecated)\n    // Returns a boolean if copy was successful\n    // See: https://stackoverflow.com/q/400212/4907950\n        async function copyText(str) {\n        console.log('Copying', str);\n        if (!navigator.clipboard) {\n            // fallback\n            let input = document.createElement('textarea');\n            input.innerHTML = str;\n            document.body.appendChild(input);\n            input.focus();\n            input.select();\n            let result;\n\n            try {\n                result = document.execCommand('copy');\n                console.log(\n                    'Fallback: Copying text command was ' + (result ? 'successful' : 'unsuccessful')\n                );\n            } catch (err) {\n                console.error('Fallback: Could not copy text: ', err);\n            }\n            document.body.removeChild(input);\n            return result;\n        }\n        const result = navigator.clipboard.writeText(str).then(\n            function () {\n                console.log('Async: Copying to clipboard was successful');\n                return true;\n            },\n            function (err) {\n                console.error('Async: Could not copy text: ', err);\n                return false;\n            }\n        );\n        return result;\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904ba", + "creator": "Tauseef Arshad", + "createdAt": 1643463939000, + "text": "

Best and Easy way in JavaScript/TypeScript use this command

\n
navigator.clipboard.writeText(textExample);\n
\n

just pass your value what you want to copy to clipboard in textExample

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246e082fcc3049e918d0", + "creator": "Daniel", + "createdAt": 1643888112000, + "text": "navigator.clipboard can be udenfined. You should catch this exception...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918d1", + "creator": "Lowis", + "createdAt": 1650556535000, + "text": "Doesn't work with IOS", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904b9", + "creator": "jbyrd", + "createdAt": 1642692387000, + "text": "

Stackoverflow's Solution

\n

I just wanted to point out that Stackoverflow actually does this. Under each answer there's a "Share" link - when you click that, it opens a popup with the share link highlighted inside an input, along with a "Copy link" link:

\n

\"enter

\n

If you go to Chrome DevTools and go to the Event Listeners for that link, you can hunt down the function they use. It's called tryCopy():

\n

\"enter

\n

And this is exactly consistent with Dean Taylors answer here (which was recently updated) - specifically read the section entitled "Async + Fallback". The TL;DR is: try using the navigator.clipboard api - if that's not supported by the browser, fall back to document.execCommand().

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246e082fcc3049e918d3", + "creator": "Lowis", + "createdAt": 1650562109000, + "text": "Hi, I was wondering if you managed to get this or Dean Taylors solution to work with IOS?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904bb", + "creator": "Judith lobo", + "createdAt": 1648208464000, + "text": "

this is the easiest way i found

\n
<!DOCTYPE html>\n<html>\n<body>\n\n <p>Click on the button to copy the text from the text field. Try to paste \n the text (e.g. ctrl+v) afterwards in a different window, to see the effect. \n </p>\n\n  <input type="text" value="Hello World" id="myInput">\n  <button onclick="myFunction()">Copy text</button>\n\n  <script>\n function myFunction() {\n   /* Get the text field */\n   var copyText = document.getElementById("myInput");\n\n  /* Select the text field */\n   copyText.select();\n    copyText.setSelectionRange(0, 99999); /* For mobile devices */\n\n   /* Copy the text inside the text field */\n   navigator.clipboard.writeText(copyText.value);\n\n   /* Alert the copied text */\n    alert("Copied the text: " + copyText.value);\n    }\n\n   </script>\n\n    </body>\n    </html>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904bc", + "creator": "NVRM", + "createdAt": 1649605246000, + "text": "

This works straight away, using the newest Clipboard API, and a user interaction:

\n

\r\n
\r\n
copy.addEventListener(\"pointerdown\", () => navigator.clipboard.writeText(\"Hello World!\"))
\r\n
<button id=\"copy\">Copy Hello World!</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904bd", + "creator": "Royce", + "createdAt": 1654774592000, + "text": "

To me less is more

\n
const text = "Copy me please!";\nnavigator.clipboard.writeText(text);\n
\n

All good.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246e082fcc3049e918d7", + "creator": "clozach", + "createdAt": 1654836880000, + "text": "This answer has already been suggested: stackoverflow.com/a/70906043/230615", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e90445", + "creator": "Jagadeesh", + "createdAt": 1361518992000, + "text": "It is giving browser undefined exception in IE as well as in FF", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90446", + "creator": "Frank Fang", + "createdAt": 1402488913000, + "text": "If we can put text into the user's clipboard, we can ruin his clipboard.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90447", + "creator": "Santiago Corredoira", + "createdAt": 1430385144000, + "text": "@Mike it probably never will because of the security implications.", + "upvotes": 3237, + "upvoterUsernames": [], + "downvotes": 3237, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90448", + "creator": "Shubham Kushwah", + "createdAt": 1629745094000, + "text": "@FrankFang we're not doing it on page load, we're doing it when they click "COPY"", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2613372, + "uvac": 2613439 + } + }, + { + "_id": "62f321bb082fcc3049e8fec2", + "title": "Setting "checked" for a checkbox with jQuery", + "title-lowercase": "setting "checked" for a checkbox with jquery", + "creator": "tpower", + "createdAt": 1231453224000, + "status": "open", + "text": "

I'd like to do something like this to tick a checkbox using jQuery:

\n\n
$(\".myCheckBox\").checked(true);\n
\n\n

or

\n\n
$(\".myCheckBox\").selected(true);\n
\n\n

Does such a thing exist?

\n", + "upvotes": 5833, + "upvoterUsernames": [], + "downvotes": 1366, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3678645, + "answers": 43, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e903d1", + "creator": "Chris Brandsma", + "createdAt": 1231454174000, + "text": "
$(\"#mycheckbox\")[0].checked = true;\n$(\"#mycheckbox\").attr('checked', true);\n$(\"#mycheckbox\").click();\n
\n\n

The last one will fire the click event for the checkbox, the others will not.\nSo if you have custom code in the onclick event for the checkbox that you want to fire, use the last one.

\n", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e916f4", + "creator": "redsquare", + "createdAt": 1231460407000, + "text": "top one will fail...checked is not a jquery object member", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e916f5", + "creator": "Blazemonger", + "createdAt": 1390313799000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e916f7", + "creator": "Vahid Amiri", + "createdAt": 1567250501000, + "text": "IMO the click event is unreliable in Firefox and Edge.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d2", + "creator": "livefree75", + "createdAt": 1282328343000, + "text": "

You can also extend the $.fn object with new methods:

\n\n
(function($)  {\n   $.fn.extend({\n      check : function()  {\n         return this.filter(\":radio, :checkbox\").attr(\"checked\", true);\n      },\n      uncheck : function()  {\n         return this.filter(\":radio, :checkbox\").removeAttr(\"checked\");\n      }\n   });\n}(jQuery));\n
\n\n

Then you can just do:

\n\n
$(\":checkbox\").check();\n$(\":checkbox\").uncheck();\n
\n\n

Or you may want to give them more unique names like mycheck() and myuncheck() in case you use some other library that uses those names.

\n", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e916fa", + "creator": "mcgrailm", + "createdAt": 1300894378000, + "text": "@livfree75 removing the the checked attribute makes it impossible to reset the form", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e916fc", + "creator": "Blazemonger", + "createdAt": 1390313776000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d5", + "creator": "cwharris", + "createdAt": 1304710281000, + "text": "

This is the correct way of checking and unchecking checkboxes with jQuery, as it is cross-platform standard, and will allow form reposts.

\n
$('.myCheckBox').each(function(){ this.checked = true; });\n\n$('.myCheckBox').each(function(){ this.checked = false; });\n
\n

By doing this, you are using JavaScript standards for checking and unchecking checkboxes, so any browser that properly implements the "checked" property of the checkbox element will run this code flawlessly. This should be all major browsers, but I am unable to test previous to Internet Explorer 9.

\n

The Problem (jQuery 1.6):

\n

Once a user clicks on a checkbox, that checkbox stops responding to the "checked" attribute changes.

\n

Here is an example of the checkbox attribute failing to do the job after someone has clicked the checkbox (this happens in Chrome).

\n

Fiddle

\n

The Solution:

\n

By using JavaScript's "checked" property on the DOM elements, we are able to solve the problem directly, instead of trying to manipulate the DOM into doing what we want it to do.

\n

Fiddle

\n

This plugin will alter the checked property of any elements selected by jQuery, and successfully check and uncheck checkboxes under all circumstances. So, while this may seem like an over-bearing solution, it will make your site's user experience better, and help prevent user frustration.

\n
(function( $ ) {\n    $.fn.checked = function(value) {\n        if(value === true || value === false) {\n            // Set the value of the checkbox\n            $(this).each(function(){ this.checked = value; });\n        } \n        else if(value === undefined || value === 'toggle') {\n            // Toggle the checkbox\n            $(this).each(function(){ this.checked = !this.checked; });\n        }\n\n        return this;\n    };\n})( jQuery );\n
\n

Alternatively, if you do not want to use a plugin, you can use the following code snippets:

\n
// Check\n$(':checkbox').prop('checked', true);\n\n// Un-check\n$(':checkbox').prop('checked', false);\n\n// Toggle\n$(':checkbox').prop('checked', function (i, value) {\n    return !value;\n});\n
\n", + "upvotes": 436, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e916fe", + "creator": "Daniel X Moore", + "createdAt": 1327906808000, + "text": "In your first JSFiddle example you should be using removeAttr('checked') rather than attr('checked', false)", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91700", + "creator": "cwharris", + "createdAt": 1328150432000, + "text": "Yes. prop is definitely the appropriate way to set attributes on an element.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d4", + "creator": "mcgrailm", + "createdAt": 1300893744000, + "text": "

To check a checkbox you should use

\n\n
 $('.myCheckbox').attr('checked',true);\n
\n\n

or

\n\n
 $('.myCheckbox').attr('checked','checked');\n
\n\n

and to uncheck a check box you should always set it to false:

\n\n
 $('.myCheckbox').attr('checked',false);\n
\n\n

If you do

\n\n
  $('.myCheckbox').removeAttr('checked')\n
\n\n

it removes the attribute all together and therefore you will not be able to reset the form.

\n\n

BAD DEMO jQuery 1.6. I think this is broken. For 1.6 I am going to make a new post on that.

\n\n

NEW WORKING DEMO jQuery 1.5.2 works in Chrome.

\n\n

Both demos use

\n\n
$('#tc').click(function() {\n    if ( $('#myCheckbox').attr('checked')) {\n        $('#myCheckbox').attr('checked', false);\n    } else {\n        $('#myCheckbox').attr('checked', 'checked');\n    }\n});\n
\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e91702", + "creator": "cwharris", + "createdAt": 1304661485000, + "text": "This is inaccurate. setting the 'checked' attribute to '' will not uncheck check boxes in at least chrome.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91703", + "creator": "mcgrailm", + "createdAt": 1304707475000, + "text": "@xixonia I did test before I posted your fiddle doesn't work because you didn't change the menu on the left to include jquery", + "upvotes": 558, + "upvoterUsernames": [], + "downvotes": 558, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91704", + "creator": "mcgrailm", + "createdAt": 1304712643000, + "text": "@xixonia thanks again, It seems this doesn't work the same way in jauery 1.6 see note above thanks for adding your solution as well", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91705", + "creator": "gnarf", + "createdAt": 1304712690000, + "text": "mcgralim - in 1.6 its even easier.... $(".mycheckbox").prop("checked", true/false)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91707", + "creator": "mcgrailm", + "createdAt": 1304712840000, + "text": "@gnarf thats great but now i'll have to go change that in all my code 8 ^ (", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91708", + "creator": "mcgrailm", + "createdAt": 1390314057000, + "text": "@Blazemonger do you mean you use .prop rather than .attr ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d0", + "creator": "bchhun", + "createdAt": 1231453525000, + "text": "

Use:

\n\n
$(\".myCheckbox\").attr('checked', true); // Deprecated\n$(\".myCheckbox\").prop('checked', true);\n
\n\n

And if you want to check if a checkbox is checked or not:

\n\n
$('.myCheckbox').is(':checked');\n
\n", + "upvotes": 1199, + "upvoterUsernames": [], + "downvotes": 434, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e91709", + "creator": "eomeroff", + "createdAt": 1304380583000, + "text": "also $(selector).checked to check is checked", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e9170b", + "creator": "bchhun", + "createdAt": 1381623268000, + "text": "@YuriAlbuquerque it was an example. you can use whatever selector you want.", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e9170d", + "creator": "SineSwiper", + "createdAt": 1444236159000, + "text": "$(selector).checked does not work. There is no 'checked' method in jQuery.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e9170f", + "creator": "bchhun", + "createdAt": 1444758927000, + "text": "@BrendanByrd There's a checked property on the DOM object though (without the jQuery wrapping)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903ce", + "creator": "Micah", + "createdAt": 1231453464000, + "text": "

You can do

\n\n
$('.myCheckbox').attr('checked',true) //Standards compliant\n
\n\n

or

\n\n
$(\"form #mycheckbox\").attr('checked', true)\n
\n\n

If you have custom code in the onclick event for the checkbox that you want to fire, use this one instead:

\n\n
$(\"#mycheckbox\").click();\n
\n\n

You can uncheck by removing the attribute entirely:

\n\n
$('.myCheckbox').removeAttr('checked')\n
\n\n

You can check all checkboxes like this:

\n\n
$(\".myCheckbox\").each(function(){\n    $(\"#mycheckbox\").click()\n});\n
\n", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32420082fcc3049e91710", + "creator": "Chris Brandsma", + "createdAt": 1231454271000, + "text": "you can also go $("#myTable input:checkbox").each(...);", + "upvotes": 331, + "upvoterUsernames": [], + "downvotes": 331, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91711", + "creator": "RobertPitt", + "createdAt": 1284983346000, + "text": "You can also go $(".myCheckbox").click()", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91713", + "creator": "mcgrailm", + "createdAt": 1300894360000, + "text": "@Michah removing the the checked attribute makes it impossible to reset the form", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91714", + "creator": "Blazemonger", + "createdAt": 1390313755000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d3", + "creator": "Abou-Emish", + "createdAt": 1284982988000, + "text": "

This selects elements that have the specified attribute with a value containing the given substring \"ckbItem\":

\n\n
$('input[name *= ckbItem]').prop('checked', true);\n
\n\n

It will select all elements that contain ckbItem in its name attribute.

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903cf", + "creator": "Xian", + "createdAt": 1231453508000, + "text": "

Modern jQuery

\n\n

Use .prop():

\n\n
$('.myCheckbox').prop('checked', true);\n$('.myCheckbox').prop('checked', false);\n
\n\n

DOM API

\n\n

If you're working with just one element, you can always just access the underlying HTMLInputElement and modify its .checked property:

\n\n
$('.myCheckbox')[0].checked = true;\n$('.myCheckbox')[0].checked = false;\n
\n\n

The benefit to using the .prop() and .attr() methods instead of this is that they will operate on all matched elements.

\n\n

jQuery 1.5.x and below

\n\n

The .prop() method is not available, so you need to use .attr().

\n\n
$('.myCheckbox').attr('checked', true);\n$('.myCheckbox').attr('checked', false);\n
\n\n

Note that this is the approach used by jQuery's unit tests prior to version 1.6 and is preferable to using $('.myCheckbox').removeAttr('checked'); since the latter will, if the box was initially checked, change the behaviour of a call to .reset() on any form that contains it – a subtle but probably unwelcome behaviour change.

\n\n

For more context, some incomplete discussion of the changes to the handling of the checked attribute/property in the transition from 1.5.x to 1.6 can be found in the version 1.6 release notes and the Attributes vs. Properties section of the .prop() documentation.

\n", + "upvotes": 8796, + "upvoterUsernames": [], + "downvotes": 2468, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32420082fcc3049e91717", + "creator": "Xian", + "createdAt": 1231454932000, + "text": "Yes this is setting the attribute value, and uncheck by removing the attribute. I have updated my answer to show this.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91718", + "creator": "mcgrailm", + "createdAt": 1300894052000, + "text": "@Xian removing the the checked attribute makes it impossible to reset the form", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e91719", + "creator": "cwharris", + "createdAt": 1304716377000, + "text": "Problem here is if you need something that works for both versions. In that case, you can use the plugin. :) Thanks for the update!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32420082fcc3049e9171a", + "creator": "cwharris", + "createdAt": 1305317333000, + "text": "As a side note, jQuery 1.6.1 should be fixing the issue I mentioned, so we can tehcnically all still go back to using $(...).prop(...)", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d6", + "creator": "Prasanth P", + "createdAt": 1308904041000, + "text": "

We can use elementObject with jQuery for getting the attribute checked:

\n\n
$(objectElement).attr('checked');\n
\n\n

We can use this for all jQuery versions without any error.

\n\n

Update: Jquery 1.6+ has the new prop method which replaces attr, e.g.:

\n\n
$(objectElement).prop('checked');\n
\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32421082fcc3049e9171d", + "creator": "Blazemonger", + "createdAt": 1390313899000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d7", + "creator": "starjahid", + "createdAt": 1319532707000, + "text": "

Here is code for checked and unchecked with a button:

\n\n
var set=1;\nvar unset=0;\njQuery( function() {\n    $( '.checkAll' ).live('click', function() {\n        $( '.cb-element' ).each(function () {\n            if(set==1){ $( '.cb-element' ).attr('checked', true) unset=0; }\n            if(set==0){ $( '.cb-element' ).attr('checked', false); unset=1; }\n        });\n        set=unset;\n    });\n});\n
\n\n

Update: Here is the same code block using the newer Jquery 1.6+ prop method, which replaces attr:

\n\n
var set=1;\nvar unset=0;\njQuery( function() {\n    $( '.checkAll' ).live('click', function() {\n        $( '.cb-element' ).each(function () {\n            if(set==1){ $( '.cb-element' ).prop('checked', true) unset=0; }\n            if(set==0){ $( '.cb-element' ).prop('checked', false); unset=1; }\n        });\n        set=unset;\n    });\n});\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32421082fcc3049e9171f", + "creator": "Blazemonger", + "createdAt": 1390313936000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903d9", + "creator": "Peter Krauss", + "createdAt": 1331296129000, + "text": "

Assuming that the question is...

\n\n

How do I check a checkbox-set BY VALUE?

\n\n

Remember that in a typical checkbox set, all input tags have the same name, they differ by the attribute value: there are no ID for each input of the set.

\n\n

Xian's answer can be extended with a more specific selector, using the following line of code:

\n\n
$(\"input.myclass[name='myname'][value='the_value']\").prop(\"checked\", true);\n
\n", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903d8", + "creator": "fredcrs", + "createdAt": 1325770012000, + "text": "

I couldn't get it working using:

\n\n
$(\"#cb\").prop('checked', 'true');\n$(\"#cb\").prop('checked', 'false');\n
\n\n

Both true and false would check the checkbox. What worked for me was:

\n\n
$(\"#cb\").prop('checked', 'true'); // For checking\n$(\"#cb\").prop('checked', '');     // For unchecking\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32421082fcc3049e91722", + "creator": "tpower", + "createdAt": 1325777645000, + "text": "shouldn't it be true and false and not 'true' and 'false'?", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32421082fcc3049e91724", + "creator": "Jone Polvora", + "createdAt": 1424558047000, + "text": "use true and false values for boolean, do not use 'true' or 'false' (strings).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903db", + "creator": "Overbeeke", + "createdAt": 1340537622000, + "text": "

I'm missing the solution. I'll always use:

\n\n
if ($('#myCheckBox:checked').val() !== undefined)\n{\n    //Checked\n}\nelse\n{\n    //Not checked\n}\n
\n", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903da", + "creator": "Clement Ho", + "createdAt": 1334644473000, + "text": "

If you are using PhoneGap doing application development, and you have a value on the button that you want to show instantly, remember to do this

\n\n
$('span.ui-[controlname]',$('[id]')).text(\"the value\");\n
\n\n

I found that without the span, the interface will not update no matter what you do.

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903dc", + "creator": "prashanth", + "createdAt": 1347272770000, + "text": "

Try this:

\n\n
$('#checkboxid').get(0).checked = true;  //For checking\n\n$('#checkboxid').get(0).checked = false; //For unchecking\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903dd", + "creator": "Xitalogy", + "createdAt": 1350952883000, + "text": "

Here is a way to do it without jQuery

\n\n

\r\n
\r\n
function addOrAttachListener(el, type, listener, useCapture) {\r\n  if (el.addEventListener) {\r\n    el.addEventListener(type, listener, useCapture);\r\n  } else if (el.attachEvent) {\r\n    el.attachEvent(\"on\" + type, listener);\r\n  }\r\n};\r\n\r\naddOrAttachListener(window, \"load\", function() {\r\n  var cbElem = document.getElementById(\"cb\");\r\n  var rcbElem = document.getElementById(\"rcb\");\r\n  addOrAttachListener(cbElem, \"click\", function() {\r\n    rcbElem.checked = cbElem.checked;\r\n  }, false);\r\n}, false);
\r\n
<label>Click Me!\r\n  <input id=\"cb\" type=\"checkbox\" />\r\n</label>\r\n<label>Reflection:\r\n  <input id=\"rcb\" type=\"checkbox\" />\r\n</label>
\r\n
\r\n
\r\n

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903de", + "creator": "Matt Peacock", + "createdAt": 1357909535000, + "text": "

If using mobile and you want the interface to update and show the checkbox as unchecked, use the following:

\n\n
$(\"#checkbox1\").prop('checked', false).checkboxradio(\"refresh\");\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903df", + "creator": "user1477929", + "createdAt": 1361980150000, + "text": "

Here's the complete answer\nusing jQuery

\n\n

I test it and it works 100% :D

\n\n
    // when the button (select_unit_button) is clicked it returns all the checed checkboxes values \n    $(\"#select_unit_button\").on(\"click\", function(e){\n\n             var arr = [];\n\n             $(':checkbox:checked').each(function(i){\n                 arr[i] = $(this).val(); // u can get id or anything else\n             });\n\n              //console.log(arr); // u can test it using this in google chrome\n    });\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903e0", + "creator": "Ramon de Jesus", + "createdAt": 1363254012000, + "text": "

To check a checkbox using jQuery 1.6 or higher just do this:

\n\n
checkbox.prop('checked', true);\n
\n\n

To uncheck, use:

\n\n
checkbox.prop('checked', false);\n
\n\n

Here' s what I like to use to toggle a checkbox using jQuery:

\n\n
checkbox.prop('checked', !checkbox.prop('checked'));\n
\n\n
\n\n

If you're using jQuery 1.5 or lower:

\n\n
checkbox.attr('checked', true);\n
\n\n

To uncheck, use:

\n\n
checkbox.attr('checked', false);\n
\n", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903e1", + "creator": "mathew", + "createdAt": 1368438611000, + "text": "
$(\".myCheckBox\").prop(\"checked\",\"checked\");\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e9172d", + "creator": "Blazemonger", + "createdAt": 1390314075000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903e3", + "creator": "ijarlax", + "createdAt": 1370870271000, + "text": "

In jQuery,

\n\n
if($(\"#checkboxId\").is(':checked')){\n    alert(\"Checked\");\n}\n
\n\n

or

\n\n
if($(\"#checkboxId\").attr('checked')==true){\n    alert(\"Checked\");\n}\n
\n\n

In JavaScript,

\n\n
if (document.getElementById(\"checkboxID\").checked){\n    alert(\"Checked\");\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e9172f", + "creator": "Samuel Liew", + "createdAt": 1370870410000, + "text": "This does not answer the question.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32422082fcc3049e91731", + "creator": "Blazemonger", + "createdAt": 1390314034000, + "text": "This answer is out-of-date because it uses .attr instead of .prop.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903e2", + "creator": "naor", + "createdAt": 1369207531000, + "text": "

Be aware of memory leaks in Internet Explorer prior to Internet Explorer 9, as the jQuery documentation states:

\n\n
\n

In Internet Explorer prior to version 9, using .prop() to set a DOM\n element property to anything other than a simple primitive value\n (number, string, or boolean) can cause memory leaks if the property is\n not removed (using .removeProp()) before the DOM element is removed\n from the document. To safely set values on DOM objects without memory\n leaks, use .data().

\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e91732", + "creator": "Blazemonger", + "createdAt": 1390313977000, + "text": "This is irrelevant, since all the (correct) answers use .prop('checked',true).", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32422082fcc3049e91733", + "creator": "naor", + "createdAt": 1390337597000, + "text": "Not sure I understood your comment.This still exists in jQuery documentation. Are you implying there is no memory leak in IE < 9 ?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32422082fcc3049e91735", + "creator": "Blazemonger", + "createdAt": 1390337795000, + "text": "There is no memory leak in this case, since we are setting it to a simple primitive value (Boolean).", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903e4", + "creator": "mahmoh", + "createdAt": 1373940652000, + "text": "
$('controlCheckBox').click(function(){\n    var temp = $(this).prop('checked');\n    $('controlledCheckBoxes').prop('checked', temp);\n});\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e91736", + "creator": "Mark Amery", + "createdAt": 1418668119000, + "text": "-​1 for being a code-only answer, not answering the question directly, and adding nothing that other answers hadn't already covered.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903e5", + "creator": "tamilmani", + "createdAt": 1376633857000, + "text": "

Here is the code and demo for how to check multiple check boxes...

\n\n

http://jsfiddle.net/tamilmani/z8TTt/

\n\n
$(\"#check\").on(\"click\", function () {\n\n    var chk = document.getElementById('check').checked;\n    var arr = document.getElementsByTagName(\"input\");\n\n    if (chk) {\n        for (var i in arr) {\n            if (arr[i].name == 'check') arr[i].checked = true;\n        }\n    } else {\n        for (var i in arr) {\n            if (arr[i].name == 'check') arr[i].checked = false;\n        }\n    }\n});\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903e6", + "creator": "eggshot", + "createdAt": 1379054222000, + "text": "

For overall:

\n\n
$(\"#checkAll\").click(function(){\n    $(\".somecheckBoxes\").prop('checked',$(this).prop('checked')?true:false);\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903e7", + "creator": "Alex W", + "createdAt": 1379466855000, + "text": "

Plain JavaScript is very simple and much less overhead:

\n\n
var elements = document.getElementsByClassName('myCheckBox');\nfor(var i = 0; i < elements.length; i++)\n{\n    elements[i].checked = true;\n}\n
\n\n

Example here

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e9173b", + "creator": "Alex W", + "createdAt": 1418674367000, + "text": "@MarkAmery The accepted answer does not cover how to do it without jQuery. My answer adds supplementary benefit to the accepted answer.", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903e8", + "creator": "Muhammad Aamir Ali", + "createdAt": 1381566222000, + "text": "

Another possible solution:

\n\n
    var c = $(\"#checkboxid\");\n    if (c.is(\":checked\")) {\n         $('#checkboxid').prop('checked', false);\n    } else {\n         $('#checkboxid').prop('checked', true);\n    }\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903e9", + "creator": "jj2422", + "createdAt": 1384410893000, + "text": "

To check and uncheck

\n\n
$('.myCheckbox').prop('checked', true);\n$('.myCheckbox').prop('checked', false);\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32422082fcc3049e9173e", + "creator": "Mark Amery", + "createdAt": 1418667936000, + "text": "-​1; this adds no value at all to an already bloated thread. The code here is exactly the same as the accepted answer.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903ea", + "creator": "anusha", + "createdAt": 1398234898000, + "text": "

You can try this:

\n\n
$('input[name=\"activity[task_state]\"]').val(\"specify the value you want to check \")\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903eb", + "creator": "Friedrich", + "createdAt": 1401203503000, + "text": "

This is probably the shortest and easiest solution:

\n\n
$(\".myCheckBox\")[0].checked = true;\n
\n\n

or

\n\n
$(\".myCheckBox\")[0].checked = false;\n
\n\n
\n\n

Even shorter would be:

\n\n
$(\".myCheckBox\")[0].checked = !0;\n$(\".myCheckBox\")[0].checked = !1;\n
\n\n
\n\n

Here is a jsFiddle as well.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ec", + "creator": "NoWar", + "createdAt": 1404843776000, + "text": "

In case you use ASP.NET MVC, generate many checkboxes and later have to select/unselect all using JavaScript you can do the following.

\n\n

HTML

\n\n
@foreach (var item in Model)\n{\n    @Html.CheckBox(string.Format(\"ProductId_{0}\", @item.Id), @item.IsSelected)\n}\n
\n\n

JavaScript

\n\n
function SelectAll() {       \n        $('input[id^=\"ProductId_\"]').each(function () {          \n            $(this).prop('checked', true);\n        });\n    }\n\n    function UnselectAll() {\n        $('input[id^=\"ProductId_\"]').each(function () {\n            $(this).prop('checked', false);\n        });\n    }\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32423082fcc3049e91741", + "creator": "tpower", + "createdAt": 1404917908000, + "text": "Why is the each needed here?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903ed", + "creator": "Serhat Koroglu", + "createdAt": 1425561051000, + "text": "

When you checked a checkbox like;

\n\n
$('.className').attr('checked', 'checked')\n
\n\n

it might not be enough. You should also call the function below;

\n\n
$('.className').prop('checked', 'true')\n
\n\n

Especially when you removed the checkbox checked attribute.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ef", + "creator": "Ardalan Shahgholi", + "createdAt": 1474656194000, + "text": "

As @livefree75 said:

\n\n

jQuery 1.5.x and below

\n\n

You can also extend the $.fn object with new methods:

\n\n
(function($)  {\n   $.fn.extend({\n      check : function()  {\n         return this.filter(\":radio, :checkbox\").attr(\"checked\", true);\n      },\n      uncheck : function()  {\n         return this.filter(\":radio, :checkbox\").removeAttr(\"checked\");\n      }\n   });\n}(jQuery));\n
\n\n

But in new versions of jQuery, we have to use something like this:

\n\n

jQuery 1.6+

\n\n
    (function($)  {\n       $.fn.extend({\n          check : function()  {\n             return this.filter(\":radio, :checkbox\").prop(\"checked\", true);\n          },\n          uncheck : function()  {\n             return this.filter(\":radio, :checkbox\").prop(\"checked\",false);\n          }\n       });\n    }(jQuery));\n
\n\n

Then you can just do:

\n\n
    $(\":checkbox\").check();\n    $(\":checkbox\").uncheck();\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ee", + "creator": "logan", + "createdAt": 1429612147000, + "text": "

For jQuery 1.6+

\n\n
$('.myCheckbox').prop('checked', true);\n$('.myCheckbox').prop('checked', false);\n
\n\n

For jQuery 1.5.x and below

\n\n
$('.myCheckbox').attr('checked', true);\n$('.myCheckbox').attr('checked', false);\n
\n\n

To check,

\n\n
$('.myCheckbox').removeAttr('checked');\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f0", + "creator": "SedJ601", + "createdAt": 1475157354000, + "text": "

This may help someone.

\n\n

HTML5

\n\n
 <input id=\"check_box\" type=\"checkbox\" onclick=\"handleOnClick()\">\n
\n\n

JavaScript.

\n\n
  function handleOnClick(){\n\n      if($(\"#check_box\").prop('checked'))\n      {        \n          console.log(\"current state: checked\");\n      }\n      else\n      {         \n          console.log(\"current state: unchecked\");\n      }    \n }\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f1", + "creator": "Kenmeister", + "createdAt": 1489523558000, + "text": "

If you happen to be using Bootstrap (perhaps unawarely) ...

\n\n
$('#myCheckbox').bootstrapToggle('on')\n$('#myCheckbox').bootstrapToggle('off')\n
\n\n

http://www.bootstraptoggle.com/

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f3", + "creator": "Gaurang Sondagar", + "createdAt": 1512112133000, + "text": "

You can check a checkbox checked condition using JavaScript in different ways. You can see below.

\n\n
    \n
  1. First method -\n$('.myCheckbox').prop('checked', true);

  2. \n
  3. Second method -\n$('.myCheckbox').attr('checked', true);

  4. \n
  5. Third method (for check condition if checkbox is checked or not) - $('.myCheckbox').is(':checked')

  6. \n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f2", + "creator": "David Arul", + "createdAt": 1509542392000, + "text": "

\r\n
\r\n
if($('jquery_selector').is(\":checked\")){\r\n  //somecode\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f5", + "creator": "L Y E S - C H I O U K H", + "createdAt": 1546441804000, + "text": "

Edited on 2019 January

\n\n

You can use: .prop( propertyName ) - version added: 1.6

\n\n

\r\n
\r\n
p {margin: 20px 0 0;}\r\nb {color: red;}\r\nlabel {color: red;}
\r\n
<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"utf-8\">\r\n  <script src=\"https://code.jquery.com/jquery-1.10.2.js\"></script>\r\n</head>\r\n<body>\r\n \r\n<input id=\"check1\" type=\"checkbox\" checked=\"checked\">\r\n<label for=\"check1\">Check here</label>\r\n<p></p>\r\n \r\n<script>\r\n$( \"input\" ).change(function() {\r\n  var $input = $( this );\r\n  $( \"p\" ).html(\r\n    \"The .attr( \\\"checked\\\" ): <b>\" + $input.attr( \"checked\" ) + \"</b><br>\" +\r\n    \"The .prop( \\\"checked\\\" ): <b>\" + $input.prop( \"checked\" ) + \"</b><br>\" +\r\n    \"The .is( \\\":checked\\\" ): <b>\" + $input.is( \":checked\" ) + \"</b>\" );\r\n}).change();\r\n</script>\r\n \r\n</body>\r\n</html>
\r\n
\r\n
\r\n

\n\n

On Angular Framework

\n\n

Example 1

\n\n

In your .html file

\n\n
<input type=\"checkbox\" (change)=\"toggleEditable($event)\">\n
\n\n

In your .ts file

\n\n
toggleEditable(event) {\n     if ( event.target.checked ) {\n         this.contentEditable = true;\n    }\n}\n
\n\n

Example 2

\n\n

In your .html file

\n\n
<input type=\"checkbox\" [(ngModel)]=\"isChecked\" (change)=\"checkAction(isChecked ? 'Action1':'Action2')\" />\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f4", + "creator": "Alex Montoya", + "createdAt": 1537034246000, + "text": "

If you are using .prop('checked', true|false) and don’t have changed checkbox, you need to add trigger('click') like this:

\n\n
// Check\n$('#checkboxF1').prop( \"checked\", true).trigger('click');\n\n\n// Uncheck\n$('#checkboxF1').prop( \"checked\", false).trigger('click');\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32424082fcc3049e91749", + "creator": "pangyuteng", + "createdAt": 1596852649000, + "text": "so happy that I found this. thank you! almost went nuts!!", + "upvotes": 111, + "upvoterUsernames": [], + "downvotes": 111, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903f6", + "creator": "Kamil Kiełczewski", + "createdAt": 1577483586000, + "text": "

A JavaScript solution can be also simple and with less overhead:

\n\n
document.querySelectorAll('.myCheckBox').forEach(x=> x.checked=1)\n
\n\n

\r\n
\r\n
document.querySelectorAll('.myCheckBox').forEach(x=> x.checked=1)
\r\n
checked A: <input type=\"checkbox\" class=\"myCheckBox\"><br/>\r\nunchecked: <input type=\"checkbox\"><br/>\r\nchecked B: <input type=\"checkbox\" class=\"myCheckBox\"><br/>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903f7", + "creator": "Omar Odeh", + "createdAt": 1607084143000, + "text": "

You can do this if you have the id to check it

\n

document.getElementById('ElementId').checked = false

\n

And this to uncheck

\n

document.getElementById('ElementId').checked = true

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32424082fcc3049e9174b", + "creator": "BingLi224", + "createdAt": 1641027848000, + "text": "It works, but that's not about jquery.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e903f8", + "creator": "vnapastiuk", + "createdAt": 1627542405000, + "text": "

If you consider using vanilla js instead of jquery there is a solution:

\n
//for one element: \ndocument.querySelector('.myCheckBox').checked = true /* or false */ //will select the first matched element\n//for multiple elements:\nfor (const checkbox of document.querySelectorAll('.myCheckBox')) {\ncheckbox.checked = true //or false\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 5, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e903c9", + "creator": "Subodh Ghulaxe", + "createdAt": 1393343168000, + "text": "Check other ways to do this using jQuery here stackoverflow.com/a/22019103/1868660", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903ca", + "creator": "HumanInDisguise", + "createdAt": 1431353184000, + "text": "If you need the onchange event triggered, it's $("#mycheckbox").click();", + "upvotes": 134, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903cb", + "creator": "Alireza", + "createdAt": 1431451569000, + "text": ""Checking something" suggests testing it, so I think 'Making a checkbox checked' is a more clear and better title.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903cc", + "creator": "divine", + "createdAt": 1493203949000, + "text": "attr() doesnot work with IE. using prop() is the best option", + "upvotes": 337, + "upvoterUsernames": [], + "downvotes": 337, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903cd", + "creator": "Tal", + "createdAt": 1568966210000, + "text": "$(".myCheckBox").prop("checked", true)", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3684483, + "uvac": 3684526 + } + }, + { + "_id": "62f321bb082fcc3049e8fef1", + "title": "Compare two dates with JavaScript", + "title-lowercase": "compare two dates with javascript", + "creator": "Alex", + "createdAt": 1233256446000, + "status": "open", + "text": "

Can someone suggest a way to compare the values of two dates greater than, less than, and not in the past using JavaScript? The values will be coming from text boxes.

\n", + "upvotes": 3732, + "upvoterUsernames": [], + "downvotes": 1173, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2987777, + "answers": 40, + "answerItems": [ + { + "_id": "62f321c9082fcc3049e90c33", + "creator": "user35288", + "createdAt": 1233256671000, + "text": "
var date = new Date(); // will give you todays date.\n\n// following calls, will let you set new dates.\nsetDate()   \nsetFullYear()   \nsetHours()  \nsetMilliseconds()   \nsetMinutes()    \nsetMonth()  \nsetSeconds()    \nsetTime()\n\nvar yesterday = new Date();\nyesterday.setDate(...date info here);\n\nif(date>yesterday)  // will compare dates\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c34", + "creator": "Jason S", + "createdAt": 1233256761000, + "text": "

what format?

\n

If you construct a Javascript Date object, you can just subtract them to get a milliseconds difference (edit: or just compare them) :

\n
js>t1 = new Date()\nThu Jan 29 2009 14:19:28 GMT-0500 (Eastern Standard Time)\njs>t2 = new Date()\nThu Jan 29 2009 14:19:31 GMT-0500 (Eastern Standard Time)\njs>t2-t1\n2672\njs>t3 = new Date('2009 Jan 1')\nThu Jan 01 2009 00:00:00 GMT-0500 (Eastern Standard Time)\njs>t1-t3\n2470768442\njs>t1>t3\ntrue\n
\n", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c35", + "creator": "moonshadow", + "createdAt": 1233256810000, + "text": "

The Date object will do what you want - construct one for each date, then compare them using the >, <, <= or >=.

\n

The ==, !=, ===, and !== operators require you to use date.getTime() as in

\n
var d1 = new Date();\nvar d2 = new Date(d1);\nvar same = d1.getTime() === d2.getTime();\nvar notSame = d1.getTime() !== d2.getTime();\n
\n

to be clear just checking for equality directly with the date objects won't work

\n
var d1 = new Date();\nvar d2 = new Date(d1);\n\nconsole.log(d1 == d2);   // prints false (wrong!) \nconsole.log(d1 === d2);  // prints false (wrong!)\nconsole.log(d1 != d2);   // prints true  (wrong!)\nconsole.log(d1 !== d2);  // prints true  (wrong!)\nconsole.log(d1.getTime() === d2.getTime()); // prints true (correct)\n
\n

I suggest you use drop-downs or some similar constrained form of date entry rather than text boxes, though, lest you find yourself in input validation hell.

\n
\n

For the curious, date.getTime() documentation:

\n
\n

Returns the numeric value of the specified date as the number of milliseconds since January 1, 1970, 00:00:00 UTC. (Negative values are returned for prior times.)

\n
\n", + "upvotes": 5874, + "upvoterUsernames": [], + "downvotes": 2819, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327f4082fcc3049e92620", + "creator": "madprog", + "createdAt": 1415282170000, + "text": "You can also compare the numeric values of your dates in order to avoid comparing the objects themselves: date1.valueOf() == date2.valueOf()", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c37", + "creator": "sky", + "createdAt": 1248061017000, + "text": "
function datesEqual(a, b)\n{\n   return (!(a>b || b>a))\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c36", + "creator": "sh1mmer", + "createdAt": 1233257719000, + "text": "

In order to create dates from free text in Javascript you need to parse it into the Date() object.

\n\n

You could use Date.parse() which takes free text tries to convert it into a new date but if you have control over the page I would recommend using HTML select boxes instead or a date picker such as the YUI calendar control or the jQuery UI Datepicker.

\n\n

Once you have a date as other people have pointed out you can use simple arithmetic to subtract the dates and convert it back into a number of days by dividing the number (in seconds) by the number of seconds in a day (60*60*24 = 86400).

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c38", + "creator": "Pravesh Hajela", + "createdAt": 1302953440000, + "text": "

Dates comparison:

\n\n
var str1  = document.getElementById(\"Fromdate\").value;\nvar str2  = document.getElementById(\"Todate\").value;\nvar dt1   = parseInt(str1.substring(0,2),10); \nvar mon1  = parseInt(str1.substring(3,5),10);\nvar yr1   = parseInt(str1.substring(6,10),10); \nvar dt2   = parseInt(str2.substring(0,2),10); \nvar mon2  = parseInt(str2.substring(3,5),10); \nvar yr2   = parseInt(str2.substring(6,10),10); \nvar date1 = new Date(yr1, mon1, dt1); \nvar date2 = new Date(yr2, mon2, dt2); \n\nif(date2 < date1)\n{\n   alert(\"To date cannot be greater than from date\");\n   return false; \n} \nelse \n{ \n   alert(\"Submitting ...\");\n   document.form1.submit(); \n} \n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c39", + "creator": "foobar", + "createdAt": 1311390215000, + "text": "

Say you got the date objects A and B, get their EPOC time value, then subtract to get the difference in milliseconds.

\n\n
var diff = +A - +B;\n
\n\n

That's all.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c3b", + "creator": "Programming Guy", + "createdAt": 1320108492000, + "text": "

By far the easiest method is to subtract one date from the other and compare the result.

\n\n

\r\n
\r\n
var oDateOne = new Date();\r\nvar oDateTwo = new Date();\r\n\r\nalert(oDateOne - oDateTwo === 0);\r\nalert(oDateOne - oDateTwo < 0);\r\nalert(oDateOne - oDateTwo > 0);
\r\n
\r\n
\r\n

\n", + "upvotes": 120, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c3a", + "creator": "Rodrigo Pinho Pereira de Souza", + "createdAt": 1313812924000, + "text": "

An Improved version of the code posted by \"some\"

\n\n
/* Compare the current date against another date.\n *\n * @param b  {Date} the other date\n * @returns   -1 : if this < b\n *             0 : if this === b\n *             1 : if this > b\n *            NaN : if a or b is an illegal date\n*/ \nDate.prototype.compare = function(b) {\n  if (b.constructor !== Date) {\n    throw \"invalid_date\";\n  }\n\n return (isFinite(this.valueOf()) && isFinite(b.valueOf()) ? \n          (this>b)-(this<b) : NaN \n        );\n};\n
\n\n

usage:

\n\n
  var a = new Date(2011, 1-1, 1);\n  var b = new Date(2011, 1-1, 1);\n  var c = new Date(2011, 1-1, 31);\n  var d = new Date(2011, 1-1, 31);\n\n  assertEquals( 0, a.compare(b));\n  assertEquals( 0, b.compare(a));\n  assertEquals(-1, a.compare(c));\n  assertEquals( 1, c.compare(a));\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f4082fcc3049e92628", + "creator": "RobG", + "createdAt": 1412060917000, + "text": "Presumably a is the Date instance on which the method is called. In which case can a be an invalid date, but still be a Date instance?", + "upvotes": 2287, + "upvoterUsernames": [], + "downvotes": 2287, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c3d", + "creator": "Jeeva", + "createdAt": 1341915612000, + "text": "
        from_date ='10-07-2012';\n        to_date = '05-05-2012';\n        var fromdate = from_date.split('-');\n        from_date = new Date();\n        from_date.setFullYear(fromdate[2],fromdate[1]-1,fromdate[0]);\n        var todate = to_date.split('-');\n        to_date = new Date();\n        to_date.setFullYear(todate[2],todate[1]-1,todate[0]);\n        if (from_date > to_date ) \n        {\n            alert(\"Invalid Date Range!\\nStart Date cannot be after End Date!\")\n\n            return false;\n        }\n
\n\n

Use this code to compare the date using javascript.

\n\n

Thanks\nD.Jeeva

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c3c", + "creator": "totten", + "createdAt": 1340460495000, + "text": "

If following is your date format, you can use this code:

\n\n
var first = '2012-11-21';\nvar second = '2012-11-03';\nif(parseInt(first.replace(/-/g,\"\"),10) > parseInt(second.replace(/-/g,\"\"),10)){\n   //...\n}\n
\n\n

It will check whether 20121121 number is bigger than 20121103 or not.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c3e", + "creator": "stay_hungry", + "createdAt": 1344509340000, + "text": "

you use this code,

\n\n
var firstValue = \"2012-05-12\".split('-');\nvar secondValue = \"2014-07-12\".split('-');\n\n var firstDate=new Date();\n firstDate.setFullYear(firstValue[0],(firstValue[1] - 1 ),firstValue[2]);\n\n var secondDate=new Date();\n secondDate.setFullYear(secondValue[0],(secondValue[1] - 1 ),secondValue[2]);     \n\n  if (firstDate > secondDate)\n  {\n   alert(\"First Date  is greater than Second Date\");\n  }\n else\n  {\n    alert(\"Second Date  is greater than First Date\");\n  }\n
\n\n

And also check this link\nhttp://www.w3schools.com/js/js_obj_date.asp

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c3f", + "creator": "jwchang", + "createdAt": 1345599614000, + "text": "

I usually store Dates as timestamps(Number) in databases.

\n

When I need to compare, I simply compare among those timestamps or

\n

convert it to Date Object and then compare with > <if necessary.

\n

Note that == or === does not work properly unless your variables are references of the same Date Object.

\n

Convert those Date objects to timestamp(number) first and then compare equality of them.

\n
\n

Date to Timestamp

\n
var timestamp_1970 = new Date(0).getTime(); // 1970-01-01 00:00:00\nvar timestamp = new Date().getTime(); // Current Timestamp\n
\n

Timestamp to Date

\n
var timestamp = 0; // 1970-01-01 00:00:00\nvar DateObject = new Date(timestamp);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c41", + "creator": "Salman A", + "createdAt": 1359648256000, + "text": "

The relational operators < <= > >= can be used to compare JavaScript dates:

\n\n
var d1 = new Date(2013, 0, 1);\nvar d2 = new Date(2013, 0, 2);\nd1 <  d2; // true\nd1 <= d2; // true\nd1 >  d2; // false\nd1 >= d2; // false\n
\n\n

However, the equality operators == != === !== cannot be used to compare (the value of) dates because:

\n\n
\n \n
\n\n

You can compare the value of dates for equality using any of these methods:

\n\n
var d1 = new Date(2013, 0, 1);\nvar d2 = new Date(2013, 0, 1);\n/*\n * note: d1 == d2 returns false as described above\n */\nd1.getTime() == d2.getTime(); // true\nd1.valueOf() == d2.valueOf(); // true\nNumber(d1)   == Number(d2);   // true\n+d1          == +d2;          // true\n
\n\n

Both Date.getTime() and Date.valueOf() return the number of milliseconds since January 1, 1970, 00:00 UTC. Both Number function and unary + operator call the valueOf() methods behind the scenes.

\n", + "upvotes": 302, + "upvoterUsernames": [], + "downvotes": 106, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c40", + "creator": "Yukulélé", + "createdAt": 1352973258000, + "text": "

Subtract two date get the difference in millisecond, if you get 0 it's the same date

\n\n\n\n
function areSameDate(d1, d2){\n    return d1 - d2 === 0\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c42", + "creator": "user1931504", + "createdAt": 1360665073000, + "text": "
var curDate=new Date();\nvar startDate=document.forms[0].m_strStartDate;\n\nvar endDate=document.forms[0].m_strEndDate;\nvar startDateVal=startDate.value.split('-');\nvar endDateVal=endDate.value.split('-');\nvar firstDate=new Date();\nfirstDate.setFullYear(startDateVal[2], (startDateVal[1] - 1), startDateVal[0]);\n\nvar secondDate=new Date();\nsecondDate.setFullYear(endDateVal[2], (endDateVal[1] - 1), endDateVal[0]);\nif(firstDate > curDate) {\n    alert(\"Start date cannot be greater than current date!\");\n    return false;\n}\nif (firstDate > secondDate) {\n    alert(\"Start date cannot be greater!\");\n    return false;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c43", + "creator": "Daniel Lidström", + "createdAt": 1369311669000, + "text": "

Compare < and > just as usual, but anything involving == or === should use a + prefix. Like so:

\n

\r\n
\r\n
const x = new Date('2013-05-23');\nconst y = new Date('2013-05-23');\n\n// less than, greater than is fine:\nconsole.log('x < y', x < y); // false\nconsole.log('x > y', x > y); // false\nconsole.log('x <= y', x <= y); // true\nconsole.log('x >= y', x >= y); // true\nconsole.log('x === y', x === y); // false, oops!\n\n// anything involving '==' or '===' should use the '+' prefix\n// it will then compare the dates' millisecond values\n\nconsole.log('+x === +y', +x === +y); // true
\r\n
\r\n
\r\n

\n", + "upvotes": 611, + "upvoterUsernames": [], + "downvotes": 143, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c44", + "creator": "Mina Gabriel", + "createdAt": 1375477576000, + "text": "

SHORT ANSWER

\n\n

Here is a function that return {boolean} if the from dateTime > to dateTime Demo in action

\n\n
var from = '08/19/2013 00:00'\nvar to = '08/12/2013 00:00 '\n\nfunction isFromBiggerThanTo(dtmfrom, dtmto){\n   return new Date(dtmfrom).getTime() >=  new Date(dtmto).getTime() ;\n}\nconsole.log(isFromBiggerThanTo(from, to)); //true\n
\n\n

Explanation

\n\n

jsFiddle

\n\n
var date_one = '2013-07-29 01:50:00',\ndate_two = '2013-07-29 02:50:00';\n//getTime() returns the number of milliseconds since 01.01.1970.\nvar timeStamp_date_one = new Date(date_one).getTime() ; //1375077000000 \nconsole.log(typeof timeStamp_date_one);//number \nvar timeStamp_date_two = new Date(date_two).getTime() ;//1375080600000 \nconsole.log(typeof timeStamp_date_two);//number \n
\n\n

since you are now having both datetime in number type\nyou can compare them with any Comparison operations

\n\n

( >, < ,= ,!= ,== ,!== ,>= AND <=)

\n\n

Then

\n\n

if you are familiar with C# Custom Date and Time Format String this library should do the exact same thing and help you format your date and time dtmFRM whether you are passing in date time string or unix format

\n\n

Usage

\n\n
var myDateTime = new dtmFRM();\n\nalert(myDateTime.ToString(1375077000000, \"MM/dd/yyyy hh:mm:ss ampm\"));\n//07/29/2013 01:50:00 AM\n\nalert(myDateTime.ToString(1375077000000,\"the year is yyyy and the day is dddd\"));\n//this year is 2013 and the day is Monday\n\nalert(myDateTime.ToString('1/21/2014', \"this month is MMMM and the day is dd\"));\n//this month is january and the day is 21\n
\n\n

DEMO

\n\n

all you have to do is passing any of these format pacified in the library js file

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c45", + "creator": "Brijesh", + "createdAt": 1379691451000, + "text": "

Here is what I did in one of my projects,

\n\n
function CompareDate(tform){\n     var startDate = new Date(document.getElementById(\"START_DATE\").value.substring(0,10));\n     var endDate = new Date(document.getElementById(\"END_DATE\").value.substring(0,10));\n\n     if(tform.START_DATE.value!=\"\"){\n         var estStartDate = tform.START_DATE.value;\n         //format for Oracle\n         tform.START_DATE.value = estStartDate + \" 00:00:00\";\n     }\n\n     if(tform.END_DATE.value!=\"\"){\n         var estEndDate = tform.END_DATE.value;\n         //format for Oracle\n         tform.END_DATE.value = estEndDate + \" 00:00:00\";\n     }\n\n     if(endDate <= startDate){\n         alert(\"End date cannot be smaller than or equal to Start date, please review you selection.\");\n         tform.START_DATE.value = document.getElementById(\"START_DATE\").value.substring(0,10);\n         tform.END_DATE.value = document.getElementById(\"END_DATE\").value.substring(0,10);\n         return false;\n     }\n}\n
\n\n

calling this on form onsubmit.\nhope this helps.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c46", + "creator": "mpen", + "createdAt": 1380296303000, + "text": "

Compare day only (ignoring time component):

\n
Date.prototype.sameDay = function(d) {\n  return this.getFullYear() === d.getFullYear()\n    && this.getDate() === d.getDate()\n    && this.getMonth() === d.getMonth();\n}\n
\n

Usage:

\n
if(date1.sameDay(date2)) {\n    // highlight day on calendar or something else clever\n}\n
\n
\n

I no longer recommend modifying the prototype of built-in objects. Try this instead:

\n

\r\n
\r\n
function isSameDay(d1, d2) {\n  return d1.getFullYear() === d2.getFullYear() &&\n    d1.getDate() === d2.getDate() &&\n    d1.getMonth() === d2.getMonth();\n}\n\n\nconsole.log(isSameDay(new Date('Jan 15 2021 02:39:53 GMT-0800'), new Date('Jan 15 2021 23:39:53 GMT-0800')));\nconsole.log(isSameDay(new Date('Jan 15 2021 10:39:53 GMT-0800'), new Date('Jan 16 2021 10:39:53 GMT-0800')));
\r\n
\r\n
\r\n

\n

N.B. the year/month/day will be returned for your timezone; I recommend using a timezone-aware library if you want to check if two dates are on the same day in a different timezone.

\n

e.g.

\n
> (new Date('Jan 15 2021 01:39:53 Z')).getDate()  // Jan 15 in UTC\n14  // Returns "14" because I'm in GMT-08\n
\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c47", + "creator": "Júlio Paulillo", + "createdAt": 1382636065000, + "text": "

Before comparing the Dates object, try setting both of their milliseconds to zero like Date.setMilliseconds(0);.

\n\n

In some cases where the Date object is dynamically created in javascript, if you keep printing the Date.getTime(), you'll see the milliseconds changing, which will prevent the equality of both dates.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c48", + "creator": "iinvole", + "createdAt": 1391056921000, + "text": "

Try using this code

\n\n
var f =date1.split(\"/\");\n\nvar t =date2.split(\"/\");\n\nvar x =parseInt(f[2]+f[1]+f[0]);\n\nvar y =parseInt(t[2]+t[1]+t[0]);\n\nif(x > y){\n    alert(\"date1 is after date2\");\n}\n\nelse if(x < y){\n    alert(\"date1 is before date2\");\n}\n\nelse{\n    alert(\"both date are same\");\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f6082fcc3049e92631", + "creator": "Isochronous", + "createdAt": 1423501619000, + "text": "Why the hell do I keep losing reputation when this answer gets downvoted?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c49", + "creator": "Paul", + "createdAt": 1395848465000, + "text": "

Just to add yet another possibility to the many existing options, you could try:

\n\n
if (date1.valueOf()==date2.valueOf()) .....\n
\n\n

...which seems to work for me. Of course you do have to ensure that both dates are not undefined...

\n\n
if ((date1?date1.valueOf():0)==(date2?date2.valueOf():0) .....\n
\n\n

This way we can ensure that a positive comparison is made if both are undefined also, or...

\n\n
if ((date1?date1.valueOf():0)==(date2?date2.valueOf():-1) .....\n
\n\n

...if you prefer them not to be equal.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4b", + "creator": "vickisys", + "createdAt": 1430911528000, + "text": "
var date_today=new Date();\nvar formated_date = formatDate(date_today);//Calling formatDate Function\n\nvar input_date=\"2015/04/22 11:12 AM\";\n\nvar currentDateTime = new Date(Date.parse(formated_date));\nvar inputDateTime   = new Date(Date.parse(input_date));\n\nif (inputDateTime <= currentDateTime){\n    //Do something...\n}\n\nfunction formatDate(date) {\n    var hours = date.getHours();\n    var minutes = date.getMinutes();\n    var ampm = hours >= 12 ? 'PM' : 'AM';\n\n    hours = hours % 12;\n    hours = hours ? hours : 12; // the hour '0' should be '12'\n    hours   = hours < 10 ? '0'+hours : hours ;\n\n    minutes = minutes < 10 ? '0'+minutes : minutes;\n\n    var strTime = hours+\":\"+minutes+ ' ' + ampm;\n    return  date.getFullYear()+ \"/\" + ((date.getMonth()+1) < 10 ? \"0\"+(date.getMonth()+1) :\n    (date.getMonth()+1) ) + \"/\" + (date.getDate() < 10 ? \"0\"+date.getDate() :\n    date.getDate()) + \" \" + strTime;\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4a", + "creator": "hex494D49", + "createdAt": 1403562138000, + "text": "

Let's suppose that you deal with this 2014[:-/.]06[:-/.]06 or this 06[:-/.]06[:-/.]2014 date format, then you may compare dates this way

\n\n
var a = '2014.06/07', b = '2014-06.07', c = '07-06/2014', d = '07/06.2014';\n\nparseInt(a.replace(/[:\\s\\/\\.-]/g, '')) == parseInt(b.replace(/[:\\s\\/\\.-]/g, '')); // true\nparseInt(c.replace(/[:\\s\\/\\.-]/g, '')) == parseInt(d.replace(/[:\\s\\/\\.-]/g, '')); // true\nparseInt(a.replace(/[:\\s\\/\\.-]/g, '')) < parseInt(b.replace(/[:\\s\\/\\.-]/g, '')); // false\nparseInt(c.replace(/[:\\s\\/\\.-]/g, '')) > parseInt(d.replace(/[:\\s\\/\\.-]/g, '')); // false\n
\n\n

As you can see, we strip separator(s) and then compare integers.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4d", + "creator": "Razan Paul", + "createdAt": 1467697980000, + "text": "

Via Moment.js

\n\n

Jsfiddle: http://jsfiddle.net/guhokemk/1/

\n\n
function compare(dateTimeA, dateTimeB) {\n    var momentA = moment(dateTimeA,\"DD/MM/YYYY\");\n    var momentB = moment(dateTimeB,\"DD/MM/YYYY\");\n    if (momentA > momentB) return 1;\n    else if (momentA < momentB) return -1;\n    else return 0;\n}\n\nalert(compare(\"11/07/2015\", \"10/07/2015\"));\n
\n\n

The method returns 1 if dateTimeA is greater than dateTimeB

\n\n

The method returns 0 if dateTimeA equals dateTimeB

\n\n

The method returns -1 if dateTimeA is less than dateTimeB

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4c", + "creator": "Qasim", + "createdAt": 1460123134000, + "text": "

Hi Here is my code to compare dates . In my case i am doing a check to not allow to select past dates.

\n\n
var myPickupDate = <pick up date> ;\nvar isPastPickupDateSelected = false;\nvar currentDate = new Date();\n\nif(currentDate.getFullYear() <= myPickupDate.getFullYear()){\n    if(currentDate.getMonth()+1 <= myPickupDate.getMonth()+1 || currentDate.getFullYear() < myPickupDate.getFullYear()){\n                        if(currentDate.getDate() <= myPickupDate.getDate() || currentDate.getMonth()+1 < myPickupDate.getMonth()+1 || currentDate.getFullYear() < myPickupDate.getFullYear()){\n                                            isPastPickupDateSelected = false;\n                                            return;\n                                        }\n                    }\n}\nconsole.log(\"cannot select past pickup date\");\nisPastPickupDateSelected = true;\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4f", + "creator": "yoniLavi", + "createdAt": 1478715748000, + "text": "

Another way to compare two dates, is through the toISOString() method. This is especially useful when comparing to a fixed date kept in a string, since you can avoid creating a short-lived object. By virtue of the ISO 8601 format, you can compare these strings lexicographically (at least when you're using the same timezone).

\n\n

I'm not necessarily saying that it's better than using time objects or timestamps; just offering this as another option. There might be edge cases when this could fail, but I haven't stumbled upon them yet :)

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c4e", + "creator": "Salahin Rocky", + "createdAt": 1472662839000, + "text": "

To compare two date we can use date.js JavaScript library which can be found at : https://code.google.com/archive/p/datejs/downloads

\n\n

and use the Date.compare( Date date1, Date date2 ) method and it return a number which mean the following result:

\n\n

-1 = date1 is lessthan date2.

\n\n

0 = values are equal.

\n\n

1 = date1 is greaterthan date2.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c51", + "creator": "user4439128", + "createdAt": 1497530541000, + "text": "

try this\nwhile compare date should be iso format \"yyyy-MM-dd\"\nif you want to compare only dates use this datehelper

\n\n
<a href=\"https://plnkr.co/edit/9N8ZcC?p=preview\"> Live Demo</a>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c50", + "creator": "Sanjeev Singh", + "createdAt": 1489511414000, + "text": "

Note - Compare Only Date Part:

\n\n

When we compare two date in javascript. It takes hours, minutes and seconds also into consideration.. So If we only need to compare date only, this is the approach:

\n\n
var date1= new Date(\"01/01/2014\").setHours(0,0,0,0);\n\nvar date2= new Date(\"01/01/2014\").setHours(0,0,0,0);\n
\n\n

Now: if date1.valueOf()> date2.valueOf() will work like a charm.

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f6082fcc3049e9263b", + "creator": "Herve Mutombo", + "createdAt": 1626278299000, + "text": "This is the answer I came to look for!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c52", + "creator": "Alireza", + "createdAt": 1499767186000, + "text": "

Comparing dates in JavaScript is quite easy... JavaScript has built-in comparison system for dates which makes it so easy to do the comparison...

\n\n

Just follow these steps for comparing 2 dates value, for example you have 2 inputs which each has a Date value in String and you to compare them...

\n\n

1. you have 2 string values you get from an input and you'd like to compare them, they are as below:

\n\n
var date1 = '01/12/2018';\nvar date2 = '12/12/2018';\n
\n\n

2. They need to be Date Object to be compared as date values, so simply convert them to date, using new Date(), I just re-assign them for simplicity of explanation, but you can do it anyway you like:

\n\n
date1 = new Date(date1);\ndate2 = new Date(date2);\n
\n\n

3. Now simply compare them, using the > < >= <=

\n\n
date1 > date2;  //false\ndate1 < date2;  //true\ndate1 >= date2; //false\ndate1 <= date2; //true\n
\n\n

\"compare

\n", + "upvotes": 159, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c54", + "creator": "bbsimonbb", + "createdAt": 1508493107000, + "text": "

BEWARE THE TIMEZONE

\n\n

A javascript date has no notion of timezone. It's a moment in time (ticks since the epoch) with handy functions for translating to and from strings in the \"local\" timezone. If you want to work with dates using date objects, as everyone here is doing, you want your dates to represent UTC midnight at the start of the date in question. This is a common and necessary convention that lets you work with dates regardless of the season or timezone of their creation. So you need to be very vigilant to manage the notion of timezone, particularly when you create your midnight UTC Date object.

\n\n

Most of the time, you will want your date to reflect the timezone of the user. Click if today is your birthday. Users in NZ and US click at the same time and get different dates. In that case, do this...

\n\n
// create a date (utc midnight) reflecting the value of myDate and the environment's timezone offset.\nnew Date(Date.UTC(myDate.getFullYear(),myDate.getMonth(), myDate.getDate()));\n
\n\n

Sometimes, international comparability trumps local accuracy. In that case, do this...

\n\n
// the date in London of a moment in time. Device timezone is ignored.\nnew Date(Date.UTC(myDate.getUTCYear(), myDate.getyUTCMonth(), myDate.getUTCDate()));\n
\n\n

Now you can directly compare your date objects as the other answers suggest.

\n\n

Having taken care to manage timezone when you create, you also need to be sure to keep timezone out when you convert back to a string representation. So you can safely use...

\n\n\n\n

And totally avoid everything else, especially...

\n\n\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c53", + "creator": "Bachas", + "createdAt": 1505905900000, + "text": "

You can date compare as most simple and understandable way like.

\n\n
<input type=\"date\" id=\"getdate1\" />\n<input type=\"date\" id=\"getdate2\" />\n
\n\n

let suppose you have two date input you want to compare them.

\n\n

so firstly write a common method to parse date.

\n\n
 <script type=\"text/javascript\">\n            function parseDate(input) {\n             var datecomp= input.split('.'); //if date format 21.09.2017\n\n              var tparts=timecomp.split(':');//if time also giving\n              return new Date(dparts[2], dparts[1]-1, dparts[0], tparts[0], tparts[1]);\n// here new date(  year, month, date,)\n            }\n        </script>\n
\n\n

parseDate() is the make common method for parsing the date.\n now you can checks your date =, > ,< any type of compare

\n\n
    <script type=\"text/javascript\">\n\n              $(document).ready(function(){\n              //parseDate(pass in this method date);\n                    Var Date1=parseDate($(\"#getdate1\").val());\n                        Var Date2=parseDate($(\"#getdate2\").val());\n               //use any oe < or > or = as per ur requirment \n               if(Date1 = Date2){\n         return false;  //or your code {}\n}\n });\n    </script>\n
\n\n

For Sure this code will help you.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c56", + "creator": "Kamil Kiełczewski", + "createdAt": 1582829673000, + "text": "

Performance

\n\n

Today 2020.02.27 I perform tests of chosen solutions on Chrome v80.0, Safari v13.0.5 and Firefox 73.0.1 on MacOs High Sierra v10.13.6

\n\n

Conclusions

\n\n\n\n

\"enter

\n\n

Details

\n\n

In below snippet solutions used in performance tests are presented. You can perform test in you machine HERE

\n\n

\r\n
\r\n
function A(d1,d2) {\r\n\treturn d1.getTime() == d2.getTime();\r\n}\r\n\r\nfunction B(d1,d2) {\r\n\treturn d1.valueOf() == d2.valueOf();\r\n}\r\n\r\nfunction C(d1,d2) {\r\n\treturn Number(d1)   == Number(d2);\r\n}\r\n\r\nfunction D(d1,d2) {\r\n\treturn d1 == d2;\r\n}\r\n\r\nfunction E(d1,d2) {\r\n\treturn d1 === d2;\r\n}\r\n\r\nfunction F(d1,d2) {\r\n\treturn (!(d1>d2 || d2>d1));\r\n}\r\n\r\nfunction G(d1,d2) {\r\n\treturn d1*1 == d2*1;\r\n}\r\n\r\nfunction H(d1,d2) {\r\n\treturn +d1 == +d2;\r\n}\r\n\r\nfunction I(d1,d2) {\r\n\treturn !(+d1 - +d2);\r\n}\r\n\r\nfunction J(d1,d2) {\r\n\treturn !(d1 - d2);\r\n}\r\n\r\nfunction K(d1,d2) {\r\n\treturn d1 - d2 == 0;\r\n}\r\n\r\nfunction L(d1,d2) {\r\n\treturn !((d1>d2)-(d1<d2));\r\n}\r\n\r\nfunction M(d1,d2) {\r\n  return d1.getFullYear() === d2.getFullYear()\r\n    && d1.getDate() === d2.getDate()\r\n    && d1.getMonth() === d2.getMonth();\r\n}\r\n\r\nfunction N(d1,d2) {\r\n\treturn (isFinite(d1.valueOf()) && isFinite(d2.valueOf()) ? !((d1>d2)-(d1<d2)) : false );\r\n}\r\n\r\n\r\n// TEST\r\n\r\nlet past= new Date('2002-12-24'); // past\r\nlet now= new Date('2020-02-26');  // now\r\n\r\nconsole.log('Code  d1>d2  d1<d2  d1=d2')\r\nvar log = (l,f) => console.log(`${l}     ${f(now,past)}  ${f(past,now)}  ${f(now,now)}`);\r\n\r\nlog('A',A);\r\nlog('B',B);\r\nlog('C',C);\r\nlog('D',D);\r\nlog('E',E);\r\nlog('G',G);\r\nlog('H',H);\r\nlog('I',I);\r\nlog('J',J);\r\nlog('K',K);\r\nlog('L',L);\r\nlog('M',M);\r\nlog('N',N);
\r\n
p {color: red}
\r\n
<p>This snippet only presents tested solutions (it not perform tests itself)</p>
\r\n
\r\n
\r\n

\n\n

Results for chrome

\n\n

\"enter

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f7082fcc3049e92640", + "creator": "TheMaster", + "createdAt": 1585536308000, + "text": "But d1==d2 or d1===d2 is useless in the context of the question.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c55", + "creator": "MICHAEL PRABHU", + "createdAt": 1561704469000, + "text": "

The simple way is,

\n\n
var first = '2012-11-21';\nvar second = '2012-11-03';\n\nif (new Date(first) > new Date(second) {\n    .....\n}\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f7082fcc3049e92642", + "creator": "wrlee", + "createdAt": 1641556674000, + "text": "This will not work for for == nor ===", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c57", + "creator": "Obot Ernest", + "createdAt": 1609275958000, + "text": "
If you are using **REACT OR REACT NATIVE**, use this and it will work (Working like charm)\n
\n

If the two dates are the same, it will return TRUE otherwise FALSE

\n
const compareDate = (dateVal1, dateVal2) => {\n        if (dateVal1.valueOf() === dateVal2.valueOf()){\n            return true;\n        }\n        else { return false;}\n    }\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f7082fcc3049e92645", + "creator": "phoenixstudio", + "createdAt": 1609287545000, + "text": "you answer looks like @Sanjeev Singh answer, can you explain what is the differnace ?", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c59", + "creator": "Aaquib Jawed", + "createdAt": 1627478240000, + "text": "
function compare_date(date1, date2){\nconst x = new Date(date1)\nconst y = new Date(date2)\nfunction checkyear(x, y){\n    if(x.getFullYear()>y.getFullYear()){\n        return "Date1 > Date2"\n    }\n    else if(x.getFullYear()<y.getFullYear()){\n        return "Date2 > Date1"\n    }\n    else{\n        return checkmonth(x, y)\n    }\n}\nfunction checkmonth(x, y){\n    if(x.getMonth()>y.getFullYear()){\n        return "Date1 > Date2"\n    }\n    else if(x.getMonth()<y.getMonth){\n        return "Date2 > Date1"\n    }\n    else {\n        return checkDate(x, y)\n    }\n}\nfunction checkDate(x, y){\n    if(x.getDate()>y.getFullYear()){\n        return "Date1 > Date2"\n    }\n    else if(x.getDate()<y.getDate()){\n        return "Date2 > Date1"\n    }\n    else {\n        return checkhour(x,y)\n    }\n}\nfunction checkhour(x, y){\n    if(x.getHours()>y.getHours()){\n        return "Date1 > Date2"\n    }\n    else if(x.getHours()<y.getHours()){\n        return "Date2 > Date1"\n    }\n    else {\n        return checkhmin(x,y)\n    }\n}\nfunction checkhmin(x,y){\n    if(x.getMinutes()>y.getMinutes()){\n        return "Date1 > Date2"\n    }\n    else if(x.getMinutes()<y.getMinutes()){\n        return "Date2 > Date1"\n    }\n    else {\n        return "Date1 = Date2"\n    }\n}\nreturn checkyear(x, y)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c58", + "creator": "Sohail Shahzad", + "createdAt": 1615879050000, + "text": "

using momentjs for dates manipulation.


\nFor checking one date is same or after as another by using \n

isSameOrAfter() method

\n
moment('2010-10-20').isSameOrAfter('2010-10-20') //true;\n
\n

For checking one date is after as another by using isAfter() method

\n
moment('2020-01-20').isAfter('2020-01-21'); // false\nmoment('2020-01-20').isAfter('2020-01-19'); // true\n
\n

For checking one date is before another by using isBefore() method.

\n
moment('2020-01-20').isBefore('2020-01-21'); // true\nmoment('2020-01-20').isBefore('2020-01-19'); // false\n
\n

For checking one date is same as another by using isSame() method

\n
moment('2020-01-20').isSame('2020-01-21'); // false\nmoment('2020-01-20').isSame('2020-01-20'); // true\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f7082fcc3049e92649", + "creator": "WebDude0482", + "createdAt": 1650489600000, + "text": "Shouldn't need additional JavaScript mods to accomplish this.", + "upvotes": 2216, + "upvoterUsernames": [], + "downvotes": 2216, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c5a", + "creator": "Guvanch", + "createdAt": 1635436529000, + "text": "

My simple answer for this question

\n
checkDisabled(date) {\n    const today = new Date()\n    const newDate = new Date(date._d)\n    if (today.getTime() > newDate.getTime()) {\n        return true\n    }\n    return false\n}\n       \n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90bd3", + "creator": "Halceyon", + "createdAt": 1379943338000, + "text": "When it comes to DateTime and manipulation in JS, I look no further than momentjs :)", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bd4", + "creator": "Lukas Liesis", + "createdAt": 1465449962000, + "text": "no need to use momentjs to compare 2 dates. Just use pure javascript's Date object. Check main answer for more details.", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bd5", + "creator": "Bob Brown", + "createdAt": 1619967830000, + "text": "For those like me who may have come along later, moment.js is now in "maintenance mode," i.e. no longer being actively developed.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2991512, + "uvac": 2991552 + } + }, + { + "_id": "62f321bb082fcc3049e8ff0c", + "title": "What is the scope of variables in JavaScript?", + "title-lowercase": "what is the scope of variables in javascript?", + "creator": "lYriCAlsSH", + "createdAt": 1233476839000, + "status": "open", + "text": "

What is the scope of variables in javascript? Do they have the same scope inside as opposed to outside a function? Or does it even matter? Also, where are the variables stored if they are defined globally?

\n", + "upvotes": 2240, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 559307, + "answers": 22, + "answerItems": [ + { + "_id": "62f3220c082fcc3049e90fe9", + "creator": "krosenvold", + "createdAt": 1233477334000, + "text": "

Javascript uses scope chains to establish the scope for a given function. There is typically one global scope, and each function defined has its own nested scope. Any function defined within another function has a local scope which is linked to the outer function. It's always the position in the source that defines the scope.

\n\n

An element in the scope chain is basically a Map with a pointer to its parent scope.

\n\n

When resolving a variable, javascript starts at the innermost scope and searches outwards.

\n", + "upvotes": 437, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fea", + "creator": "geowa4", + "createdAt": 1233478089000, + "text": "

Here's an example:

\n\n
<script>\n\nvar globalVariable = 7; //==window.globalVariable\n\nfunction aGlobal( param ) { //==window.aGlobal(); \n                            //param is only accessible in this function\n  var scopedToFunction = {\n    //can't be accessed outside of this function\n\n    nested : 3 //accessible by: scopedToFunction.nested\n  };\n\n  anotherGlobal = {\n    //global because there's no `var`\n  }; \n\n}\n\n</script>\n
\n\n

You'll want to investigate closures, and how to use them to make private members.

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fe8", + "creator": "Jon Skeet", + "createdAt": 1233477094000, + "text": "

Variables declared globally have a global scope. Variables declared within a function are scoped to that function, and shadow global variables of the same name.

\n\n

(I'm sure there are many subtleties that real JavaScript programmers will be able to point out in other answers. In particular I came across this page about what exactly this means at any time. Hopefully this more introductory link is enough to get you started though.)

\n", + "upvotes": 215, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92cdf", + "creator": "Kenan Banks", + "createdAt": 1515515389000, + "text": "Somehow Jon Skeet is responsible for MY most popular answer on Stack Overflow.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90feb", + "creator": "kennytm", + "createdAt": 1270552779000, + "text": "

In \"Javascript 1.7\" (Mozilla's extension to Javascript) one can also declare block-scope variables with let statement:

\n\n
 var a = 4;\n let (a = 3) {\n   alert(a); // 3\n }\n alert(a);   // 4\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32974082fcc3049e92ce1", + "creator": "IgorGanapolsky", + "createdAt": 1293557954000, + "text": "Yeah, but is it safe to use? I mean would I realistically choose this implementation if my code will run in WebKit?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32974082fcc3049e92ce3", + "creator": "kennytm", + "createdAt": 1293560951000, + "text": "@Python: No, WebKit doesn't support let.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32974082fcc3049e92ce5", + "creator": "GazB", + "createdAt": 1349942777000, + "text": "I guess the only valid use for this would be if you knew all the clients would be using a Mozilla browser like for a companies internal system.", + "upvotes": 423, + "upvoterUsernames": [], + "downvotes": 423, + "downvoterUsernames": [] + }, + { + "_id": "62f32974082fcc3049e92ce7", + "creator": "Gerard ONeill", + "createdAt": 1382659340000, + "text": "Or if you are programming using the XUL framework, Mozilla's interface framework where you build using css, xml, and javascript.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fee", + "creator": "Mig82", + "createdAt": 1376156234000, + "text": "

Try this curious example. In the example below if a were a numeric initialized at 0, you'd see 0 and then 1. Except a is an object and javascript will pass f1 a pointer of a rather than a copy of it. The result is that you get the same alert both times.

\n\n
var a = new Date();\nfunction f1(b)\n{\n    b.setDate(b.getDate()+1);\n    alert(b.getDate());\n}\nf1(a);\nalert(a.getDate());\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fed", + "creator": "austincheney", + "createdAt": 1363887071000, + "text": "

I found that many people new to JavaScript have trouble understanding that inheritance is available by default in the language and that function scope is the only scope, so far. I provided an extension to a beautifier I wrote at the end of last year called JSPretty. The feature colors function scope in the code and always associates a color to all variables declared in that scope. Closure is visually demonstrated when a variable with a color from one scope is used in a different scope.

\n\n

Try the feature at:

\n\n\n\n

See a demo at:

\n\n\n\n

View the code at:

\n\n\n\n

Currently the feature offers support for a depth of 16 nested functions, but currently does not color global variables.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32974082fcc3049e92ce9", + "creator": "mplwork", + "createdAt": 1392317070000, + "text": "Doesn't work for me with Firefox 26. I paste code or load a file, click execute and nothing happens.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32974082fcc3049e92ceb", + "creator": "Ben Aston", + "createdAt": 1582825991000, + "text": "Scope and inheritance are two difference things.", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 149, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fec", + "creator": "James McMahon", + "createdAt": 1337103510000, + "text": "

The key, as I understand it, is that Javascript has function level scoping vs the more common C block scoping.

\n\n

Here is a good article on the subject.

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff0", + "creator": "Jhankar Mahbub", + "createdAt": 1403619178000, + "text": "

Global Scope :

\n\n

Global variables are exactly like global stars (Jackie Chan, Nelson Mandela). You can access them (get or set the value), from any part of your application. Global functions are like global events (New Year, Christmas). You can execute (call) them from any part of your application.

\n\n
//global variable\nvar a = 2;\n\n//global function\nfunction b(){\n   console.log(a);  //access global variable\n}\n
\n\n

Local Scope :

\n\n

If you are in the USA, you may know Kim Kardashian, infamous celebrity ( she somehow manages to make the tabloids). But people outside of the USA will not recognize her. She is a local star, bound to her territory.

\n\n

Local variables are like local stars. You can only access them (get or set the value) inside the scope. A local function is like local events - you can execute only (celebrate) inside that scope. If you want to access them from outside of the scope, you will get a reference error

\n\n
function b(){\n   var d = 21; //local variable\n   console.log(d);\n\n   function dog(){  console.log(a); }\n     dog(); //execute local function\n}\n\n console.log(d); //ReferenceError: dddddd is not defined    \n
\n\n
\n\n

Check this article for in-depth understanding of scope

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fef", + "creator": "Gerard ONeill", + "createdAt": 1382661677000, + "text": "

1) There is a global scope, a function scope, and the with and catch scopes. There is no 'block' level scope in general for variable's -- the with and the catch statements add names to their blocks.

\n\n

2) Scopes are nested by functions all the way to the global scope.

\n\n

3) Properties are resolved by going through the prototype chain. The with statement brings object property names into the lexical scope defined by the with block.

\n\n

EDIT: ECMAAScript 6 (Harmony) is spec'ed to support let, and I know chrome allows a 'harmony' flag, so perhaps it does support it..

\n\n

Let would be a support for block level scoping, but you have to use the keyword to make it happen.

\n\n

EDIT: Based on Benjamin's pointing out of the with and catch statements in the comments, I've edited the post, and added more. Both the with and the catch statements introduce variables into their respective blocks, and that is a block scope. These variables are aliased to the properties of the objects passed into them.

\n\n
 //chrome (v8)\n\n var a = { 'test1':'test1val' }\n test1   // error not defined\n with (a) { var test1 = 'replaced' }\n test1   // undefined\n a       // a.test1 = 'replaced'\n
\n\n

EDIT: Clarifying example:

\n\n

test1 is scoped to the with block, but is aliased to a.test1. 'Var test1' creates a new variable test1 in the upper lexical context (function, or global), unless it is a property of a -- which it is.

\n\n

Yikes! Be careful using 'with' -- just like var is a noop if the variable is already defined in the function, it is also a noop with respect to names imported from the object! A little heads up on the name already being defined would make this much safer. I personally will never use with because of this.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32974082fcc3049e92cef", + "creator": "Benjamin Gruenbaum", + "createdAt": 1382662537000, + "text": "You have some mistakes here, for one JavaScript does have forms of block scoping.", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [] + }, + { + "_id": "62f32974082fcc3049e92cf0", + "creator": "Benjamin Gruenbaum", + "createdAt": 1382727666000, + "text": "Which is exactly what block scoping means :)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90ff2", + "creator": "Yeasin Abedin", + "createdAt": 1413626096000, + "text": "

run the code. hope this will give an idea about scoping

\n\n
Name = 'global data';\ndocument.Name = 'current document data';\n(function(window,document){\nvar Name = 'local data';\nvar myObj = {\n    Name: 'object data',\n    f: function(){\n        alert(this.Name);\n    }\n};\n\nmyObj.newFun = function(){\n    alert(this.Name);\n}\n\nfunction testFun(){\n    alert(\"Window Scope : \" + window.Name + \n          \"\\nLocal Scope : \" + Name + \n          \"\\nObject Scope : \" + this.Name + \n          \"\\nCurrent document Scope : \" + document.Name\n         );\n}\n\n\ntestFun.call(myObj);\n})(window,document);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff1", + "creator": "Anshul", + "createdAt": 1411332250000, + "text": "

JavaScript have only two type of scope :

\n\n
    \n
  1. Global Scope : Global is nothing but a window level scope.Here, variable present throughout the application.
  2. \n
  3. Functional Scope : Variable declared within a function with var keyword has functional scope.
  4. \n
\n\n

Whenever a function is called, a variable scope object is created (and included in scope chain) which is followed by variables in JavaScript.

\n\n
        a = \"global\";\n         function outer(){ \n              b = \"local\";\n              console.log(a+b); //\"globallocal\"\n         }\nouter();\n
\n\n

Scope chain -->

\n\n
    \n
  1. Window level - a and outer function are at top level in scope chain.
  2. \n
  3. when outer function called a new variable scope object(and included in scope chain) added with variable b inside it.
  4. \n
\n\n

Now when a variable a required it first searches for nearest variable scope and if variable is not there than it move's to next object of variable scope chain.which is in this case is window level.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff3", + "creator": "koredalin", + "createdAt": 1436637807000, + "text": "

There are only function scopes in JS. Not block scopes!\nYou can see what is hoisting too.

\n\n
var global_variable = \"global_variable\";\nvar hoisting_variable = \"global_hoist\";\n\n// Global variables printed\nconsole.log(\"global_scope: - global_variable: \" + global_variable);\nconsole.log(\"global_scope: - hoisting_variable: \" + hoisting_variable);\n\nif (true) {\n    // The variable block will be global, on true condition.\n    var block = \"block\";\n}\nconsole.log(\"global_scope: - block: \" + block);\n\nfunction local_function() {\n    var local_variable = \"local_variable\";\n    console.log(\"local_scope: - local_variable: \" + local_variable);\n    console.log(\"local_scope: - global_variable: \" + global_variable);\n    console.log(\"local_scope: - block: \" + block);\n    // The hoisting_variable is undefined at the moment.\n    console.log(\"local_scope: - hoisting_variable: \" + hoisting_variable);\n\n    var hoisting_variable = \"local_hoist\";\n    // The hoisting_variable is now set as a local one.\n    console.log(\"local_scope: - hoisting_variable: \" + hoisting_variable);\n}\n\nlocal_function();\n\n// No variable in a separate function is visible into the global scope.\nconsole.log(\"global_scope: - local_variable: \" + local_variable);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff4", + "creator": "A. Randhawa", + "createdAt": 1443646005000, + "text": "

There are two types of scopes in JavaScript.

\n\n
    \n
  1. Global scope: variable which is announced in global scope can be used anywhere in the program very smoothly. For example:

    \n\n
    var carName = \" BMW\";\n\n// code here can use carName\n\nfunction myFunction() {\n     // code here can use carName \n}\n
  2. \n
  3. Functional scope or Local scope: variable declared in this scope can be used in its own function only. For example:

    \n\n
    // code here can not use carName\nfunction myFunction() {\n   var carName = \"BMW\";\n   // code here can use carName\n}\n
  4. \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32974082fcc3049e92cf3", + "creator": "Sebastian Simon", + "createdAt": 1647131249000, + "text": "This answer was already outdated when it was posted and didn’t add anything new to the existing answers.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90ff5", + "creator": "jackbean818", + "createdAt": 1446135139000, + "text": "

There are ALMOST only two types of JavaScript scopes:

\n\n\n\n

So, any blocks other than functions do not create a new scope. That explains why for-loops overwrite outer scoped variables:

\n\n
var i = 10, v = 10;\nfor (var i = 0; i < 5; i++) { var v = 5; }\nconsole.log(i, v);\n// output 5 5\n
\n\n

Using functions instead:

\n\n
var i = 10, v = 10;\n$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });\nconsole.log(i,v);\n// output 10 10\n
\n\n

In the first example, there was no block scope, so the initially declared variables were overwritten. In the second example, there was a new scope due to the function, so the initially declared variables were SHADOWED, and not overwritten.

\n\n

That's almost all you need to know in terms of JavaScript scoping, except:

\n\n\n\n

So you can see JavaScript scoping is actually extremely simple, albeit not always intuitive. A few things to be aware of:

\n\n\n\n

So this code:

\n\n
var i = 1;\nfunction abc() {\n  i = 2;\n  var i = 3;\n}\nconsole.log(i);     // outputs 1\n
\n\n

is equivalent to:

\n\n
var i = 1;\nfunction abc() {\n  var i;     // var declaration moved to the top of the scope\n  i = 2;\n  i = 3;     // the assignment stays where it is\n}\nconsole.log(i);\n
\n\n

This may seem counter intuitive, but it makes sense from the perspective of a imperative language designer.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff6", + "creator": "James Drinkard", + "createdAt": 1459344784000, + "text": "

Just to add to the other answers, scope is a look-up list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code. This look-up may be for the purposes of assigning to the variable, which is an LHS (lefthand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (righthand-side) reference. These look-ups are what the JavaScript engine is doing internally when it's compiling and executing the code.

\n\n

So from this perspective, I think that a picture would help that I found in the Scopes and Closures ebook by Kyle Simpson:

\n\n

\"image\"

\n\n

Quoting from his ebook:

\n\n
\n

The building represents our program’s nested scope ruleset. The first\n floor of the building represents your currently executing scope,\n wherever you are. The top level of the building is the global scope.\n You resolve LHS and RHS references by looking on your current floor,\n and if you don’t find it, taking the elevator to the next floor,\n looking there, then the next, and so on. Once you get to the top floor\n (the global scope), you either find what you’re looking for, or you\n don’t. But you have to stop regardless.

\n
\n\n

One thing of note that is worth mentioning, \"Scope look-up stops once it finds the first match\".

\n\n

This idea of \"scope levels\" explains why \"this\" can be changed with a newly created scope, if it's being looked up in a nested function.\nHere is a link that goes into all these details, Everything you wanted to know about javascript scope

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff7", + "creator": "mrmaclean89", + "createdAt": 1505601307000, + "text": "

My understanding is that there are 3 scopes: global scope, available globally; local scope, available to an entire function regardless of blocks; and block scope, only available to the block, statement, or expression on which it was used. Global and local scope are indicated with the keyword 'var', either within a function or outside, and block scope is indicated with the keyword 'let'.

\n\n

For those that believe there is only global and local scope, please explain why Mozilla would have an entire page describing the nuances of block scope in JS.

\n\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff9", + "creator": "Abdur Rahman", + "createdAt": 1509527778000, + "text": "

In JavaScript there are two types of scope:

\n\n\n\n

The Below function has a local scope variable carName. And this variable is not accessible from outside of the function.

\n\n
function myFunction() {\n    var carName = \"Volvo\";\n    alert(carName);\n    // code here can use carName\n}\n
\n\n

The Below Class has a Global scope variable carName. And this variable is accessible from everywhere in the class.

\n\n
class {\n\n    var carName = \" Volvo\";\n\n    // code here can use carName\n\n    function myFunction() {\n        alert(carName);\n        // code here can use carName \n    }\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ff8", + "creator": "Gibolt", + "createdAt": 1506457391000, + "text": "

Modern Js, ES6+, 'const' and 'let'

\n

You should be using block scoping for every variable you create, just like most other major languages. var is obsolete. This makes your code safer and more maintainable.

\n

const should be used for 95% of cases. It makes it so the variable reference can't change. Array, object, and DOM node properties can change and should likely be const.

\n

let should be be used for any variable expecting to be reassigned. This includes within a for loop. If you ever change value beyond initialization, use let.

\n

Block scope means that the variable will only be available within the brackets in which it is declared. This extends to internal scopes, including anonymous functions created within your scope.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ffa", + "creator": "Vivek Mehta", + "createdAt": 1513315889000, + "text": "

In EcmaScript5, there are mainly two scopes, local scope and global scope but in EcmaScript6 we have mainly three scopes, local scope, global scope and a new scope called block scope.

\n\n

Example of block scope is :-

\n\n
for ( let i = 0; i < 10; i++)\n{\n statement1...\nstatement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ffb", + "creator": "Davaakhuu Erdenekhuu", + "createdAt": 1518190632000, + "text": "

ECMAScript 6 introduced the let and const keywords. These keywords can be used in place of the var keyword. Contrary to the var keyword, the let and const keywords support the declaration of local scope inside block statements.

\n\n
var x = 10\nlet y = 10\nconst z = 10\n{\n  x = 20\n  let y = 20\n  const z = 20\n  {\n    x = 30\n    // x is in the global scope because of the 'var' keyword\n    let y = 30\n    // y is in the local scope because of the 'let' keyword\n    const z = 30\n    // z is in the local scope because of the 'const' keyword\n    console.log(x) // 30\n    console.log(y) // 30\n    console.log(z) // 30\n  }\n  console.log(x) // 30\n  console.log(y) // 20\n  console.log(z) // 20\n}\n\nconsole.log(x) // 30\nconsole.log(y) // 10\nconsole.log(z) // 10\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32975082fcc3049e92cfa", + "creator": "iAmOren", + "createdAt": 1593588182000, + "text": "const and let are not part of the question. why bring them up? personally, they don't belong in javascript...", + "upvotes": 144, + "upvoterUsernames": [], + "downvotes": 144, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90ffd", + "creator": "ludy", + "createdAt": 1624878810000, + "text": "

\r\n
\r\n
(function foo() { console.log(foo) })();\nconsole.log(typeof foo); // undefined, because `foo` is scoped to its own expression\n\n//but, like this\n(function foo() {\n    console.log('1:', foo) // function foo\n    foo = 100\n    console.log('2:', foo) // function foo, is not 100, why?\n})()
\r\n
\r\n
\r\n

\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90ffc", + "creator": "Ahmed Khashaba", + "createdAt": 1590423102000, + "text": "

I really like the accepted answer but I want to add this:

\n\n

Scope collects and maintains a look-up list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code.

\n\n

Scope is a set of rules for looking up variables by their identifier name.

\n\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 561547, + "uvac": 561569 + } + }, + { + "_id": "62f321bb082fcc3049e8feaf", + "title": "How do I redirect to another webpage?", + "title-lowercase": "how do i redirect to another webpage?", + "creator": "venkatachalam", + "createdAt": 1233579256000, + "status": "open", + "text": "

How can I redirect the user from one page to another using jQuery or pure JavaScript?

\n", + "upvotes": 8037, + "upvoterUsernames": [], + "downvotes": 330, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 7312298, + "answers": 56, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e8ff6b", + "creator": "tvanfosson", + "createdAt": 1233580686000, + "text": "

It would help if you were a little more descriptive in what you are trying to do. If you are trying to generate paged data, there are some options in how you do this. You can generate separate links for each page that you want to be able to get directly to.

\n\n
<a href='/path-to-page?page=1' class='pager-link'>1</a>\n<a href='/path-to-page?page=2' class='pager-link'>2</a>\n<span class='pager-link current-page'>3</a>\n...\n
\n\n

Note that the current page in the example is handled differently in the code and with CSS.

\n\n

If you want the paged data to be changed via AJAX, this is where jQuery would come in. What you would do is add a click handler to each of the anchor tags corresponding to a different page. This click handler would invoke some jQuery code that goes and fetches the next page via AJAX and updates the table with the new data. The example below assumes that you have a web service that returns the new page data.

\n\n
$(document).ready( function() {\n    $('a.pager-link').click( function() {\n        var page = $(this).attr('href').split(/\\?/)[1];\n        $.ajax({\n            type: 'POST',\n            url: '/path-to-service',\n            data: page,\n            success: function(content) {\n               $('#myTable').html(content);  // replace\n            }\n        });\n        return false; // to stop link\n    });\n});\n
\n", + "upvotes": 384, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff6c", + "creator": "Ryan McGeary", + "createdAt": 1233635049000, + "text": "

One does not simply redirect using jQuery

\n\n

jQuery is not necessary, and window.location.replace(...) will best simulate an HTTP redirect.

\n\n

window.location.replace(...) is better than using window.location.href, because replace() does not keep the originating page in the session history, meaning the user won't get stuck in a never-ending back-button fiasco.

\n\n

If you want to simulate someone clicking on a link, use\n location.href

\n\n

If you want to simulate an HTTP redirect, use location.replace

\n\n

For example:

\n\n
// similar behavior as an HTTP redirect\nwindow.location.replace(\"http://stackoverflow.com\");\n\n// similar behavior as clicking on a link\nwindow.location.href = \"http://stackoverflow.com\";\n
\n", + "upvotes": 18501, + "upvoterUsernames": [], + "downvotes": 2538, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32250082fcc3049e9109a", + "creator": "png", + "createdAt": 1601938281000, + "text": "Note: Similar behaviour to HTTP redirect for replace() means it won't create an entry in your browser's history.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32250082fcc3049e9109c", + "creator": "Luke T O'Brien", + "createdAt": 1608384295000, + "text": "I created this 5 years ago after seeing your answer imgflip.com/i/11ua9c", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff6e", + "creator": "Boris Guéry", + "createdAt": 1256747736000, + "text": "

WARNING: This answer has merely been provided as a possible solution; it is obviously not the best solution, as it requires jQuery. Instead, prefer the pure JavaScript solution.

\n
$(location).prop('href', 'http://stackoverflow.com')\n
\n", + "upvotes": 1888, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff6d", + "creator": "user188973", + "createdAt": 1255426597000, + "text": "
var url = 'asdf.html';\nwindow.location.href = url;\n
\n", + "upvotes": 210, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff70", + "creator": "xloadx", + "createdAt": 1315243509000, + "text": "

This works with jQuery:

\n\n
$(window).attr(\"location\", \"http://google.fr\");\n
\n", + "upvotes": 206, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff6f", + "creator": "Fred", + "createdAt": 1287791130000, + "text": "

This works for every browser:

\n\n
window.location.href = 'your_url';\n
\n", + "upvotes": 665, + "upvoterUsernames": [], + "downvotes": 303, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff71", + "creator": "ScoRpion", + "createdAt": 1337778203000, + "text": "

You can do that without jQuery as:

\n\n
window.location = \"http://yourdomain.com\";\n
\n\n

And if you want only jQuery then you can do it like:

\n\n
$jq(window).attr(\"location\",\"http://yourdomain.com\");\n
\n", + "upvotes": 296, + "upvoterUsernames": [], + "downvotes": 147, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff72", + "creator": "Mark Pieszak - Trilon.io", + "createdAt": 1343400073000, + "text": "

Standard "vanilla" JavaScript way to redirect a page

\n
window.location.href = 'newPage.html';\n
\n

Or more simply: (since window is Global)

\n
location.href = 'newPage.html';\n
\n
\n
\n

If you are here because you are losing HTTP_REFERER when redirecting, keep reading:

\n

(Otherwise ignore this last part)

\n
\n
\n

The following section is for those using HTTP_REFERER as one of many security measures (although it isn't a great protective measure). If you're using Internet Explorer 8 or lower, these variables get lost when using any form of JavaScript page redirection (location.href, etc.).

\n

Below we are going to implement an alternative for IE8 & lower so that we don't lose HTTP_REFERER. Otherwise, you can almost always simply use window.location.href.

\n

Testing against HTTP_REFERER (URL pasting, session, etc.) can help tell whether a request is legitimate.\n(Note: there are also ways to work-around / spoof these referrers, as noted by droop's link in the comments)

\n
\n

Simple cross-browser testing solution (fallback to window.location.href for Internet Explorer 9+ and all other browsers)

\n

Usage: redirect('anotherpage.aspx');

\n
function redirect (url) {\n    var ua        = navigator.userAgent.toLowerCase(),\n        isIE      = ua.indexOf('msie') !== -1,\n        version   = parseInt(ua.substr(4, 2), 10);\n\n    // Internet Explorer 8 and lower\n    if (isIE && version < 9) {\n        var link = document.createElement('a');\n        link.href = url;\n        document.body.appendChild(link);\n        link.click();\n    }\n\n    // All other browsers can use the standard window.location.href (they don't lose HTTP_REFERER like Internet Explorer 8 & lower does)\n    else { \n        window.location.href = url; \n    }\n}\n
\n", + "upvotes": 1594, + "upvoterUsernames": [], + "downvotes": 789, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff74", + "creator": "Swaprks", + "createdAt": 1352859469000, + "text": "

On your click function, just add:

\n\n
window.location.href = \"The URL where you want to redirect\";\n$('#id').click(function(){\n    window.location.href = \"http://www.google.com\";\n});\n
\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff73", + "creator": "Nadeem Yasin", + "createdAt": 1351599329000, + "text": "

But if someone wants to redirect back to home page then he may use the following snippet.

\n\n
window.location = window.location.host\n
\n\n

It would be helpful if you have three different environments as development, staging, and production.

\n\n

You can explore this window or window.location object by just putting these words in Chrome Console or Firebug's Console.

\n", + "upvotes": 307, + "upvoterUsernames": [], + "downvotes": 69, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff76", + "creator": "Anup", + "createdAt": 1370522255000, + "text": "

First write properly. You want to navigate within an application for another link from your application for another link. Here is the code:

\n\n
window.location.href = \"http://www.google.com\";\n
\n\n

And if you want to navigate pages within your application then I also have code, if you want.

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff75", + "creator": "Patartics Milán", + "createdAt": 1366884744000, + "text": "

I also think that location.replace(URL) is the best way, but if you want to notify the search engines about your redirection (they don't analyze JavaScript code to see the redirection) you should add the rel="canonical" meta tag to your website.

\n

Adding a noscript section with a HTML refresh meta tag in it, is also a good solution. I suggest you to use this JavaScript redirection tool to create redirections. It also has Internet Explorer support to pass the HTTP referrer.

\n

Sample code without delay looks like this:

\n
<!-- Place this snippet right after opening the head tag to make it work properly -->\n\n<!-- This code is licensed under GNU GPL v3 -->\n<!-- You are allowed to freely copy, distribute and use this code, but removing author credit is strictly prohibited -->\n<!-- Generated by http://insider.zone/tools/client-side-url-redirect-generator/ -->\n\n<!-- REDIRECTING STARTS -->\n<link rel="canonical" href="https://yourdomain.example/"/>\n<noscript>\n    <meta http-equiv="refresh" content="0;URL=https://yourdomain.example/">\n</noscript>\n<!--[if lt IE 9]><script type="text/javascript">var IE_fix=true;</script><![endif]-->\n<script type="text/javascript">\n    var url = "https://yourdomain.example/";\n    if(typeof IE_fix != "undefined") // IE8 and lower fix to pass the http referer\n    {\n        document.write("redirecting..."); // Don't remove this line or appendChild() will fail because it is called before document.onload to make the redirect as fast as possible. Nobody will see this text, it is only a tech fix.\n        var referLink = document.createElement("a");\n        referLink.href = url;\n        document.body.appendChild(referLink);\n        referLink.click();\n    }\n    else { window.location.replace(url); } // All other browsers\n</script>\n<!-- Credit goes to http://insider.zone/ -->\n<!-- REDIRECTING ENDS -->\n
\n", + "upvotes": 358, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32251082fcc3049e910a6", + "creator": "Jay", + "createdAt": 1628209629000, + "text": "This is no longer true re: Search Engines not analyzing Javascript", + "upvotes": 1673, + "upvoterUsernames": [], + "downvotes": 1673, + "downvoterUsernames": [] + }, + { + "_id": "62f32251082fcc3049e910a7", + "creator": "Good Night Nerd Pride", + "createdAt": 1639134092000, + "text": "This seems to be the most solid approach to me.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff77", + "creator": "Bidyut", + "createdAt": 1370880297000, + "text": "

Write the below code after the PHP, HTML or jQuery section. If in the middle of the PHP or HTML section, then use the <script> tag.

\n\n
location.href = \"http://google.com\"\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff78", + "creator": "user2496033", + "createdAt": 1372651887000, + "text": "

In JavaScript and jQuery we can use the following code to redirect the one page to another page:

\n\n
window.location.href=\"http://google.com\";\nwindow.location.replace(\"page1.html\");\n
\n", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff79", + "creator": "bren", + "createdAt": 1373564431000, + "text": "

jQuery is not needed. You can do this:

\n\n
window.open(\"URL\",\"_self\",\"\",\"\")\n
\n\n

It is that easy!

\n\n

The best way to initiate an HTTP request is with document.loacation.href.replace('URL').

\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7a", + "creator": "iConnor", + "createdAt": 1377906393000, + "text": "

So, the question is how to make a redirect page, and not how to redirect to a website?

\n

You only need to use JavaScript for this. Here is some tiny code that will create a dynamic redirect page.

\n
<script>\n    var url = window.location.search.split('url=')[1]; // Get the URL after ?url=\n    if( url ) window.location.replace(url);\n</script>\n
\n

So say you just put this snippet into a redirect/index.html file on your website you can use it like so.

\n
\n

http://www.mywebsite.com/redirect?url=http://stackoverflow.com

\n
\n

And if you go to that link it will automatically redirect you to stackoverflow.com.

\n
\n

Link to Documentation

\n
\n

And that's how you make a Simple redirect page with JavaScript

\n

Edit:

\n

There is also one thing to note. I have added window.location.replace in my code because I think it suits a redirect page, but, you must know that when using window.location.replace and you get redirected, when you press the back button in your browser it will not got back to the redirect page, and it will go back to the page before it, take a look at this little demo thing.

\n

Example:

\n
\n

The process: store home => redirect page to google => google

\n

When at google: google => back button in browser => store home

\n
\n

So, if this suits your needs then everything should be fine. If you want to include the redirect page in the browser history replace this

\n
if( url ) window.location.replace(url);\n
\n

with

\n
if( url ) window.location.href = url;\n
\n", + "upvotes": 152, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7c", + "creator": "Stefan Gruenwald", + "createdAt": 1385686185000, + "text": "

Here is a time-delay redirection. You can set the delay time to whatever you want:

\n\n
<!doctype html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Your Document Title</title>\n    <script type=\"text/javascript\">\n        function delayer(delay) {\n            onLoad = setTimeout('window.location.href = \"http://www.google.com/\"', delay);\n        }\n    </script>\n</head>\n\n<body>\n    <script>\n        delayer(8000)\n    </script>\n    <div>You will be redirected in 8 seconds!</div>\n</body>\n\n</html>\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7b", + "creator": "tilak", + "createdAt": 1384833670000, + "text": "

Try this:

\n\n
location.assign(\"http://www.google.com\");\n
\n\n

Code snippet of example.

\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7d", + "creator": "Vinay Sharma", + "createdAt": 1386670167000, + "text": "

You can use it like in the following code where getRequestToForwardPage is the request mapping (URL). You can also use your URL.

\n\n
function savePopUp(){\n    $.blockUI();\n    $.ajax({\n        url:\"GuestHouseProcessor?roomType=\"+$(\"#roomType\").val(),\n        data: $(\"#popForm\").serialize(),\n        dataType: \"json\",\n        error: (function() {\n            alert(\"Server Error\");\n            $.unblockUI();\n    }),\n    success: function(map) {\n        $(\"#layer1\").hide();\n        $.unblockUI();\n        window.location = \"getRequestToForwardPage\";\n    }\n});\n
\n\n

This is for the same context of the application.

\n\n

If you want to use only jquery specific code then following code may help:

\n\n
 $(location).attr('href',\"http://www.google.com\");\n $jq(window).attr(\"location\",\"http://www.google.com\");\n $(location).prop('href',\"http://www.google.com\"); \n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7e", + "creator": "Ashish Ratan", + "createdAt": 1390817474000, + "text": "

You need to put this line in your code:

\n\n
$(location).attr(\"href\",\"http://stackoverflow.com\");\n
\n\n

If you don't have jQuery, go with JavaScript:

\n\n
window.location.replace(\"http://stackoverflow.com\");\nwindow.location.href(\"http://stackoverflow.com\");\n
\n", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff80", + "creator": "Jaydeep Jadav", + "createdAt": 1390990832000, + "text": "
<script type=\"text/javascript\">\nvar url = \"https://yourdomain.com\";\n\n// IE8 and lower fix\nif (navigator.userAgent.match(/MSIE\\s(?!9.0)/))\n{\n    var referLink = document.createElement(\"a\");\n    referLink.href = url;\n    document.body.appendChild(referLink);\n    referLink.click();\n}\n\n// All other browsers\nelse { window.location.replace(url); }\n</script>\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff7f", + "creator": "Govind Singh", + "createdAt": 1390883306000, + "text": "

There are lots of ways of doing this.

\n\n
// window.location\nwindow.location.replace('http://www.example.com')\nwindow.location.assign('http://www.example.com')\nwindow.location.href = 'http://www.example.com'\ndocument.location.href = '/path'\n\n// window.history\nwindow.history.back()\nwindow.history.go(-1)\n\n// window.navigate; ONLY for old versions of Internet Explorer\nwindow.navigate('top.jsp')\n\n\n// Probably no bueno\nself.location = 'http://www.example.com';\ntop.location = 'http://www.example.com';\n\n// jQuery\n$(location).attr('href','http://www.example.com')\n$(window).attr('location','http://www.example.com')\n$(location).prop('href', 'http://www.example.com')\n
\n", + "upvotes": 780, + "upvoterUsernames": [], + "downvotes": 291, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff81", + "creator": "Sakthi Karthik", + "createdAt": 1391682537000, + "text": "

# HTML Page Redirect Using jQuery/JavaScript Method

\n

Try this example code:

\n
function YourJavaScriptFunction()\n{\n    var i = $('#login').val();\n    if (i == 'login')\n        window.location = "Login.php";\n    else\n        window.location = "Logout.php";\n}\n
\n

If you want to give a complete URL as window.location = "www.google.co.in";.

\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff82", + "creator": "dnxit", + "createdAt": 1391804200000, + "text": "

This is how I use it.

\n\n
   window.location.replace('yourPage.aspx');   \n   // If you're on root and redirection page is also on the root\n\n   window.location.replace(window.location.host + '/subDirectory/yourPage.aspx');\n\n   // If you're in sub directory and redirection page is also in some other sub directory.\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff83", + "creator": "Newse", + "createdAt": 1392561775000, + "text": "

Should just be able to set using window.location.

\n\n

Example:

\n\n
window.location = \"https://stackoverflow.com/\";\n
\n\n

Here is a past post on the subject: How do I redirect to another webpage?

\n", + "upvotes": 236, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff84", + "creator": "Ben", + "createdAt": 1404694498000, + "text": "

There are three main ways to do this,

\n
window.location.href='blaah.com';\nwindow.location.assign('blaah.com');\n
\n

and...

\n
window.location.replace('blaah.com');\n
\n

The last one is best, for a traditional redirect, because it will not save the page you went to before being redirected in your search history. However, if you just want to open a tab with JavaScript, you can use any of the above.1

\n

EDIT: The window prefix is optional.

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff85", + "creator": "Azam Alvi", + "createdAt": 1409935004000, + "text": "

You can redirect in jQuery like this:

\n\n
$(location).attr('href', 'http://yourPage.com/');\n
\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff86", + "creator": "Adarsh Gowda K R", + "createdAt": 1410151779000, + "text": "

Here is the code to redirect to some other page with a timeout of 10 seconds.

\n\n
<script>\n    function Redirect()\n    {\n        window.location=\"http://www.adarshkr.com\";\n    }\n\n    document.write(\"You will be redirected to a new page in 10 seconds.\");\n    setTimeout('Redirect()', 10000);\n</script>\n
\n\n

You can also do it like this, on click of a button using location.assign:

\n\n
<input type=\"button\" value=\"Load new document\" onclick=\"newPage()\">\n<script>\n    function newPage() {\n        window.location.assign(\"http://www.adarshkr.com\")\n    }\n</script>\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32253082fcc3049e910b9", + "creator": "Patrick W. McMahon", + "createdAt": 1418921669000, + "text": "don't use inline click events. Bad practice and is not going to be supported in future iterations of all web browsers.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff88", + "creator": "devst3r", + "createdAt": 1411998190000, + "text": "

Redirecting User using jQuery/JavaScript

\n\n

By using the location object in jQuery or JavaScript we can redirect the user to another web page.

\n\n

In jQuery

\n\n

The code to redirect the user from one page to another is:

\n\n
var url = 'http://www.example.com';\n$(location).attr('href', url);\n
\n\n

In JavaScript

\n\n

The code to redirect the user from one page to another is:

\n\n
var url = 'http://www.example.com';\nwindow.location.href = url;\n
\n\n

Or

\n\n
var url = 'http://www.example.com';\nwindow.location = url;\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff87", + "creator": "stratum", + "createdAt": 1410450188000, + "text": "

Use the jQuery function:

\n\n
$.extend({\n  redirectPost: function(location, args) {\n    var form = '';\n    $.each(args, function(key, value) {\n      form += '<input type=\"hidden\" name=\"' + key + '\" value=\"' + value + '\">';\n    });\n    $('<form action=\"' + location + '\" method=\"POST\">' + form + '</form>').appendTo($(document.body)).submit();\n  }\n});\n
\n\n

In your code you use it like this:

\n\n
$.redirectPost(\"addPhotos.php\", {pimreference:  $(\"#pimreference\").val(), tag: $(\"#tag\").val()});\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8a", + "creator": "Epiphany", + "createdAt": 1425138164000, + "text": "

I just had to update this ridiculousness with yet another newer jQuery method:

\n\n
var url = 'http://www.fiftywaystoleaveyourlocation.com';\n$(location).prop('href', url);\n
\n", + "upvotes": 69, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff89", + "creator": "SergeDirect", + "createdAt": 1419620901000, + "text": "

Original question: \"How to redirect using jQuery?\", hence the answer implements jQuery >> Complimentary usage case.

\n\n
\n\n

To just redirect to a page with JavaScript:

\n\n
window.location.href = \"/contact/\";\n
\n\n

Or if you need a delay:

\n\n
setTimeout(function () {\n  window.location.href = \"/contact/\";\n}, 2000);   // Time in milliseconds\n
\n\n

jQuery allows you to select elements from a web page with ease. You can find anything you want on a page and then use jQuery to add special effects, react to user actions, or show and hide content inside or outside the element you have selected. All these tasks start with knowing how to select an element or an event.

\n\n
$('a,img').on('click',function(e){\n  e.preventDefault();\n  $(this).animate({\n    opacity: 0 //Put some CSS animation here\n  }, 500);\n  setTimeout(function(){\n    // OK, finished jQuery staff, let's go redirect\n    window.location.href = \"/contact/\";\n  },500);\n});\n
\n\n

Imagine someone wrote a script/plugin with 10000 lines of code. With jQuery you can connect to this code with just a line or two.

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8c", + "creator": "vipul sorathiya", + "createdAt": 1434456113000, + "text": "

Simply in JavaScript, you can redirect to a specific page by using the following:

\n\n
window.location.replace(\"http://www.test.com\");\n
\n\n

Or

\n\n
location.replace(\"http://www.test.com\");\n
\n\n

Or

\n\n
window.location.href = \"http://www.test.com\";\n
\n\n

Using jQuery:

\n\n
$(window).attr(\"location\",\"http://www.test.com\");\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8b", + "creator": "lalithkumar", + "createdAt": 1432271891000, + "text": "

Javascript:

\n\n
window.location.href='www.your_url.com';\nwindow.top.location.href='www.your_url.com';\nwindow.location.replace('www.your_url.com');\n
\n\n

Jquery:

\n\n
var url='www.your_url.com';\n$(location).attr('href',url);\n$(location).prop('href',url);//instead of location you can use window\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8e", + "creator": "Divyesh Kanzariya", + "createdAt": 1457002215000, + "text": "

jQuery code to redirect a page or URL

\n
\n

First Way

\n
\n

Here is the jQuery code for redirecting a page. Since, I have put this code on the $(document).ready() function, it will execute as soon as the page is loaded.

\n

\r\n
\r\n
var url = \"http://stackoverflow.com\";\n$(location).attr('href',url);
\r\n
\r\n
\r\n

\n

You can even pass a URL directly to the attr() method, instead of using a variable.

\n
\n

Second Way

\n
\n
 window.location.href="http://stackoverflow.com";\n
\n

You can also code like this (both are same internally):

\n
window.location="http://stackoverflow.com";\n
\n

If you are curious about the difference between window.location and window.location.href, then you can see that the latter one is setting href property explicitly, while the former one does it implicitly. Since window.location returns an object, which by default sets its .href property.

\n
\n

Third Way

\n
\n

There is another way to redirect a page using JavaScript, the replace() method of window.location object. You can pass a new URL to the replace() method, and it will simulate an HTTP redirect. By the way, remember that window.location.replace() method doesn't put the originating page in the session history, which may affect behavior of the back button. Sometime, it's what you want, so use it carefully.

\n

\r\n
\r\n
// Doesn't put originating page in history\nwindow.location.replace(\"http://stackoverflow.com\");
\r\n
\r\n
\r\n

\n
\n

Fourth Way

\n
\n

like attr() method (after jQuery 1.6 introduce)

\n

\r\n
\r\n
var url = \"http://stackoverflow.com\";\n$(location).prop('href', url);
\r\n
\r\n
\r\n

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8d", + "creator": "Muhammad Waqas", + "createdAt": 1449247304000, + "text": "

In JavaScript and jQuery we use this following code to redirect the page:

\n\n
window.location.href=\"http://google.com\";\nwindow.location.replace(\"page1.html\");\n
\n\n

But you can make a function in jQuery to redirect the page:

\n\n
jQuery.fn.redirect=function(url)\n{\n    window.location.href=url;\n}\n
\n\n

And call this function:

\n\n
jQuery(window).redirect(\"http://stackoverflow.com/\")\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32254082fcc3049e910c3", + "creator": "A1rPun", + "createdAt": 1449255317000, + "text": "Why are you passing window to the jQuery function in this line: jQuery(window).redirect(...)?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32254082fcc3049e910c5", + "creator": "Muhammad Waqas", + "createdAt": 1449267615000, + "text": "@A1rPun what i need to pass window ord document ?", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff90", + "creator": "1j01", + "createdAt": 1460564605000, + "text": "

ECMAScript 6 + jQuery, 85 bytes

\n\n
$({jQueryCode:(url)=>location.replace(url)}).attr(\"jQueryCode\")(\"http://example.com\")\n
\n\n

Please don't kill me, this is a joke. It's a joke. This is a joke.

\n\n

This did \"provide an answer to the question\", in the sense that it asked for a solution \"using jQuery\" which in this case entails forcing it into the equation somehow.

\n\n

Ferrybig apparently needs the joke explained (still joking, I'm sure there are limited options on the review form), so without further ado:

\n\n

Other answers are using jQuery's attr() on the location or window objects unnecessarily.

\n\n

This answer also abuses it, but in a more ridiculous way. Instead of using it to set the location, this uses attr() to retrieve a function that sets the location.

\n\n

The function is named jQueryCode even though there's nothing jQuery about it, and calling a function somethingCode is just horrible, especially when the something is not even a language.

\n\n

The \"85 bytes\" is a reference to Code Golf. Golfing is obviously not something you should do outside of code golf, and furthermore this answer is clearly not actually golfed.

\n\n

Basically, cringe.

\n", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff8f", + "creator": "Zigri2612", + "createdAt": 1457076174000, + "text": "

All way to make a redirect from the client side:

\n\n

\r\n
\r\n
<!DOCTYPE html>\r\n<html>\r\n    <head>\r\n        <title>JavaScript and jQuery example to redirect a page or URL </title>\r\n    </head>\r\n    <body>\r\n        <div id=\"redirect\">\r\n            <h2>Redirecting to another page</h2>\r\n        </div>\r\n\r\n        <script src=\"scripts/jquery-1.6.2.min.js\"></script>\r\n        <script>\r\n            // JavaScript code to redirect a URL\r\n            window.location.replace(\"http://stackoverflow.com\");\r\n            // window.location.replace('http://code.shouttoday.com');\r\n\r\n            // Another way to redirect page using JavaScript\r\n\r\n            // window.location.assign('http://code.shouttoday.com');\r\n            // window.location.href = 'http://code.shouttoday.com';\r\n            // document.location.href = '/relativePath';\r\n\r\n            //jQuery code to redirect a page or URL\r\n            $(document).ready(function(){\r\n                //var url = \"http://code.shouttoday.com\";\r\n                //$(location).attr('href',url);\r\n                // $(window).attr('location',url)\r\n                //$(location).prop('href', url)\r\n            });\r\n        </script>\r\n    </body>\r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff91", + "creator": "user1012506", + "createdAt": 1466094418000, + "text": "

I already use the function redirect() of JavaScript. It's working.

\n\n
<script type=\"text/javascript\">\n    $(function () {\n        //It's similar to HTTP redirect\n        window.location.replace(\"http://www.Technomark.in\");\n\n        //It's similar to clicking on a link\n        window.location.href = \"Http://www.Technomark.in\";\n    })\n</script>\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32254082fcc3049e910c9", + "creator": "Mimouni", + "createdAt": 1466167624000, + "text": "what is the difference between this answer and the first ?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff92", + "creator": "madhur", + "createdAt": 1472452699000, + "text": "

You can use:

\n\n
window.location.replace(\"http://www.example.com/\");\n
\n\n

The replace() method does not save the originating page in the session history, so the user can't go back using the back button and again get redirected. NOTE: The browser back button will be deactivated in this case.

\n\n

However, if you want an effect the same as clicking on a link you should go for:

\n\n
window.location.href = \"http://www.example.com/\";\n
\n\n

In this case, the browser back button will be active.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff93", + "creator": "Abhishek K. Upadhyay", + "createdAt": 1475237708000, + "text": "

This is very easy to implement. You can use:

\n\n
window.location.href = \"http://www.example.com/\";\n
\n\n

This will remember the history of the previous page. So one can go back by clicking on the browser's back button.

\n\n

Or:

\n\n
window.location.replace(\"http://www.example.com/\");\n
\n\n

This method does not remember the history of the previous page. The back button becomes disabled in this case.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff96", + "creator": "Taufiq Rahman", + "createdAt": 1481625812000, + "text": "

In jQuery, use $(location).attr('href', url):

\n\n

\r\n
\r\n
$(document).ready(function(){\r\n    var url = \"https://www.youtube.com/watch?v=JwMKRevYa_M\";\r\n    $(location).attr('href', url); // Using this\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n\n

In raw JavaScript, there are a number of ways to achieve that:

\n\n
window.location.href=\"https://www.youtube.com/watch?v=JwMKRevYa_M\";\n
\n\n

- sets href property explicitly.

\n\n
window.location = \"http://www.GameOfThrones.com\";\n
\n\n

- does it implicitly Since window.location returns an object, which by default sets its .href property.

\n\n
window.location.replace(\"http://www.stackoverflow.com\");\n
\n\n

- replaces the location of the current window with the new one.

\n\n
self.location = \"http://www.somewebsite.com\";\n
\n\n

- sets the location of the current window itself.

\n\n

Here is an example of JavaScript redirecting after a certain time (3 seconds):

\n\n

\r\n
\r\n
<script>\r\n    setTimeout(function() {\r\n        window.location.href = \"https://www.youtube.com/\";\r\n    }, 3000);\r\n</script>
\r\n
\r\n
\r\n

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff94", + "creator": "SantoshK", + "createdAt": 1477301187000, + "text": "

You can redirect the page by using the below methods:

\n\n
    \n
  1. By using a meta tag in the head - <meta http-equiv=\"refresh\" content=\"0;url=http://your-page-url.com\" />. Note that content=\"0;... is used for after how many seconds you need to redirect the page

  2. \n
  3. By using JavaScript: window.location.href = \"http://your-page-url.com\";

  4. \n
  5. By using jQuery: $(location).attr('href', 'http://yourPage.com/');

  6. \n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32254082fcc3049e910cf", + "creator": "1j01", + "createdAt": 1489080620000, + "text": "There is no reason to use jQuery for this and window. is unnecessary.", + "upvotes": 2392, + "upvoterUsernames": [], + "downvotes": 2392, + "downvoterUsernames": [] + }, + { + "_id": "62f32254082fcc3049e910d0", + "creator": "SantoshK", + "createdAt": 1490094108000, + "text": "Yes location.href = would be better option to redirect the page -:)", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff95", + "creator": "cascading-style", + "createdAt": 1479919329000, + "text": "

Use:

\n\n
function redirect(a) {\n    location = a\n}\n
\n\n

And call it with: redirect([url]);

\n\n

There's no need for the href after location, as it is implied.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff98", + "creator": "Kalpesh Panchal", + "createdAt": 1487890888000, + "text": "

Using JavaScript:

\n\n

Method 1:

\n\n
window.location.href=\"http://google.com\";\n
\n\n

Method 2:

\n\n
window.location.replace(\"http://google.com\");\n
\n\n
\n\n

Using jQuery:

\n\n

Method 1: $(location)

\n\n
$(location).attr('href', 'http://google.com');\n
\n\n

Method 2: Reusable Function

\n\n
jQuery.fn.redirectTo = function(url){\n    window.location.href = url;\n}\n\njQuery(window).redirectTo(\"http://google.com\");\n
\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff97", + "creator": "sneha", + "createdAt": 1486622043000, + "text": "
<script type=\"text/javascript\">\n    if(window.location.href === \"http://stackoverflow.com\") {      \n         window.location.replace(\"https://www.google.co.in/\");\n       }\n</script>\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff99", + "creator": "Alireza", + "createdAt": 1488116209000, + "text": "

Basically jQuery is just a JavaScript framework and for doing some of the things like redirection in this case, you can just use pure JavaScript, so in that case you have 3 options using vanilla JavaScript:

\n\n

1) Using location replace, this will replace the current history of the page, means that it is not possible to use the back button to go back to the original page.

\n\n
window.location.replace(\"http://stackoverflow.com\");\n
\n\n

2) Using location assign, this will keep the history for you and with using back button, you can go back to the original page:

\n\n
window.location.assign(\"http://stackoverflow.com\");\n
\n\n

3) I recommend using one of those previous ways, but this could be the third option using pure JavaScript:

\n\n
window.location.href=\"http://stackoverflow.com\";\n
\n\n

You can also write a function in jQuery to handle it, but not recommended as it's only one line pure JavaScript function, also you can use all of above functions without window if you are already in the window scope, for example window.location.replace(\"http://stackoverflow.com\"); could be location.replace(\"http://stackoverflow.com\");

\n\n

Also I show them all on the image below:

\n\n

\"location.replace

\n", + "upvotes": 294, + "upvoterUsernames": [], + "downvotes": 82, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9a", + "creator": "Maniruzzaman Akash", + "createdAt": 1495826635000, + "text": "

I just add another way:

\n\n

To redirect for any specific page/links of your site to another page, just add this line of code:

\n\n
<script>\n    if(window.location.href == 'old_url')\n    {\n        window.location.href=\"new_url\";\n    }\n\n    // Another URL redirect\n    if(window.location.href == 'old_url2')\n    {\n        window.location.href=\"new_url2\";\n    }\n</script>\n
\n\n

For a real life example,

\n\n
<script>\n    if(window.location.href == 'https://old-site.com')\n    {\n        window.location.href=\"https://new-site.com\";\n    }\n\n    // Another URL redirect\n    if(window.location.href == 'https://old-site.com/simple-post.html')\n    {\n        window.location.href=\"https://new-site.com/simple-post.html\";\n    }\n</script>\n
\n\n

By using this simple code, you can redirect full site or any single page.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9b", + "creator": "Mohideen bin Mohammed", + "createdAt": 1498556171000, + "text": "
    \n
  1. location.assign():

    \n\n

    To assign a route path by passing a path into it.. Assign will give you a history even after the path was assigned.

    \n\n

    Usage Method: value should be passed into it.

    \n\n

    For example: location.assign(\"http://google.com\")

    \n\n

    \"Enter

  2. \n
  3. location.href

    \n\n

    Can define give a path into it... And it will redirect into a given path once it setup, and it will keep history...

    \n\n

    Usage Method: value should be assign into it.

    \n\n

    For example: location.href = \"http://google.com\"

  4. \n
  5. location.replace():

    \n\n

    It will help to replace a path if you don't want to keep history. It won't give you a history once you replace its path.

    \n\n

    Usage Method: value should be pass into it.

    \n\n

    For example: location.replace(\"http://google.com\")

    \n\n

    \"Enter

  6. \n
\n\n
\n\n

assign() and href are similar, and both can hold history. assign will work by passing a value and href works by assigning.

\n\n

You can achieve it using JavaScript itself without using jQuery by assigning,

\n\n
window.location = \"http://google.com\"\nlocation.href = \"http://google.com\"\n
\n\n

You can achieve similar thing using jQuery like below. It will do exactly the same like above,

\n\n
$(window).attr('location', \"http://www.google.com\");\n$(location).attr('href', \"http://www.google.com\");\n
\n\n

You can easily understand the difference between both...

\n\n

Here is the Location object,

\n\n

\"Location

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9c", + "creator": "RuNpiXelruN", + "createdAt": 1500462062000, + "text": "

If you want to redirect to a route within the same app simply

\n\n
window.location.pathname = '/examplepath'\n
\n\n

would be the way to go.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9e", + "creator": "omkaartg", + "createdAt": 1512684495000, + "text": "

In my work experience, JavaScript is much better to redirect.

\n\n

It depends on how you want to change the location. If you want to log your website in users history, use window.location.href='ur website';. Otherwise to do it without logging in history, use window.location.replace(\"your website\");.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9d", + "creator": "HD..", + "createdAt": 1503900098000, + "text": "

Single Page Application, within the same application route

\n\n
window.location.pathname = '/stack';\n
\n\n

JavaScript:

\n\n
location.href = \"http://stack.com\";\nwindow.location = \"http://stack.com\";\n
\n\n

jQuery:

\n\n
$(location).attr('href', \"http://www.stack.com\");\n$(window).attr('location', \"http://www.stack.com\");\n
\n\n

Angular 4

\n\n
import { Router } from '@angular/router';\nexport class NavtabComponent{\n    constructor(private router: Router) {\n    }\n    this.router.navigate(['bookings/taxi']);\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa0", + "creator": "wild coder", + "createdAt": 1518604550000, + "text": "

You can write the Url.Action for the Button click event in the script section as follows.

\n\n
function onclick() {\n    location.href = '@Url.Action(\"Index\", \"Home\")';\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff9f", + "creator": "Andrei Todorut", + "createdAt": 1513976458000, + "text": "

Using location.replace() will redirect you, but without saving the history of the previous page. This is better to use when a form is submitted.

\n\n

But when you want to keep your history you have to use location.href=//path.

\n\n

Examples:

\n\n
// Form with steps\ndocument.getElementById('#next').onclick = function() {\n   window.location.href='/step2' // Iteration of steps;\n}\n\n// Go to next step\ndocument.getElementById('#back').onclick = function() {\n   window.history.back();\n}\n\n// Finish\ndocument.getElementById('#finish').onclick = function() {\n   window.location.href = '/success';\n}\n\n// On success page\nwindow.onload = function() {\n    setTimeout(function() {\n       window.location.replace('/home'); // I can't go back to success page by pressing the back button\n    },3000);\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa1", + "creator": "Alessandro Foolish Ciak DAnton", + "createdAt": 1518899502000, + "text": "

If you prefer to use pure JavaScript I realized that using of document.location.href = \"https://example.com\" or window.location.href = \"https://example.com\" causes compatibility issues in Firefox. Instead try to use:

\n\n
location.href = \"https://example.com\";\nlocation.replace(\"http://example.com\");\n
\n\n\n\n

In my case has solved the problem. Good luck!

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa2", + "creator": "Mustkeem K", + "createdAt": 1522410211000, + "text": "

JavaScript is very extensive. If you want to jump to another page you have three options.

\n
 window.location.href='otherpage.com';\n window.location.assign('otherpage.com');\n //and...\n\n window.location.replace('otherpage.com');\n
\n

As you want to move to another page, you can use any from these if this is your requirement.\nHowever all three options are limited to different situations. Chose wisely according to your requirement.

\n

If you are interested in more knowledge about the concept, you can go through further.

\n
window.location.href; // Returns the href (URL) of the current page\nwindow.location.hostname; // Returns the domain name of the web host\nwindow.location.pathname; // Returns the path and filename of the current page\nwindow.location.protocol; // Returns the web protocol used (http: or https:)\nwindow.location.assign; // Loads a new document\nwindow.location.replace; // RReplace the current location with new one.\n
\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 7320335, + "uvac": 7320391 + } + }, + { + "_id": "62f32ab6082fcc3049e931fb", + "title": "How does JavaScript .prototype work?", + "title-lowercase": "how does javascript .prototype work?", + "creator": "John Leidegren", + "createdAt": 1235219478000, + "status": "open", + "text": "

I'm not that into dynamic programming languages but I've written my fair share of JavaScript code. I never really got my head around this prototype-based programming, does any one know how this works?

\n
var obj = new Object();\nobj.prototype.test = function() { alert('Hello?'); };\nvar obj2 = new obj();\nobj2.test();\n
\n

I remember a lot discussion I had with people a while back (I'm not exactly sure what I'm doing) but as I understand it, there's no concept of a class. It's just an object, and instances of those objects are clones of the original, right?

\n

But what is the exact purpose of this ".prototype" property in JavaScript? How does it relate to instantiating objects?

\n

Update: correct way

\n
var obj = new Object(); // not a functional object\nobj.prototype.test = function() { alert('Hello?'); }; // this is wrong!\n\nfunction MyObject() {} // a first class functional object\nMyObject.prototype.test = function() { alert('OK'); } // OK\n
\n

Also these slides really helped a lot.

\n", + "upvotes": 3914, + "upvoterUsernames": [], + "downvotes": 1764, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 506906, + "answers": 18, + "answerItems": [ + { + "_id": "62f32ab7082fcc3049e93203", + "creator": "Tom", + "createdAt": 1265395330000, + "text": "

When a constructor creates an object, that object implicitly references the constructor’s “prototype” property for the purpose of resolving property references. The constructor’s “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93202", + "creator": "Georg Schölly", + "createdAt": 1235220091000, + "text": "

Javascript doesn't have inheritance in the usual sense, but it has the prototype chain.

\n\n

prototype chain

\n\n

If a member of an object can't be found in the object it looks for it in the prototype chain. The chain consists of other objects. The prototype of a given instance can be accessed with the __proto__ variable. Every object has one, as there is no difference between classes and instances in javascript.

\n\n

The advantage of adding a function / variable to the prototype is that it has to be in the memory only once, not for every instance.

\n\n

It's also useful for inheritance, because the prototype chain can consist of many other objects.

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32ab8082fcc3049e93212", + "creator": "some", + "createdAt": 1235220244000, + "text": "FF and Chrome supports proto, but not IE nor Opera.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32ab7082fcc3049e93200", + "creator": "Ramesh", + "createdAt": 1235219830000, + "text": "

prototype allows you to make classes. if you do not use prototype then it becomes a static.

\n\n

Here is a short example.

\n\n
var obj = new Object();\nobj.test = function() { alert('Hello?'); };\n
\n\n

In the above case, you have static funcation call test. This function can be accessed only by obj.test where you can imagine obj to be a class.

\n\n

where as in the below code

\n\n
function obj()\n{\n}\n\nobj.prototype.test = function() { alert('Hello?'); };\nvar obj2 = new obj();\nobj2.test();\n
\n\n

The obj has become a class which can now be instantiated. Multiple instances of obj can exist and they all have the test function.

\n\n

The above is my understanding. I am making it a community wiki, so people can correct me if I am wrong.

\n", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93201", + "creator": "dirkgently", + "createdAt": 1235219858000, + "text": "
\n

what is the exact purpose of this \".prototype\" property?

\n
\n\n

The interface to standard classes become extensible. For example, you are using the Array class and you also need to add a custom serializer for all your array objects. Would you spend time coding up a subclass, or use composition or ... The prototype property solves this by letting the users control the exact set of members/methods available to a class.

\n\n

Think of prototypes as an extra vtable-pointer. When some members are missing from the original class, the prototype is looked up at runtime.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93204", + "creator": "rockXrock", + "createdAt": 1352281691000, + "text": "

After reading this thread, I feel confused with JavaScript Prototype Chain, then I found these charts

\n\n

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance\n\"*[[protytype]]*

\n\n

it's a clear chart to show JavaScript Inheritance by Prototype Chain

\n\n

and

\n\n

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

\n\n

this one contains a example with code and several nice diagrams.

\n\n
\n

prototype chain ultimately falls back to Object.prototype.

\n \n

prototype chain can be technically extended as long as you want, each time by setting the prototype of the subclass equal to an object of the parent class.

\n
\n\n

Hope it's also helpful for you to understand JavaScript Prototype Chain.

\n", + "upvotes": 125, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93205", + "creator": "sam", + "createdAt": 1371584888000, + "text": "

Every object has an internal property, [[Prototype]], linking it to another object:

\n\n
object [[Prototype]] → anotherObject\n
\n\n

In traditional javascript, the linked object is the prototype property of a function:

\n\n
object [[Prototype]] → aFunction.prototype\n
\n\n

Some environments expose [[Prototype]] as __proto__:

\n\n
anObject.__proto__ === anotherObject\n
\n\n

You create the [[Prototype]] link when creating an object.

\n\n
// (1) Object.create:\nvar object = Object.create(anotherObject)\n// object.__proto__ = anotherObject\n\n// (2) ES6 object initializer:\nvar object = { __proto__: anotherObject };\n// object.__proto__ = anotherObject\n\n// (3) Traditional JavaScript:\nvar object = new aFunction;\n// object.__proto__ = aFunction.prototype\n
\n\n

So these statements are equivalent:

\n\n
var object = Object.create(Object.prototype);\nvar object = { __proto__: Object.prototype }; // ES6 only\nvar object = new Object;\n
\n\n

You can't actually see the link target (Object.prototype) in a new statement; instead the target is implied by the constructor (Object).

\n\n

Remember:

\n\n\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32ab8082fcc3049e93216", + "creator": "Palec", + "createdAt": 1437942099000, + "text": "Revision 5 removed some useful info, including info on Object.create(). See revision 4.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab8082fcc3049e93218", + "creator": "sam", + "createdAt": 1438023015000, + "text": "@Palec what should I add back?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32ab7082fcc3049e93207", + "creator": "Aravind", + "createdAt": 1415548058000, + "text": "

Let me tell you my understanding of prototypes. I am not going to compare the inheritance here with other languages. I wish people would stop comparing languages, and just understand the language as itself. Understanding prototypes and prototypal inheritance is so simple, as I will show you below.

\n\n

Prototype is like a model, based on which you create a product. The crucial point to understand is that when you create an object using another object as it's prototype, the link between the prototype and the product is ever-lasting. For instance:

\n\n
var model = {x:2};\nvar product = Object.create(model);\nmodel.y = 5;\nproduct.y\n=>5\n
\n\n

Every object contains an internal property called the [[prototype]], which can be accessed by the Object.getPrototypeOf() function. Object.create(model) creates a new object and sets it's [[prototype]] property to the object model. Hence when you do Object.getPrototypeOf(product), you will get the object model.

\n\n

Properties in the product are handled in the following way:

\n\n\n\n

Such a linking of objects using the prototype property is called prototypal inheritance. There, it is so simple, agree?

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32ab8082fcc3049e93219", + "creator": "Aravind", + "createdAt": 1415594261000, + "text": "HMR: Thank for your comment, it made me think and test my understanding.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32ab7082fcc3049e93206", + "creator": "B M", + "createdAt": 1382309177000, + "text": "

I found it helpful to explain the \"prototype chain\" as recursive convention when obj_n.prop_X is being referenced:

\n\n

if obj_n.prop_X doesn't exist, check obj_n+1.prop_X where obj_n+1 = obj_n.[[prototype]]

\n\n

If the prop_X is finally found in the k-th prototype object then

\n\n

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

\n\n

You can find a graph of the relation of Javascript objects by their properties here:

\n\n

\"js

\n\n

http://jsobjects.org

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93208", + "creator": "rus1", + "createdAt": 1445721700000, + "text": "

Another attempt to explain JavaScript prototype-based inheritance with better pictures

\n\n

\"Simple

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93209", + "creator": "Bad", + "createdAt": 1455808629000, + "text": "

The Definitive Guide to Object-Oriented JavaScript - a very concise and clear ~30min video explanation of the asked question (Prototypal Inheritance topic begins from 5:45, although I'd rather listen to the whole video). The author of this video also made JavaScript object visualizer website http://www.objectplayground.com/.\"enter\n\"enter

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32ab8082fcc3049e9321d", + "creator": "lukeaus", + "createdAt": 1470819349000, + "text": "great video reference", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32ab7082fcc3049e9320a", + "creator": "John Slegers", + "createdAt": 1457724276000, + "text": "

Consider the following keyValueStore object :

\n\n
var keyValueStore = (function() {\n    var count = 0;\n    var kvs = function() {\n        count++;\n        this.data = {};\n        this.get = function(key) { return this.data[key]; };\n        this.set = function(key, value) { this.data[key] = value; };\n        this.delete = function(key) { delete this.data[key]; };\n        this.getLength = function() {\n            var l = 0;\n            for (p in this.data) l++;\n            return l;\n        }\n    };\n\n    return  { // Singleton public properties\n        'create' : function() { return new kvs(); },\n        'count' : function() { return count; }\n    };\n})();\n
\n\n

I can create a new instance of this object by doing this :

\n\n
kvs = keyValueStore.create();\n
\n\n

Each instance of this object would have the following public properties :

\n\n\n\n

Now, suppose we create 100 instances of this keyValueStore object. Even though get, set, delete, getLength will do the exact same thing for each of these 100 instances, every instance has its own copy of this function.

\n\n

Now, imagine if you could have just a single get, set, delete and getLength copy, and each instance would reference that same function. This would be better for performance and require less memory.

\n\n

That's where prototypes come in. A prototype is a \"blueprint\" of properties that is inherited but not copied by instances. So this means that it exists only once in memory for all instances of an object and is shared by all of those instances.

\n\n

Now, consider the keyValueStore object again. I could rewrite it like this :

\n\n
var keyValueStore = (function() {\n    var count = 0;\n    var kvs = function() {\n        count++;\n        this.data = {};\n    };\n\n    kvs.prototype = {\n        'get' : function(key) { return this.data[key]; },\n        'set' : function(key, value) { this.data[key] = value; },\n        'delete' : function(key) { delete this.data[key]; },\n        'getLength' : function() {\n            var l = 0;\n            for (p in this.data) l++;\n            return l;\n        }\n    };\n\n    return  {\n        'create' : function() { return new kvs(); },\n        'count' : function() { return count; }\n    };\n})();\n
\n\n

This does EXACTLY the same as the previous version of the keyValueStore object, except that all of its methods are now put in a prototype. What this means, is that all of the 100 instances now share these four methods instead of each having their own copy.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e9320b", + "creator": "Louis Moore", + "createdAt": 1495794247000, + "text": "

I always like analogies when it comes to understand this type of stuff. 'Prototypical inheritance' is pretty confusing in comparison to class bass inheritance in my opinion, even though prototypes are much simpler paradigm. In fact with prototypes, there really is no inheritance, so the name in and of itself misleading, it's more a type of 'delegation'.

\n\n

Imagine this ....

\n\n

You're in high-school, and you're in class and have a quiz that's due today, but you don't have a pen to fill out your answers. Doh!

\n\n

You're sitting next to your friend Finnius, who might have a pen. You ask, and he looks around his desk unsuccessfully, but instead of saying \"I don't have a pen\", he's a nice friend he checks with his other friend Derp if he has a pen. Derp does indeed have a spare pen and passes it back to Finnius, who passes it over to you to complete your quiz. Derp has entrusted the pen to Finnius, who has delegated the pen to you for use.

\n\n

What is important here is that Derp does not give the pen to you, as you don't have a direct relationship with him.

\n\n

This, is a simplified example of how prototypes work, where a tree of data is searched for the thing you're looking for.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e9320d", + "creator": "IvanM", + "createdAt": 1533190563000, + "text": "

another scheme showing __proto__, prototype and constructor relations:\n\"enter

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e9320c", + "creator": "shiva kumar", + "createdAt": 1517477586000, + "text": "

It's just that you already have an object with Object.new but you still don't have an object when using the constructor syntax.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e9320e", + "creator": "Willem van der Veen", + "createdAt": 1534587860000, + "text": "

Summary:

\n\n\n\n

Example:

\n\n

\r\n
\r\n
function Person (name) {\r\n  this.name = name;\r\n}\r\n\r\nlet me = new Person('willem');\r\n\r\nconsole.log(Person.prototype) // Person has a prototype property\r\n\r\nconsole.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.
\r\n
\r\n
\r\n

\n\n

Why is this usefull:

\n\n

Javascript has a mechanism when looking up properties on Objects which is called 'prototypal inheritance', here is what is basically does:

\n\n\n\n

For example:

\n\n

\r\n
\r\n
function Person(name) {\r\n  this.name = name;\r\n}\r\n\r\nlet mySelf = new Person('Willem');\r\n\r\nconsole.log(mySelf.__proto__ === Person.prototype);\r\n\r\nconsole.log(mySelf.__proto__.__proto__ === Object.prototype);
\r\n
\r\n
\r\n

\n\n

Update:

\n\n

The __proto__ property has been deprecated, although it is implemented in most modern browsers a better way to obtain the prototype object reference would be:

\n\n

Object.getPrototypeOf()

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e9320f", + "creator": "Arif", + "createdAt": 1545722652000, + "text": "

The Prototype creates new object by cloning existing object. So really when we think about prototype we can really think cloning or making a copy of something instead of making it up.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93210", + "creator": "Baraa Al-Tabbaa", + "createdAt": 1548060321000, + "text": "
\n

It's important to understand that there is a distinction between an object's prototype (which is available via Object.getPrototypeOf(obj), or via the deprecated __proto__ property) and the prototype property on constructor functions. The former is the property on each instance, and the latter is the property on the constructor. That is, Object.getPrototypeOf(new Foobar()) refers to the same object as Foobar.prototype.

\n
\n\n

Reference: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32ab7082fcc3049e93211", + "creator": "kabirbaidhya", + "createdAt": 1600877793000, + "text": "

If you want to understand the concept of prototype and prototype based inheritance from the basics, check the official MDN docs, they explain it pretty well.

\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

\n
\n

When it comes to inheritance, JavaScript only has one construct:\nobjects. Each object has a private property which holds a link to\nanother object called its prototype. That prototype object has a\nprototype of its own, and so on until an object is reached with null\nas its prototype. By definition, null has no prototype, and acts as\nthe final link in this prototype chain.

\n
\n

Also, here's another good resource that explains using simple examples - https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

\n", + "upvotes": 117, + "upvoterUsernames": [], + "downvotes": 117, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f32ab7082fcc3049e931fc", + "creator": "Anshul", + "createdAt": 1361288420000, + "text": "Do we really need a functional object for applying prototype? if yes than why ?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab7082fcc3049e931fd", + "creator": "Naor", + "createdAt": 1365593730000, + "text": "This might help you: webdeveasy.com/javascript-prototype", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab7082fcc3049e931fe", + "creator": "Mathieu Larose", + "createdAt": 1406635109000, + "text": "An introduction to prototype in JavaScript", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32ab7082fcc3049e931ff", + "creator": "Codebeat", + "createdAt": 1483059547000, + "text": "Don't forget a semicollon ; after a declaration ;-)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 510824, + "uvac": 510842 + } + }, + { + "_id": "62f2513da5cd2c51555ff2db", + "title": "How to insert an item into an array at a specific index (JavaScript)", + "title-lowercase": "how to insert an item into an array at a specific index (javascript)", + "creator": "tags2k", + "createdAt": 1235572170000, + "status": "open", + "text": "

I am looking for a JavaScript array insert method, in the style of:

\n
arr.insert(index, item)\n
\n

Preferably in jQuery, but any JavaScript implementation will do at this point.

\n", + "upvotes": 6056, + "upvoterUsernames": [], + "downvotes": 2181, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2947686, + "answers": 26, + "answerItems": [ + { + "_id": "62f25140a5cd2c51555ff35f", + "creator": "Luis Perez", + "createdAt": 1346301433000, + "text": "

If you want to insert multiple elements into an array at once check out this Stack Overflow answer: A better way to splice an array into an array in javascript

\n

Also here are some functions to illustrate both examples:

\n
function insertAt(array, index) {\n    var arrayToInsert = Array.prototype.splice.apply(arguments, [2]);\n    return insertArrayAt(array, index, arrayToInsert);\n}\n\nfunction insertArrayAt(array, index, arrayToInsert) {\n    Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));\n    return array;\n}\n
\n

Finally here is a jsFiddle so you can see it for yourself: http://jsfiddle.net/luisperezphd/Wc8aS/

\n

And this is how you use the functions:

\n
// if you want to insert specific values whether constants or variables:\ninsertAt(arr, 1, "x", "y", "z");\n\n// OR if you have an array:\nvar arrToInsert = ["x", "y", "z"];\ninsertArrayAt(arr, 1, arrToInsert);\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25145a5cd2c51555ffcc3", + "creator": "CRice", + "createdAt": 1422492293000, + "text": "this is a great example of when to use 'apply'", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25145a5cd2c51555ffcc5", + "creator": "S At", + "createdAt": 1659366233000, + "text": "Thank alot and i use it in qml", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff35e", + "creator": "tvanfosson", + "createdAt": 1235572376000, + "text": "

You want the splice function on the native array object.

\n

arr.splice(index, 0, item); will insert item into arr at the specified index (deleting 0 items first, that is, it's just an insert).

\n

In this example we will create an array and add an element to it into index 2:

\n

\r\n
\r\n
var arr = [];\narr[0] = \"Jani\";\narr[1] = \"Hege\";\narr[2] = \"Stale\";\narr[3] = \"Kai Jim\";\narr[4] = \"Borge\";\n\nconsole.log(arr.join()); // Jani,Hege,Stale,Kai Jim,Borge\narr.splice(2, 0, \"Lene\");\nconsole.log(arr.join()); // Jani,Hege,Lene,Stale,Kai Jim,Borge
\r\n
\r\n
\r\n

\n", + "upvotes": 7455, + "upvoterUsernames": [], + "downvotes": 1236, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f25145a5cd2c51555ffcd0", + "creator": "Christoph", + "createdAt": 1235573596000, + "text": "@tags2k: because the function does more than inserting items and it's name was already established in perl?", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [] + }, + { + "_id": "62f25145a5cd2c51555ffcd2", + "creator": "ArtOfWarfare", + "createdAt": 1464956380000, + "text": "@tonix - Your code is written in JavaScript and needs to be interpreted. Splice's code is likely more native.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25145a5cd2c51555ffcd4", + "creator": "tonix", + "createdAt": 1465038180000, + "text": "@ArtOfWarfare Yes, I know. But I am curious to do a benchmark anyway. I just don't remember the name of the site where you benchmark JS code...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f25145a5cd2c51555ffcd6", + "creator": "Nate Thompson", + "createdAt": 1620934593000, + "text": "Is there a certain critereria that you would you splice instead of just array[index] = newValue?", + "upvotes": 244, + "upvoterUsernames": [], + "downvotes": 244, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff362", + "creator": "Ville", + "createdAt": 1449254165000, + "text": "

Even though this has been answered already, I'm adding this note for an alternative approach.

\n

I wanted to place a known number of items into an array, into specific positions, as they come off of an "associative array" (i.e. an object) which by definition is not guaranteed to be in a sorted order. I wanted the resulting array to be an array of objects, but the objects to be in a specific order in the array since an array guarantees their order. So I did this.

\n

First the source object, a JSONB string retrieved from PostgreSQL. I wanted to have it sorted by the "order" property in each child object.

\n
var jsonb_str = '{"one": {"abbr": "", "order": 3}, "two": {"abbr": "", "order": 4}, "three": {"abbr": "", "order": 5}, "initialize": {"abbr": "init", "order": 1}, "start": {"abbr": "", "order": 2}}';\n\nvar jsonb_obj = JSON.parse(jsonb_str);\n
\n

Since the number of nodes in the object is known, I first create an array with the specified length:

\n
var obj_length = Object.keys(jsonb_obj).length;\nvar sorted_array = new Array(obj_length);\n
\n

And then iterate the object, placing the newly created temporary objects into the desired locations in the array without really any "sorting" taking place.

\n
for (var key of Object.keys(jsonb_obj)) {\n  var tobj = {};\n  tobj[key] = jsonb_obj[key].abbr;\n\n  var position = jsonb_obj[key].order - 1;\n  sorted_array[position] = tobj;\n}\n\nconsole.dir(sorted_array);\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff361", + "creator": "VisioN", + "createdAt": 1364233530000, + "text": "

Custom array insert methods

\n\n

1. With multiple arguments and chaining support

\n\n
/* Syntax:\n   array.insert(index, value1, value2, ..., valueN) */\n\nArray.prototype.insert = function(index) {\n    this.splice.apply(this, [index, 0].concat(\n        Array.prototype.slice.call(arguments, 1)));\n    return this;\n};\n
\n\n

It can insert multiple elements (as native splice does) and supports chaining:

\n\n
[\"a\", \"b\", \"c\", \"d\"].insert(2, \"X\", \"Y\", \"Z\").slice(1, 6);\n// [\"b\", \"X\", \"Y\", \"Z\", \"c\"]\n
\n\n
\n\n

2. With array-type arguments merging and chaining support

\n\n
/* Syntax:\n   array.insert(index, value1, value2, ..., valueN) */\n\nArray.prototype.insert = function(index) {\n    index = Math.min(index, this.length);\n    arguments.length > 1\n        && this.splice.apply(this, [index, 0].concat([].pop.call(arguments)))\n        && this.insert.apply(this, arguments);\n    return this;\n};\n
\n\n

It can merge arrays from the arguments with the given array and also supports chaining:

\n\n
[\"a\", \"b\", \"c\", \"d\"].insert(2, \"V\", [\"W\", \"X\", \"Y\"], \"Z\").join(\"-\");\n// \"a-b-V-W-X-Y-Z-c-d\"\n
\n\n

DEMO: http://jsfiddle.net/UPphH/

\n", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffcd9", + "creator": "Nolo", + "createdAt": 1364687809000, + "text": "Is there a compact way to have this version also merge an array when it finds one in the arguments?", + "upvotes": 195, + "upvoterUsernames": [], + "downvotes": 195, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffcdb", + "creator": "Corné", + "createdAt": 1593140938000, + "text": "For some reason, when I go to use insert2, i get an "Expected 1 argument, but got 2" exception.", + "upvotes": 374, + "upvoterUsernames": [], + "downvotes": 374, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff360", + "creator": "FrEsC 81", + "createdAt": 1349274414000, + "text": "

You can implement the Array.insert method by doing this:

\n\n
Array.prototype.insert = function ( index, item ) {\n    this.splice( index, 0, item );\n};\n
\n\n

Then you can use it like:

\n\n
var arr = [ 'A', 'B', 'D', 'E' ];\narr.insert(2, 'C');\n\n// => arr == [ 'A', 'B', 'C', 'D', 'E' ]\n
\n", + "upvotes": 570, + "upvoterUsernames": [], + "downvotes": 193, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffcde", + "creator": "rep_movsd", + "createdAt": 1404293935000, + "text": "The problem with adding stuff to array is that the function will show up as an element when you do for(i in arr) {...}", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce0", + "creator": "marsze", + "createdAt": 1534844893000, + "text": "But keep in mind that it's not recommended to extend native types since it might interfere with other code or future functionality.", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce2", + "creator": "Luis Cabrera Benito", + "createdAt": 1550599765000, + "text": "Don’t modify objects you don’t own", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce4", + "creator": "Solomon Ucko", + "createdAt": 1557023966000, + "text": "@RyanSmith Or, with ES6, Array.prototype.insert = function(index, ...items) { this.splice.call(this, index, 0, ...items); }.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce5", + "creator": "Ryan Smith", + "createdAt": 1557432786000, + "text": "@SolomonUcko yeah you could tbh for ES6 I'd just do [...arr.slice(0, index), ...items, ...arr.slice(index)]", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce7", + "creator": "Solomon Ucko", + "createdAt": 1557432887000, + "text": "@RyanSmith The problem is that that has to copy the start of the array, not just the end.", + "upvotes": 1246, + "upvoterUsernames": [], + "downvotes": 1246, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffce8", + "creator": "satya164", + "createdAt": 1559307260000, + "text": "Don't modify prototypes", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff363", + "creator": "Redu", + "createdAt": 1463181734000, + "text": "

For proper functional programming and chaining purposes, an invention of Array.prototype.insert() is essential. Actually, the splice could have been perfect if it had returned the mutated array instead of a totally meaningless empty array. So here it goes:

\n

\r\n
\r\n
Array.prototype.insert = function(i,...rest){\n  this.splice(i,0,...rest)\n  return this\n}\n\nvar a = [3,4,8,9];\ndocument.write(\"<pre>\" + JSON.stringify(a.insert(2,5,6,7)) + \"</pre>\");
\r\n
\r\n
\r\n

\n

Well, OK, the above with the Array.prototype.splice() one mutates the original array and some might complain like "you shouldn't modify what doesn't belong to you" and that might turn out to be right as well. So for the public welfare, I would like to give another Array.prototype.insert() which doesn't mutate the original array. Here it goes;

\n

\r\n
\r\n
Array.prototype.insert = function(i,...rest){\n  return this.slice(0,i).concat(rest,this.slice(i));\n}\n\nvar a = [3,4,8,9],\n    b = a.insert(2,5,6,7);\nconsole.log(JSON.stringify(a));\nconsole.log(JSON.stringify(b));
\r\n
\r\n
\r\n

\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff364", + "creator": "gafi", + "createdAt": 1467623882000, + "text": "

Other than splice, you can use this approach which will not mutate the original array, but it will create a new array with the added item. It is useful, when you need to avoid mutation. I'm using the ES6 spread operator here.

\n

\r\n
\r\n
const items = [1, 2, 3, 4, 5]\n\nconst insert = (arr, index, newItem) => [\n  // part of the array before the specified index\n  ...arr.slice(0, index),\n  // inserted item\n  newItem,\n  // part of the array after the specified index\n  ...arr.slice(index)\n]\n\nconst result = insert(items, 1, 10)\n\nconsole.log(result)\n// [1, 10, 2, 3, 4, 5]
\r\n
\r\n
\r\n

\n

This can be used to add more than one item by tweaking the function a bit to use the rest operator for the new items, and spread that in the returned result as well:

\n

\r\n
\r\n
const items = [1, 2, 3, 4, 5]\n\nconst insert = (arr, index, ...newItems) => [\n  // part of the array before the specified index\n  ...arr.slice(0, index),\n  // inserted items\n  ...newItems,\n  // part of the array after the specified index\n  ...arr.slice(index)\n]\n\nconst result = insert(items, 1, 10, 20)\n\nconsole.log(result)\n// [1, 10, 20, 2, 3, 4, 5]
\r\n
\r\n
\r\n

\n", + "upvotes": 360, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffcec", + "creator": "gafi", + "createdAt": 1513463758000, + "text": "@HarshKanchina That's probably because most of the answers are pre ES6, but this approach is very common now from my experience", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffcee", + "creator": "Daniele Testa", + "createdAt": 1621935273000, + "text": "You should explain WHY you should avoid mutation. Is it slow? If so, how much slower? Is it worth the extra "hassle"?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff366", + "creator": "kind user", + "createdAt": 1491412018000, + "text": "

Another possible solution, with usage of Array.reduce.

\n

\r\n
\r\n
const arr = [\"apple\", \"orange\", \"raspberry\"];\nconst arr2 = [1, 2, 4];\n\nconst insert = (arr, item, index) =>\n  arr.reduce(function(s, a, i) {\n    i === index ? s.push(item, a) : s.push(a);\n    return s;\n  }, []);\n\nconsole.log(insert(arr, \"banana\", 1));\nconsole.log(insert(arr2, 3, 2))
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff365", + "creator": "Pawan", + "createdAt": 1488954923000, + "text": "

I tried this and it is working fine!

\n
var initialArr = ["India","China","Japan","USA"];\ninitialArr.splice(index, 0, item);\n
\n

Index is the position where you want to insert or delete the element.

\n

0, i.e., the second parameter, defines the number of elements from the index to be removed.\nitem contains the new entries which you want to make in the array. It can be one or more than one.

\n
initialArr.splice(2, 0, "Nigeria");\ninitialArr.splice(2, 0, "Australia","UK");\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff369", + "creator": "Clyde", + "createdAt": 1544070403000, + "text": "

Anyone who's still having issues with this one and have tried all the options in previous answers and never got it. I'm sharing my solution, and this is to take into consideration that you don't want to explicitly state the properties of your object vs the array.

\n
function isIdentical(left, right){\n    return JSON.stringify(left) === JSON.stringify(right);\n}\n\nfunction contains(array, obj){\n    let count = 0;\n    array.map((cur) => {\n        if(this.isIdentical(cur, obj)) \n            count++;\n    });\n    return count > 0;\n}\n
\n

This is a combination of iterating the reference array and comparing it to the object you wanted to check, converting both of them into a string, and then iterating if it matched. Then you can just count. This can be improved, but this is where I settled.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff368", + "creator": "Srikrushna", + "createdAt": 1530213204000, + "text": "

Append a single element at a specific index

\n
// Append at a specific position (here at index 1)\narrName.splice(1, 0,'newName1');\n// 1: index number, 0: number of element to remove, newName1: new element\n\n\n// Append at a specific position (here at index 3)\narrName[3] = 'newName1';\n
\n

Append multiple elements at a specific index

\n
// Append from index number 1\narrName.splice(1, 0, 'newElemenet1', 'newElemenet2', 'newElemenet3');\n// 1: index number from where append start,\n// 0: number of element to remove,\n//newElemenet1,2,3: new elements\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffcf3", + "creator": "sebastianf182", + "createdAt": 1536619257000, + "text": "Worth noticing that arrName[3] does not append, it overrides.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffcf5", + "creator": "sebastianf182", + "createdAt": 1536778295000, + "text": "If arrName has more than 3 elements you are overriding the 3rd, not appending. Or am I looking this at the wrong way?", + "upvotes": 249, + "upvoterUsernames": [], + "downvotes": 249, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffcf6", + "creator": "sebastianf182", + "createdAt": 1536785295000, + "text": "I dont have a problem, what I am saying is that this does not append: arrName[3]. You are overriding whatever is in the 4th position.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffcf7", + "creator": "Srikrushna", + "createdAt": 1608790612000, + "text": "@awe I am agree in your comment, but the question is in specific index, we have to insert the elements. For that question my answer is right", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff367", + "creator": "Alireza", + "createdAt": 1514207042000, + "text": "

I recommend using pure JavaScript in this case. Also there isn't any insert method in JavaScript, but we have a method which is a built-in Array method which does the job for you. It's called splice...

\n

Let's see what's splice()...

\n
\n

The splice() method changes the contents of an array by removing\nexisting elements and/or adding new elements.

\n
\n

OK, imagine we have this array below:

\n
const arr = [1, 2, 3, 4, 5];\n
\n

We can remove 3 like this:

\n
arr.splice(arr.indexOf(3), 1);\n
\n

It will return 3, but if we check the arr now, we have:

\n
[1, 2, 4, 5]\n
\n

So far, so good, but how we can add a new element to array using splice?

\n

Let's put back 3 in the arr...

\n
arr.splice(2, 0, 3);\n
\n

Let's see what we have done...

\n

We use splice again, but this time for the second argument, we pass 0, meaning we don't want to delete any item, but at the same time, we add a third argument which is the 3 that will be added at second index...

\n

You should be aware that we can delete and add at the same time. For example, now we can do:

\n
arr.splice(2, 2, 3);\n
\n

Which will delete two items at index 2. Then add 3 at index 2 and the result will be:

\n
[1, 2, 3, 5];\n
\n

This is showing how each item in splice work:

\n
\n

array.splice(start, deleteCount, item1, item2, item3 ...)

\n
\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36a", + "creator": "alejoko", + "createdAt": 1547312677000, + "text": "

Taking profit of the reduce method as follows:

\n
function insert(arr, val, index) {\n    return index >= arr.length\n        ? arr.concat(val)\n        : arr.reduce((prev, x, i) => prev.concat(i === index ? [val, x] : x), []);\n}\n
\n

So in this way we can return a new array (will be a cool functional way - more much better than using push or splice) with the element inserted at index, and if the index is greater than the length of the array it will be inserted at the end.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36b", + "creator": "Lesly Revenge", + "createdAt": 1550357323000, + "text": "

Here's a working function that I use in one of my applications.

\n

This checks if an item exists:

\n
let ifExist = (item, strings = [ '' ], position = 0) => {\n     // Output into an array with an empty string. Important just in case their isn't any item.\n    let output = [ '' ];\n    // Check to see if the item that will be positioned exist.\n    if (item) {\n        // Output should be equal to an array of strings.\n        output = strings;\n       // Use splice() in order to break the array.\n       // Use positional parameters to state where to put the item\n       // and 0 is to not replace an index. Item is the actual item we are placing at the prescribed position.\n        output.splice(position, 0, item);\n    }\n    // Empty string is so we do not concatenate with comma or anything else.\n    return output.join("");\n};\n
\n

And then I call it below.

\n
ifExist("friends", [ ' ( ', ' )' ], 1)}  // Output: ( friends )\nifExist("friends", [ ' - '], 1)}  // Output:  - friends\nifExist("friends", [ ':'], 0)}  // Output:   friends:\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36c", + "creator": "meem", + "createdAt": 1563471486000, + "text": "

I have to agree with Redu's answer because splice() definitely has a bit of a confusing interface. And the response given by cdbajorin that "it only returns an empty array when the second parameter is 0. If it's greater than 0, it returns the items removed from the array" is, while accurate, proving the point.

\n

The function's intent is to splice or as said earlier by Jakob Keller, "to join or connect, also to change.

\n

You have an established array that you are now changing which would involve adding or removing elements...." Given that, the return value of the elements, if any, that were removed is awkward at best. And I 100% agree that this method could have been better suited to chaining if it had returned what seems natural, a new array with the spliced elements added. Then you could do things like ["19", "17"].splice(1,0,"18").join("...") or whatever you like with the returned array.

\n

The fact that it returns what was removed is just kind of nonsense IMHO. If the intention of the method was to "cut out a set of elements" and that was its only intent, maybe. It seems like if I don't know what I'm cutting out already though, I probably have little reason to cut those elements out, doesn't it?

\n

It would be better if it behaved like concat(), map(), reduce(), slice(), etc. where a new array is made from the existing array rather than mutating the existing array. Those are all chainable, and that is a significant issue. It's rather common to chain array manipulation.

\n

It seems like the language needs to go one or the other direction and try to stick to it as much as possible. JavaScript being functional and less declarative, it just seems like a strange deviation from the norm.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36d", + "creator": "M. Hamza Rajput", + "createdAt": 1566984502000, + "text": "

Here are two ways:

\n

\r\n
\r\n
const array = [ 'My', 'name', 'Hamza' ];\n\narray.splice(2, 0, 'is');\n\nconsole.log(\"Method 1: \", array.join(\" \"));
\r\n
\r\n
\r\n

\n

Or

\n

\r\n
\r\n
Array.prototype.insert = function ( index, item ) {\n    this.splice( index, 0, item );\n};\n\nconst array = [ 'My', 'name', 'Hamza' ];\narray.insert(2, 'is');\n\nconsole.log(\"Method 2 : \", array.join(\" \"));
\r\n
\r\n
\r\n

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36e", + "creator": "vkarpov15", + "createdAt": 1577129849000, + "text": "

Array#splice() is the way to go, unless you really want to avoid mutating the array. Given 2 arrays arr1 and arr2, here's how you would insert the contents of arr2 into arr1 after the first element:

\n\n

\r\n
\r\n
const arr1 = ['a', 'd', 'e'];\r\nconst arr2 = ['b', 'c'];\r\n\r\narr1.splice(1, 0, ...arr2); // arr1 now contains ['a', 'b', 'c', 'd', 'e']\r\n\r\nconsole.log(arr1)
\r\n
\r\n
\r\n

\n\n

If you are concerned about mutating the array (for example, if using Immutable.js), you can instead use slice(), not to be confused with splice() with a 'p'.

\n\n
const arr3 = [...arr1.slice(0, 1), ...arr2, ...arr1.slice(1)];\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff36f", + "creator": "Kamil Kiełczewski", + "createdAt": 1587726468000, + "text": "

Solutions & Performance

\n

Today (2020.04.24) I perform tests for chosen solutions for big and small arrays. I tested them on macOS v10.13.6 (High Sierra) on Chrome 81.0, Safari 13.1, and Firefox 75.0.

\n

Conclusions

\n

For all browsers

\n\n

\"Enter

\n

Details

\n

Tests were divided into two groups: in-place solutions (AI, BI, and CI) and non-in-place solutions (D, E, and F) and was performed for two cases:

\n\n

Tested code is presented in the below snippet:

\n

jsfiddle

\n

\r\n
\r\n
function AI(arr, i, el) {\n  arr.splice(i, 0, el);\n  return arr;\n}\n\nfunction BI(arr, i, el) {\n  Array.prototype.splice.apply(arr, [i, 0, el]);\n  return arr;\n}\n\nfunction CI(arr, i, el) {\n  Array.prototype.splice.call(arr, i, 0, el);\n  return arr;\n}\n\nfunction D(arr, i, el) {\n  return arr.slice(0, i).concat(el, arr.slice(i));\n}\n\nfunction E(arr, i, el) {\n  return [...arr.slice(0, i), el, ...arr.slice(i)]\n}\n\nfunction F(arr, i, el) {\n  return arr.reduce((s, a, j)=> (j-i ? s.push(a) : s.push(el, a), s), []);\n}\n\n\n\n// -------------\n// TEST\n// -------------\n\nlet arr = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"];\n\nlet log = (n, f) => {\n  let a = f([...arr], 3, \"NEW\");\n  console.log(`${n}: [${a}]`);\n};\n\nlog('AI', AI);\nlog('BI', BI);\nlog('CI', CI);\nlog('D', D);\nlog('E', E);\nlog('F', F);
\r\n
This snippet only presents tested code (it not perform tests)
\r\n
\r\n
\r\n

\n

Example results for a small array on Google Chrome are below:

\n

\"Enter

\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffcfe", + "creator": "kabirbaidhya", + "createdAt": 1599889923000, + "text": "The answer doesn't address the OP's question.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff370", + "creator": "kabirbaidhya", + "createdAt": 1599892653000, + "text": "

Immutable insertion

\n

Using the splice method is surely the best answer if you need to insert into an array in-place.

\n

However, if you are looking for an immutable function that returns a new updated array instead of mutating the original array on insert, you can use the following function.

\n

\r\n
\r\n
function insert(array, index) {\n  const items = Array.prototype.slice.call(arguments, 2);\n\n  return [].concat(array.slice(0, index), items, array.slice(index));\n}\n\nconst list = ['one', 'two', 'three'];\n\nconst list1 = insert(list, 0, 'zero'); // Insert single item\nconst list2 = insert(list, 3, 'four', 'five', 'six'); // Insert multiple\n\nconsole.log('Original list: ', list);\nconsole.log('Inserted list1: ', list1);\nconsole.log('Inserted list2: ', list2);
\r\n
\r\n
\r\n

\n

Note: This is a pre-ES6 way of doing it, so it works for both older and newer browsers.

\n

If you're using ES6 then you can try out rest parameters too; see this answer.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff372", + "creator": "Baptiste Arnaud", + "createdAt": 1639391953000, + "text": "

Here is the modern (Typescript functional) way:

\n
export const insertItemInList = <T>(\n  arr: T[],\n  index: number,\n  newItem: T\n): T[] => [...arr.slice(0, index), newItem, ...arr.slice(index)]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff371", + "creator": "hossein sedighian", + "createdAt": 1602195999000, + "text": "

I like a little safety and I use this:

\n

\r\n
\r\n
Array.prototype.Insert = function (item, before) {\n  if (!item) return;\n  if (before == null || before < 0 || before > this.length - 1) {\n    this.push(item);\n    return;\n  }\n  this.splice(before, 0, item);\n}\n\n\nvar t = [\"a\", \"b\"]\n\nt.Insert(\"v\", 1)\n\nconsole.log(t)
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffd02", + "creator": "blazkovicz", + "createdAt": 1610370109000, + "text": "You said you like safety, then suggest to modify basic prototype! logic? no", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f25146a5cd2c51555ffd04", + "creator": "hossein sedighian", + "createdAt": 1611723627000, + "text": "yep .. it is m y definition of safety .. lol", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff373", + "creator": "Ran Turner", + "createdAt": 1639847474000, + "text": "

Using Array.prototype.splice() is an easy way to achieve it

\n

\r\n
\r\n
const numbers = ['one', 'two', 'four', 'five']\nnumbers.splice(2, 0, 'three');\n\nconsole.log(numbers)
\r\n
\r\n
\r\n

\n

Read more about Array.prototype.splice() here

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff374", + "creator": "Gass", + "createdAt": 1641462378000, + "text": "

You can use splice() for this

\n

The splice() method usually receives three arguments when adding an element:

\n
    \n
  1. The index of the array where the item is going to be added.
  2. \n
  3. The number of items to be removed, which in this case is 0.
  4. \n
  5. The element to add.
  6. \n
\n

\r\n
\r\n
let array = ['item 1', 'item 2', 'item 3']\nlet insertAtIndex = 0\nlet itemsToRemove = 0\n    \narray.splice(insertAtIndex, itemsToRemove, 'insert this string on index 0')\n\nconsole.log(array)
\r\n
\r\n
\r\n

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f25146a5cd2c51555ffd07", + "creator": "vdegenne", + "createdAt": 1653991020000, + "text": "next time read the answers please, this solution was already given. You are just repeating information.", + "upvotes": 6094, + "upvoterUsernames": [], + "downvotes": 6094, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f25140a5cd2c51555ff375", + "creator": "bmaggi", + "createdAt": 1642124540000, + "text": "

I do it like so:

\n
const insert = (what, where, index) => \n  ([...where.slice(0, index), what , ...where.slice(index, where.length)]);\n
\n

\r\n
\r\n
const insert = (what, where, index) =>\n  ([...where.slice(0, index), what , ...where.slice(index, where.length)]);\n  \nconst list = [1, 2, 3, 4, 5, 6];\nconst newList = insert('a', list, 2);\n\nconsole.log(newList.indexOf('a') === 2);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff376", + "creator": "Pikamander2", + "createdAt": 1647160169000, + "text": "

Here's a simple function that supports inserting multiple values at the same time:

\n
function add_items_to_array_at_position(array, index, new_items)\n{\n    return [...array.slice(0, index), ...new_items, ...array.slice(index)];\n}\n
\n

Usage example:

\n
let old_array = [1,2,5];\n\nlet new_array = add_items_to_array_at_position(old_array, 2, [3,4]);\n\nconsole.log(new_array);\n\n//Output: [1,2,3,4,5]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f25140a5cd2c51555ff377", + "creator": "Steve purpose", + "createdAt": 1658636359000, + "text": "
var array= [10,20,30,40]\n\nvar i;\n\nvar pos=2; //pos=index + 1\n/*pos is position which we want to insert at which is index + 1.position two in an array is index 1.*/\n\nvar value=5 \n//value to insert\n\n//Initialize from last array element\n\nfor(i=array.length-1;i>=pos-1;i--){\n\narray[i+1]=array[i]\n\n}\n\narray[pos-1]=value\n\nconsole.log(array)\n
\n", + "upvotes": 1944, + "upvoterUsernames": [], + "downvotes": 1944, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f25140a5cd2c51555ff356", + "creator": "Domino", + "createdAt": 1424273809000, + "text": "Note that JQuery is a DOM and event manipulation library, not a language of its own. It has nothing to do with array manipulation.", + "upvotes": 156, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f25140a5cd2c51555ff358", + "creator": "Victor", + "createdAt": 1525699874000, + "text": "@Tim, But it's still not a language of its own (still there are some questions like "how to sum two numbers in jQuery" here on SO)", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f25140a5cd2c51555ff35a", + "creator": "Tim", + "createdAt": 1525760222000, + "text": "@Victor No, and never will be. jQuery was useful and relevant, but it's had its day.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f25140a5cd2c51555ff35c", + "creator": "Andrew", + "createdAt": 1591488893000, + "text": "I'm annoyed that the title of this question and its page title are different (jQuery vs. JS). Shows up differently in search results.", + "upvotes": 4110, + "upvoterUsernames": [], + "downvotes": 4110, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2953746, + "uvac": 2953772 + } + }, + { + "_id": "62f321bb082fcc3049e8fedf", + "title": "How can I know which radio button is selected via jQuery?", + "title-lowercase": "how can i know which radio button is selected via jquery?", + "creator": "juan", + "createdAt": 1235764407000, + "status": "open", + "text": "

I have two radio buttons and want to post the value of the selected one.\nHow can I get the value with jQuery?

\n\n

I can get all of them like this:

\n\n
$(\"form :radio\")\n
\n\n

How do I know which one is selected?

\n", + "upvotes": 3933, + "upvoterUsernames": [], + "downvotes": 957, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2425652, + "answers": 37, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e90944", + "creator": "Peter J", + "createdAt": 1235764719000, + "text": "

To get the value of the selected radioName item of a form with id myForm:

\n
$('input[name=radioName]:checked', '#myForm').val()\n
\n

Here's an example:

\n

\r\n
\r\n
$('#myForm input').on('change', function() {\n  alert($('input[name=radioName]:checked', '#myForm').val());\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<form id=\"myForm\">\n  <fieldset>\n    <legend>Choose radioName</legend>\n    <label><input type=\"radio\" name=\"radioName\" value=\"1\" /> 1</label> <br />\n    <label><input type=\"radio\" name=\"radioName\" value=\"2\" /> 2</label> <br />\n    <label><input type=\"radio\" name=\"radioName\" value=\"3\" /> 3</label> <br />\n  </fieldset>\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 5043, + "upvoterUsernames": [], + "downvotes": 836, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32681082fcc3049e92111", + "creator": "trinalbadger587", + "createdAt": 1616910861000, + "text": "For a non-jquery version, see here: jsfiddle.net/2y76vp1e", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32681082fcc3049e92113", + "creator": "Ali", + "createdAt": 1624088509000, + "text": "@ ZeroNine, that's also the case in Symfony framework that I use.", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90945", + "creator": "tvanfosson", + "createdAt": 1235764825000, + "text": "

You can use the :checked selector along with the radio selector.

\n\n
 $(\"form:radio:checked\").val();\n
\n", + "upvotes": 163, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90946", + "creator": "Joberror", + "createdAt": 1262825135000, + "text": "

Use this..

\n\n
$(\"#myform input[type='radio']:checked\").val();\n
\n", + "upvotes": 696, + "upvoterUsernames": [], + "downvotes": 245, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90947", + "creator": "Francisco Alvarado", + "createdAt": 1285787206000, + "text": "

In a JSF generated radio button (using <h:selectOneRadio> tag), you can do this:

\n\n
radiobuttonvalue = jQuery(\"input[name='form_id\\:radiobutton_id']:checked\").val();\n
\n\n

where selectOneRadio ID is radiobutton_id and form ID is form_id.

\n\n

Be sure to use name instead id, as indicated, because jQuery uses this attribute (name is generated automatically by JSF resembling control ID).

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90948", + "creator": "Phillip Senn", + "createdAt": 1287260416000, + "text": "
$(\"input:radio:checked\").val();\n
\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90949", + "creator": "Matt Browne", + "createdAt": 1297275930000, + "text": "

If you already have a reference to a radio button group, for example:

\n\n
var myRadio = $(\"input[name=myRadio]\");\n
\n\n

Use the filter() function, not find(). (find() is for locating child/descendant elements, whereas filter() searches top-level elements in your selection.)

\n\n
var checkedValue = myRadio.filter(\":checked\").val();\n
\n\n
\n\n

Notes: This answer was originally correcting another answer that recommended using find(), which seems to have since been changed. find() could still be useful for the situation where you already had a reference to a container element, but not to the radio buttons, e.g.:

\n\n
var form = $(\"#mainForm\");\n...\nvar checkedValue = form.find(\"input[name=myRadio]:checked\").val();\n
\n", + "upvotes": 337, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9094a", + "creator": "Cam Tullos", + "createdAt": 1301524179000, + "text": "

This should work:

\n\n
$(\"input[name='radioName']:checked\").val()\n
\n\n

Note the \"\" usaged around the input:checked and not '' like the Peter J's solution

\n", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32682082fcc3049e92119", + "creator": "ajzbrun", + "createdAt": 1643244704000, + "text": "Why does this returns me "on" instead of the value of the selected radio button?", + "upvotes": 145, + "upvoterUsernames": [], + "downvotes": 145, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9094b", + "creator": "Alex V", + "createdAt": 1314899705000, + "text": "

Get all radios:

\n\n
var radios = jQuery(\"input[type='radio']\");\n
\n\n

Filter to get the one thats checked

\n\n
radios.filter(\":checked\")\n
\n", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9094c", + "creator": "RedDragon", + "createdAt": 1316633722000, + "text": "

Another option is:

\n\n
$('input[name=radioName]:checked').val()\n
\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32682082fcc3049e9211d", + "creator": "Matt Browne", + "createdAt": 1422710026000, + "text": "The ":radio" part isn't necessary here.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9094d", + "creator": "Mark", + "createdAt": 1321373042000, + "text": "

Also, check if the user does not select anything.

\n\n
var radioanswer = 'none';\nif ($('input[name=myRadio]:checked').val() != null) {           \n   radioanswer = $('input[name=myRadio]:checked').val();\n}\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9094e", + "creator": "Ramesh", + "createdAt": 1339044350000, + "text": "

If you have Multiple radio buttons in single form then

\n\n
var myRadio1 = $('input[name=radioButtonName1]');\nvar value1 = myRadio1.filter(':checked').val();\n\nvar myRadio2 = $('input[name=radioButtonName2]');\nvar value2 = myRadio2.filter(':checked').val();\n
\n\n

This is working for me.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9094f", + "creator": "Swadesh Bhattacharya", + "createdAt": 1342526506000, + "text": "
 $(\".Stat\").click(function () {\n     var rdbVal1 = $(\"input[name$=S]:checked\").val();\n }\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90951", + "creator": "Darin Peterson", + "createdAt": 1348244378000, + "text": "

Here's how I would write the form and handle the getting of the checked radio.

\n\n

Using a form called myForm:

\n\n
<form id='myForm'>\n    <input type='radio' name='radio1' class='radio1' value='val1' />\n    <input type='radio' name='radio1' class='radio1' value='val2' />\n    ...\n</form>\n
\n\n

Get the value from the form:

\n\n
$('#myForm .radio1:checked').val();\n
\n\n

If you're not posting the form, I would simplify it further by using:

\n\n
<input type='radio' class='radio1' value='val1' />\n<input type='radio' class='radio1' value='val2' />\n
\n\n

Then getting the checked value becomes:

\n\n
    $('.radio1:checked').val();\n
\n\n

Having a class name on the input allows me to easily style the inputs...

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90952", + "creator": "Rodrigo Dias", + "createdAt": 1357927537000, + "text": "

To get the value of the selected radio that uses a class:

\n\n
$('.class:checked').val()\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90950", + "creator": "rmbianchi", + "createdAt": 1343927389000, + "text": "

In my case I have two radio buttons in one form and I wanted to know the status of each button.\nThis below worked for me:

\n\n

\r\n
\r\n
// get radio buttons value\r\nconsole.log( \"radio1: \" +  $('input[id=radio1]:checked', '#toggle-form').val() );\r\nconsole.log( \"radio2: \" +  $('input[id=radio2]:checked', '#toggle-form').val() );\r\n\r\n\r\n    
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<form id=\"toggle-form\">\r\n  <div id=\"radio\">\r\n    <input type=\"radio\" id=\"radio1\" name=\"radio\" checked=\"checked\" /><label for=\"radio1\">Plot single</label>\r\n    <input type=\"radio\" id=\"radio2\" name=\"radio\"/><label for=\"radio2\">Plot all</label>\r\n  </div>\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90953", + "creator": "Mathias Lykkegaard Lorenzen", + "createdAt": 1366192729000, + "text": "

I wrote a jQuery plugin for setting and getting radio-button values. It also respects the \"change\" event on them.

\n\n
(function ($) {\n\n    function changeRadioButton(element, value) {\n        var name = $(element).attr(\"name\");\n        $(\"[type=radio][name=\" + name + \"]:checked\").removeAttr(\"checked\");\n        $(\"[type=radio][name=\" + name + \"][value=\" + value + \"]\").attr(\"checked\", \"checked\");\n        $(\"[type=radio][name=\" + name + \"]:checked\").change();\n    }\n\n    function getRadioButton(element) {\n        var name = $(element).attr(\"name\");\n        return $(\"[type=radio][name=\" + name + \"]:checked\").attr(\"value\");\n    }\n\n    var originalVal = $.fn.val;\n    $.fn.val = function(value) {\n\n        //is it a radio button? treat it differently.\n        if($(this).is(\"[type=radio]\")) {\n\n            if (typeof value != 'undefined') {\n\n                //setter\n                changeRadioButton(this, value);\n                return $(this);\n\n            } else {\n\n                //getter\n                return getRadioButton(this);\n\n            }\n\n        } else {\n\n            //it wasn't a radio button - let's call the default val function.\n            if (typeof value != 'undefined') {\n                return originalVal.call(this, value);\n            } else {\n                return originalVal.call(this);\n            }\n\n        }\n    };\n})(jQuery);\n
\n\n

Put the code anywhere to enable the addin. Then enjoy! It just overrides the default val function without breaking anything.

\n\n

You can visit this jsFiddle to try it in action, and see how it works.

\n\n

Fiddle

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90954", + "creator": "jeswin", + "createdAt": 1376158933000, + "text": "

Use this:

\n\n
value = $('input[name=button-name]:checked').val();\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90955", + "creator": "Randy Greencorn", + "createdAt": 1376273595000, + "text": "

If you only have 1 set of radio buttons on 1 form, the jQuery code is as simple as this:

\n\n
$( \"input:checked\" ).val()\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90956", + "creator": "Juan", + "createdAt": 1377894440000, + "text": "

If you want just the boolean value, i.e. if it's checked or not try this:

\n\n
$(\"#Myradio\").is(\":checked\")\n
\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9095d", + "creator": "Code Spy", + "createdAt": 1501567798000, + "text": "

DEMO : https://jsfiddle.net/ipsjolly/xygr065w/

\n\n

\r\n
\r\n
\t$(function(){\r\n\t    $(\"#submit\").click(function(){      \r\n\t        alert($('input:radio:checked').val());\r\n\t    });\r\n\t });
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<table>\r\n       <tr>\r\n         <td>Sales Promotion</td>\r\n         <td><input type=\"radio\" name=\"q12_3\" value=\"1\">1</td>\r\n         <td><input type=\"radio\" name=\"q12_3\" value=\"2\">2</td>\r\n         <td><input type=\"radio\" name=\"q12_3\" value=\"3\">3</td>\r\n         <td><input type=\"radio\" name=\"q12_3\" value=\"4\">4</td>\r\n         <td><input type=\"radio\" name=\"q12_3\" value=\"5\">5</td>\r\n      </tr>\r\n    </table>\r\n<button id=\"submit\">submit</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9095e", + "creator": "user1585204", + "createdAt": 1504539695000, + "text": "

What I needed to do was simplify C# code, that is do as much as possible in the front end JavaScript. I'm using a fieldset container because I'm working in DNN and it has its own form. So I can't add a form.

\n\n

I need to test which text box out of 3 is being used and if it is, what's the type of search? Starts with the value, Contains the value, Exact Match of the value.

\n\n

HTML:

\n\n
<fieldset id=\"fsPartNum\" class=\"form-inline\">\n<div class=\"form-group\">\n    <label for=\"txtPartNumber\">Part Number:</label>\n    <input type=\"text\" id=\"txtPartNumber\" class=\"input-margin-pn\" />\n</div>\n<div class=\"form-group\">\n    <label for=\"radPNStartsWith\">Starts With: </label>\n    <input type=\"radio\" id=\"radPNStartsWith\" name=\"partNumber\" checked  value=\"StartsWith\"/>\n</div>\n<div class=\"form-group\">\n    <label for=\"radPNContains\">Contains: </label>\n    <input type=\"radio\" id=\"radPNContains\" name=\"partNumber\" value=\"Contains\" />\n</div>\n<div class=\"form-group\">\n    <label for=\"radPNExactMatch\">Exact Match: </label>\n    <input type=\"radio\" id=\"radPNExactMatch\" name=\"partNumber\" value=\"ExactMatch\" />\n</div>\n
\n\n

\n\n

And my JavaScript is:

\n\n
        alert($('input[name=partNumber]:checked', '#fsPartNum').val()); \n    if(txtPartNumber.val() !== ''){\n        message = 'Customer Part Number';\n    }\n    else if(txtCommercialPartNumber.val() !== ''){\n\n    }\n    else if(txtDescription.val() !== ''){\n\n    }\n
\n\n

Just saying any containing tag with an ID can be used. For DNNers, this is good to know. The end goal here is pass to the mid-level code what is needed to start a parts search in SQL Server.

\n\n

This way I don't have to copy the much more complicated previous C# code also. The heavy lifting is being done right here.

\n\n

I had to look a bit for this and then tinker with it to get it to work. So for other DNNers, hopefully this is easy to find.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9095f", + "creator": "Mehdi Bouzidi", + "createdAt": 1507035412000, + "text": "

Another way to get it:

\n\n

\r\n
\r\n
 $(\"#myForm input[type=radio]\").on(\"change\",function(){\r\n   if(this.checked) {\r\n    alert(this.value);\r\n    }\r\n  });
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<form id=\"myForm\">\r\n   <span><input type=\"radio\" name=\"q12_3\" value=\"1\">1</span><br>\r\n   <span><input type=\"radio\" name=\"q12_3\" value=\"2\">2</span>\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90960", + "creator": "Sandeep Sherpur", + "createdAt": 1530693118000, + "text": "

You can call Function onChange()

\n\n
  <input type=\"radio\" name=\"radioName\" value=\"1\" onchange=\"radio_changed($(this).val())\" /> 1 <br />\n  <input type=\"radio\" name=\"radioName\" value=\"2\" onchange=\"radio_changed($(this).val())\"  /> 2 <br />\n  <input type=\"radio\" name=\"radioName\" value=\"3\"  onchange=\"radio_changed($(this).val())\" /> 3 <br />\n\n<script>\nfunction radio_changed(val){\n    alert(val);\n}\n</script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90961", + "creator": "fitorec", + "createdAt": 1531910982000, + "text": "

You need access with the :checked selector:

\n\n

Check this doc:

\n\n
\n \n
\n\n

a example:

\n\n

\r\n
\r\n
$('input[name=radioName]:checked', '#myForm').val()\r\n$('#myForm input').on('change', function() {\r\n\t$('#val').text($('input[name=radioName]:checked', '#myForm').val());\r\n});
\r\n
#val {\r\n  color: #EB0054;\r\n  font-size: 1.5em;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n<h3>Radio value: <span id='val'><span></h3>\r\n<form id=\"myForm\">\r\n  <input type=\"radio\" name=\"radioName\" value=\"a\"> a <br>\r\n  <input type=\"radio\" name=\"radioName\" value=\"b\"> b <br>\r\n  <input type=\"radio\" name=\"radioName\" value=\"c\"> c <br>\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90962", + "creator": "Iyyappan Amirthalingam", + "createdAt": 1541622440000, + "text": "

JQuery to get all the radio buttons in the form and the checked value.

\n\n
$.each($(\"input[type='radio']\").filter(\":checked\"), function () {\n  console.log(\"Name:\" + this.name);\n  console.log(\"Value:\" + $(this).val());\n});\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90963", + "creator": "Alireza", + "createdAt": 1548511543000, + "text": "

How about this?

\n\n

Using change and get the value of radio type is checked...

\n\n

\r\n
\r\n
$('#my-radio-form').on('change', function() {\r\n  console.log($('[type=\"radio\"]:checked').val());\r\n});
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js\"></script>\r\n<form id=\"my-radio-form\">\r\n  <input type=\"radio\" name=\"input-radio\" value=\"a\" />a\r\n  <input type=\"radio\" name=\"input-radio\" value=\"b\" />b\r\n  <input type=\"radio\" name=\"input-radio\" value=\"c\" />c\r\n  <input type=\"radio\" name=\"input-radio\" value=\"d\" />d\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90964", + "creator": "Javed Khan", + "createdAt": 1559105949000, + "text": "

**Please try below example to check which radio button in selected **

\n\n
<script>\n    $('#form1 input').on('change', function() {\n       alert($('input[name=age]:checked', '#form1 ').val()); \n    });\n</script>\n <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\">\n    <form id=\"form1\">\n      <input type=\"radio\" name=\"age\" value=\"18\" /> 18 <br />\n      <input type=\"radio\" name=\"age\" value=\"20\" /> 20 <br />\n      <input type=\"radio\" name=\"age\" value=\"22\" /> 22 <br />\n    </form>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90965", + "creator": "Samir Lakhani", + "createdAt": 1577437300000, + "text": "

jQuery plugin for setting and getting radio-button values. It also respects the \"change\" event on them.

\n\n
 <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n    <form id=\"toggle-form\">\n      <div id=\"radio\">\n        <input type=\"radio\" id=\"radio1\" name=\"radio\" checked=\"checked\" /><label for=\"radio1\">Plot single</label>\n        <input type=\"radio\" id=\"radio2\" name=\"radio\"/><label for=\"radio2\">Plot all</label>\n      </div>\n    </form>\n    <script type=\"text/javascript\">\n    $( document ).ready(function() {\n     //Get all radios:\n     var radios = jQuery(\"input[type='radio']\");\n     checked_radios=radios.filter(\":checked\");\nfor(i=0;i<checked_radios.length;i++)\n{\n   console.log(checked_radios[i]);\n}\n\n    });\n    </script>\n
\n\n

or another way

\n\n
<script type=\"text/javascript\">\n$( document ).ready(function() {\n  //Get all radios:\n  checked_radios=jQuery('input[name=radio]:checked').val(); \nfor(i=0;i<checked_radios.length;i++)\n{\n   console.log(checked_radios[i]);\n}\n\n});\n</script>\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90966", + "creator": "Kamil Kiełczewski", + "createdAt": 1578648173000, + "text": "

Try

\n\n
myForm.myOption.value\n
\n\n

\r\n
\r\n
function check() {\r\n  console.log( myForm.myOption.value );\r\n}
\r\n
<form id=\"myForm\">\r\n  <input type=\"radio\" name=\"myOption\" value=\"1\"> 1 <br>\r\n  <input type=\"radio\" name=\"myOption\" value=\"2\"> 2 <br>\r\n  <input type=\"radio\" name=\"myOption\" value=\"3\"> 3 <br>\r\n</form>\r\n<button onclick=\"check()\">check</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90968", + "creator": "Garrett", + "createdAt": 1609621505000, + "text": "

This solution does not require jQuery.

\n
const RADIO_NAME = "radioName";\nconst radios = Array.from(document.getElementsByName(RADIO_NAME));\nconst checkedRadio = radios.filter(e=>e.checked);\n
\n

This uses jQuery:

\n
const radios = Array.from($(`[name=${RADIO_NAME}`));\nconst checkedRadio = radios.filter(e=>e.checked);\n
\n

jQuery adds an extra layer of abstraction that isn't needed here.

\n

You could also use:

\n
const radios = Array.from(document.querySelectorAll(`[name=${RADIO_NAME}`));\nconst checkedRadio = radios.filter(e=>e.checked)[0];\n
\n

But getElementsByName is simple and clear enough.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90967", + "creator": "salman ifrahim", + "createdAt": 1601293709000, + "text": "

try this one.\nit worked for me

\n
$('input[type="radio"][name="name"]:checked').val();\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90957", + "creator": "Nilesh", + "createdAt": 1378178723000, + "text": "

To retrieve all radio buttons values in JavaScript array use following jQuery code :

\n\n
var values = jQuery('input:checkbox:checked.group1').map(function () {\n    return this.value;\n}).get();\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c1082fcc3049e92e5b", + "creator": "Matt Browne", + "createdAt": 1422710424000, + "text": "Radio buttons are not the same as checkboxes. This example uses checkboxes.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9095a", + "creator": "Manoj", + "createdAt": 1423914097000, + "text": "

This works fine

\n\n
$('input[type=\"radio\"][class=\"className\"]:checked').val()\n
\n\n

Working Demo

\n\n

The :checked selector works for checkboxes, radio buttons, and select elements. For select elements only, use the :selected selector.

\n\n

API for :checked Selector

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90958", + "creator": "Ryan", + "createdAt": 1390459207000, + "text": "

I've released a library to help with this. Pulls all possible input values, actually, but also includes which radio button was checked. You can check it out at https://github.com/mazondo/formalizedata

\n\n

It'll give you a js object of the answers, so a form like:

\n\n
<form>\n<input type=\"radio\" name\"favorite-color\" value=\"blue\" checked> Blue\n<input type=\"radio\" name=\"favorite-color\" value=\"red\"> Red\n</form>\n
\n\n

will give you:

\n\n
$(\"form\").formalizeData()\n{\n  \"favorite-color\" : \"blue\"\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9095c", + "creator": "Gautam Rai", + "createdAt": 1499689281000, + "text": "

try it-

\n\n
var radioVal = $(\"#myform\").find(\"input[type='radio']:checked\").val();\n\nconsole.log(radioVal);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9095b", + "creator": "JoshYates1980", + "createdAt": 1493933201000, + "text": "
$(function () {\n// Someone has clicked one of the radio buttons\nvar myform= 'form.myform';\n$(myform).click(function () {\n    var radValue= \"\";\n    $(this).find('input[type=radio]:checked').each(function () {\n        radValue= $(this).val();\n    });\n  })\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90959", + "creator": "Lafif Astahdziq", + "createdAt": 1413740049000, + "text": "

I use this simple script

\n\n
$('input[name=\"myRadio\"]').on('change', function() {\n  var radioValue = $('input[name=\"myRadio\"]:checked').val();        \n  alert(radioValue); \n});\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c2082fcc3049e92e60", + "creator": "Matt Browne", + "createdAt": 1422710559000, + "text": "Use the click event rather than the change event if you want it to work in all browsers", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2429585, + "uvac": 2429622 + } + }, + { + "_id": "62f321bb082fcc3049e8ff05", + "title": "JavaScript equivalent to printf/String.Format", + "title-lowercase": "javascript equivalent to printf/string.format", + "creator": "Chris S", + "createdAt": 1236171182000, + "status": "open", + "text": "

I'm looking for a good JavaScript equivalent of the C/PHP printf() or for C#/Java programmers, String.Format() (IFormatProvider for .NET).

\n\n

My basic requirement is a thousand separator format for numbers for now, but something that handles lots of combinations (including dates) would be good.

\n\n

I realize Microsoft's Ajax library provides a version of String.Format(), but we don't want the entire overhead of that framework.

\n", + "upvotes": 3678, + "upvoterUsernames": [], + "downvotes": 1378, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2191913, + "answers": 56, + "answerItems": [ + { + "_id": "62f32924082fcc3049e92adf", + "creator": "Sven N", + "createdAt": 1254040560000, + "text": "

I use a small library called String.format for JavaScript which supports most of the format string capabilities (including format of numbers and dates), and uses the .NET syntax. The script itself is smaller than 4 kB, so it doesn't create much of overhead.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f05", + "creator": "ivarni", + "createdAt": 1403586478000, + "text": "@starmole the link is to a (minified) 4 kB javascript library. I don't believe pasting it into the answer is a good idea.", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae1", + "creator": "user437231", + "createdAt": 1283364324000, + "text": "

+1 Zippo with the exception that the function body needs to be as below or otherwise it appends the current string on every iteration:

\n\n
String.prototype.format = function() {\n    var formatted = this;\n    for (var arg in arguments) {\n        formatted = formatted.replace(\"{\" + arg + \"}\", arguments[arg]);\n    }\n    return formatted;\n};\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f08", + "creator": "xiao 啸", + "createdAt": 1292063221000, + "text": "It didn't work on Firefox. The debugger show arg as undefined.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f0a", + "creator": "alexandre-rousseau", + "createdAt": 1557393392000, + "text": "You should propose an answer edit instead of duplicate answer. This duplicate this answer", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae0", + "creator": "Zippo", + "createdAt": 1281959536000, + "text": "

I use this simple function:

\n\n
String.prototype.format = function() {\n    var formatted = this;\n    for( var arg in arguments ) {\n        formatted = formatted.replace(\"{\" + arg + \"}\", arguments[arg]);\n    }\n    return formatted;\n};\n
\n\n

That's very similar to string.format:

\n\n
\"{0} is dead, but {1} is alive!\".format(\"ASP\", \"ASP.NET\")\n
\n", + "upvotes": 236, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f0c", + "creator": "BrunoLM", + "createdAt": 1289579457000, + "text": "What about "{{0}} use this next instead {0}"? Not really a String.Format, but it is small and nice function to use.", + "upvotes": 997, + "upvoterUsernames": [], + "downvotes": 997, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f0e", + "creator": "guilin 桂林", + "createdAt": 1291098618000, + "text": "why +=?, should it formatted = this.replace("{" + arg + "}", arguments[arg]);", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f10", + "creator": "Pauan", + "createdAt": 1354747791000, + "text": "The variable arg is global. You need to do this instead: for (var arg in arguments) {", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae2", + "creator": "jsxt", + "createdAt": 1287054348000, + "text": "

JavaScript programmers can use String.prototype.sprintf at https://github.com/ildar-shaimordanov/jsxt/blob/master/js/String.js. Below is example:

\n\n
var d = new Date();\nvar dateStr = '%02d:%02d:%02d'.sprintf(\n    d.getHours(), \n    d.getMinutes(), \n    d.getSeconds());\n
\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92ae3", + "creator": "Filipiz", + "createdAt": 1290516100000, + "text": "

jsxt, Zippo

\n\n

This option fits better.

\n\n
String.prototype.format = function() {\n    var formatted = this;\n    for (var i = 0; i < arguments.length; i++) {\n        var regexp = new RegExp('\\\\{'+i+'\\\\}', 'gi');\n        formatted = formatted.replace(regexp, arguments[i]);\n    }\n    return formatted;\n};\n
\n\n

With this option I can replace strings like these:

\n\n
'The {0} is dead. Don\\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP');\n
\n\n

With your code the second {0} wouldn't be replaced. ;)

\n", + "upvotes": 316, + "upvoterUsernames": [], + "downvotes": 135, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f14", + "creator": "AndiDog", + "createdAt": 1342988683000, + "text": "jsxt is GPL-licensed unfortunately", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f16", + "creator": "Artem Balianytsia", + "createdAt": 1642920508000, + "text": "Very inefficient approach. Uses regex when not needed, looks up a whole string for searching many times.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae5", + "creator": "bart", + "createdAt": 1295971999000, + "text": "

Here's a minimal implementation of sprintf in JavaScript: it only does \"%s\" and \"%d\", but I have left space for it to be extended. It is useless to the OP, but other people who stumble across this thread coming from Google might benefit from it.

\n\n
function sprintf() {\n    var args = arguments,\n    string = args[0],\n    i = 1;\n    return string.replace(/%((%)|s|d)/g, function (m) {\n        // m is the matched format, e.g. %s, %d\n        var val = null;\n        if (m[2]) {\n            val = m[2];\n        } else {\n            val = args[i];\n            // A switch statement so that the formatter can be extended. Default is %s\n            switch (m) {\n                case '%d':\n                    val = parseFloat(val);\n                    if (isNaN(val)) {\n                        val = 0;\n                    }\n                    break;\n            }\n            i++;\n        }\n        return val;\n    });\n}\n
\n\n

Example:

\n\n
alert(sprintf('Latitude: %s, Longitude: %s, Count: %d', 41.847, -87.661, 'two'));\n// Expected output: Latitude: 41.847, Longitude: -87.661, Count: 0\n
\n\n

In contrast with similar solutions in previous replies, this one does all substitutions in one go, so it will not replace parts of previously replaced values.

\n", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f19", + "creator": "Dongdong Kong", + "createdAt": 1631240734000, + "text": "any idea how to suit for %02d?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae6", + "creator": "Spudley", + "createdAt": 1302870352000, + "text": "

The PHPJS project has written JavaScript implementations for many of PHP's functions. Since PHP's sprintf() function is basically the same as C's printf(), their JavaScript implementation of it should satisfy your needs.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92ae4", + "creator": "fearphage", + "createdAt": 1294862554000, + "text": "

Building on the previously suggested solutions:

\n\n
// First, checks if it isn't implemented yet.\nif (!String.prototype.format) {\n  String.prototype.format = function() {\n    var args = arguments;\n    return this.replace(/{(\\d+)}/g, function(match, number) { \n      return typeof args[number] != 'undefined'\n        ? args[number]\n        : match\n      ;\n    });\n  };\n}\n
\n\n

\"{0} is dead, but {1} is alive! {0} {2}\".format(\"ASP\", \"ASP.NET\")

\n\n

outputs

\n\n
\n

ASP is dead, but ASP.NET is alive! ASP {2}

\n
\n\n
\n\n

If you prefer not to modify String's prototype:

\n\n
if (!String.format) {\n  String.format = function(format) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return format.replace(/{(\\d+)}/g, function(match, number) { \n      return typeof args[number] != 'undefined'\n        ? args[number] \n        : match\n      ;\n    });\n  };\n}\n
\n\n

Gives you the much more familiar:

\n\n

String.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET');

\n\n

with the same result:

\n\n
\n

ASP is dead, but ASP.NET is alive! ASP {2}

\n
\n", + "upvotes": 2073, + "upvoterUsernames": [], + "downvotes": 591, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f1d", + "creator": "fserb", + "createdAt": 1298124960000, + "text": "the || trick doesn't work if args[number] is 0. Should do an explicit if() to see if (args[number] === undefined).", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f1f", + "creator": "Daan Mortier", + "createdAt": 1360967271000, + "text": "Great little function. The typeof check could be replaced with return number in args to make it even shorter.", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f21", + "creator": "avenmore", + "createdAt": 1371797763000, + "text": "Is there a way to make JSHint happy with the regex? "JSHint: Unescaped '{'."", + "upvotes": 3359, + "upvoterUsernames": [], + "downvotes": 3359, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f22", + "creator": "user2233706", + "createdAt": 1378664775000, + "text": "I think this is better than sprintf() for JS because it does basically the same thing and it is very small.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f24", + "creator": "Boris", + "createdAt": 1390930809000, + "text": "Of course, a better example would be String.format('{{0}} was replaced with {0}', replcmnt);.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f26", + "creator": "fearphage", + "createdAt": 1427318913000, + "text": "@dmasi Those are both valid suggestions for a much more strict environment. I won't be incorporating those, but I appreciate the suggestion.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92ae7", + "creator": "Mehmet Aydemir", + "createdAt": 1307981804000, + "text": "

arg function:

\n\n
/**\n * Qt stil arg()\n * var scr = \"<div id='%1' class='%2'></div>\".arg(\"mydiv\").arg(\"mydivClass\");\n */\nString.prototype.arg = function() {\n    var signIndex = this.indexOf(\"%\");\n    var result = this;\n    if (signIndex > -1 && arguments.length > 0) {\n        var argNumber = this.charAt(signIndex + 1);\n        var _arg = \"%\"+argNumber;\n        var argCount = this.split(_arg);\n        for (var itemIndex = 0; itemIndex < argCount.length; itemIndex++) {\n            result = result.replace(_arg, arguments[0]);\n        }\n    }\n    return result;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92ae8", + "creator": "Craig Stuntz", + "createdAt": 1311166584000, + "text": "

There is also Globalize.format in the jQuery Globalize project, the official globalization service for jQuery UI. IT's nice when you need culture-aware formatting.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aea", + "creator": "lior hakim", + "createdAt": 1323601189000, + "text": "

Very elegant:

\n\n
String.prototype.format = function (){\n    var args = arguments;\n    return this.replace(/\\{\\{|\\}\\}|\\{(\\d+)\\}/g, function (curlyBrack, index) {\n        return ((curlyBrack == \"{{\") ? \"{\" : ((curlyBrack == \"}}\") ? \"}\" : args[index]));\n    });\n};\n\n// Usage:\n\"{0}{1}\".format(\"{1}\", \"{0}\")\n
\n\n

Credit goes to (broken link) https://gist.github.com/0i0/1519811

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92ae9", + "creator": "rescdsk", + "createdAt": 1313158331000, + "text": "

Number Formatting in JavaScript

\n\n

I got to this question page hoping to find how to format numbers in JavaScript, without introducing yet another library. Here's what I've found:

\n\n

Rounding floating-point numbers

\n\n

The equivalent of sprintf(\"%.2f\", num) in JavaScript seems to be num.toFixed(2), which formats num to 2 decimal places, with rounding (but see @ars265's comment about Math.round below).

\n\n
(12.345).toFixed(2); // returns \"12.35\" (rounding!)\n(12.3).toFixed(2); // returns \"12.30\" (zero padding)\n
\n\n

Exponential form

\n\n

The equivalent of sprintf(\"%.2e\", num) is num.toExponential(2).

\n\n
(33333).toExponential(2); // \"3.33e+4\"\n
\n\n

Hexadecimal and other bases

\n\n

To print numbers in base B, try num.toString(B). JavaScript supports automatic conversion to and from bases 2 through 36 (in addition, some browsers have limited support for base64 encoding).

\n\n
(3735928559).toString(16); // to base 16: \"deadbeef\"\nparseInt(\"deadbeef\", 16); // from base 16: 3735928559\n
\n\n

Reference Pages

\n\n

Quick tutorial on JS number formatting

\n\n

Mozilla reference page for toFixed() (with links to toPrecision(), toExponential(), toLocaleString(), ...)

\n", + "upvotes": 589, + "upvoterUsernames": [], + "downvotes": 247, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a05082fcc3049e92f2b", + "creator": "rmobis", + "createdAt": 1353894804000, + "text": "Wouldn't it just be better to enclose the number literal in parenthesis, instead of leaving a weird white space there?", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f2d", + "creator": "rescdsk", + "createdAt": 1354397286000, + "text": "That would probably look better, true. But my goal there is just to point out the syntax error trap.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f2f", + "creator": "Peter Jaric", + "createdAt": 1367914321000, + "text": "@Raphael_ and @rescdsk: .. also works: 33333..toExponential(2);", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32a05082fcc3049e92f31", + "creator": "Jonathan", + "createdAt": 1471617170000, + "text": "Or (33333).toExponential(2)", + "upvotes": 5626, + "upvoterUsernames": [], + "downvotes": 5626, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92aeb", + "creator": "Tracker1", + "createdAt": 1338533065000, + "text": "

I have a slightly longer formatter for JavaScript here...

\n\n

You can do formatting several ways:

\n\n\n\n

Also, if you have say a ObjectBase.prototype.format (such as with DateJS) it will use that.

\n\n

Examples...

\n\n
var input = \"numbered args ({0}-{1}-{2}-{3})\";\nconsole.log(String.format(input, \"first\", 2, new Date()));\n//Outputs \"numbered args (first-2-Thu May 31 2012...Time)-{3})\"\n\nconsole.log(input.format(\"first\", 2, new Date()));\n//Outputs \"numbered args(first-2-Thu May 31 2012...Time)-{3})\"\n\nconsole.log(input.format(\n    \"object properties ({first}-{second}-{third:yyyy-MM-dd}-{fourth})\"\n    ,{\n        'first':'first'\n        ,'second':2\n        ,'third':new Date() //assumes Date.prototype.format method\n    }\n));\n//Outputs \"object properties (first-2-2012-05-31-{3})\"\n
\n\n

I've also aliased with .asFormat and have some detection in place in case there's already a string.format (such as with MS Ajax Toolkit (I hate that library).

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aec", + "creator": "moechofe", + "createdAt": 1348919612000, + "text": "

This one works with {0}, {1} and {}.

\n\n
String.prototype.format = function format()\n{                                                                                                               \n  var msg = this;\n  for(var i in arguments)\n    msg = msg.replace(/\\{\\}/,arguments[i]).replace(new RegExp('\\\\{'+i+'\\\\}','g'),arguments[i]);\n  return msg;\n}\n
\n", + "upvotes": 1970, + "upvoterUsernames": [], + "downvotes": 1970, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aed", + "creator": "jerone", + "createdAt": 1349855616000, + "text": "

I did not see the String.format variant:

\n\n
String.format = function (string) {\n    var args = Array.prototype.slice.call(arguments, 1, arguments.length);\n    return string.replace(/{(\\d+)}/g, function (match, number) {\n        return typeof args[number] != \"undefined\" ? args[number] : match;\n    });\n};\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aee", + "creator": "Peter", + "createdAt": 1349970003000, + "text": "

One very slightly different version, the one I prefer (this one uses {xxx} tokens rather than {0} numbered arguments, this is much more self-documenting and suits localization much better):

\n\n
String.prototype.format = function(tokens) {\n  var formatted = this;\n  for (var token in tokens)\n    if (tokens.hasOwnProperty(token))\n      formatted = formatted.replace(RegExp(\"{\" + token + \"}\", \"g\"), tokens[token]);\n  return formatted;\n};\n
\n\n

A variation would be:

\n\n
  var formatted = l(this);\n
\n\n

that calls an l() localization function first.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aef", + "creator": "Rtlprmft", + "createdAt": 1353238350000, + "text": "

I want to share my solution for the 'problem'. I haven't re-invented the wheel but tries to find a solution based on what JavaScript already does. The advantage is, that you get all implicit conversions for free. Setting the prototype property $ of String gives a very nice and compact syntax (see examples below). It is maybe not the most efficient way, but in most cases dealing with output it does not have to be super optimized.

\n\n
String.form = function(str, arr) {\n    var i = -1;\n    function callback(exp, p0, p1, p2, p3, p4) {\n        if (exp=='%%') return '%';\n        if (arr[++i]===undefined) return undefined;\n        exp  = p2 ? parseInt(p2.substr(1)) : undefined;\n        var base = p3 ? parseInt(p3.substr(1)) : undefined;\n        var val;\n        switch (p4) {\n            case 's': val = arr[i]; break;\n            case 'c': val = arr[i][0]; break;\n            case 'f': val = parseFloat(arr[i]).toFixed(exp); break;\n            case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;\n            case 'e': val = parseFloat(arr[i]).toExponential(exp); break;\n            case 'x': val = parseInt(arr[i]).toString(base?base:16); break;\n            case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;\n        }\n        val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);\n        var sz = parseInt(p1); /* padding size */\n        var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */\n        while (val.length<sz) val = p0 !== undefined ? val+ch : ch+val; /* isminus? */\n       return val;\n    }\n    var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;\n    return str.replace(regex, callback);\n}\n\nString.prototype.$ = function() {\n    return String.form(this, Array.prototype.slice.call(arguments));\n}\n
\n\n

Here are a few examples:

\n\n
String.format(\"%s %s\", [ \"This is a string\", 11 ])\nconsole.log(\"%s %s\".$(\"This is a string\", 11))\nvar arr = [ \"12.3\", 13.6 ]; console.log(\"Array: %s\".$(arr));\nvar obj = { test:\"test\", id:12 }; console.log(\"Object: %s\".$(obj));\nconsole.log(\"%c\", \"Test\");\nconsole.log(\"%5d\".$(12)); // '   12'\nconsole.log(\"%05d\".$(12)); // '00012'\nconsole.log(\"%-5d\".$(12)); // '12   '\nconsole.log(\"%5.2d\".$(123)); // '  120'\nconsole.log(\"%5.2f\".$(1.1)); // ' 1.10'\nconsole.log(\"%10.2e\".$(1.1)); // '   1.10e+0'\nconsole.log(\"%5.3p\".$(1.12345)); // ' 1.12'\nconsole.log(\"%5x\".$(45054)); // ' affe'\nconsole.log(\"%20#2x\".$(\"45054\")); // '    1010111111111110'\nconsole.log(\"%6#2d\".$(\"111\")); // '     7'\nconsole.log(\"%6#16d\".$(\"affe\")); // ' 45054'\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af0", + "creator": "hienbt88", + "createdAt": 1354604930000, + "text": "

You can use this function

\n\n
            String.prototype.format = function (args) {\n            var str = this;\n            return str.replace(String.prototype.format.regex, function(item) {\n                var intVal = parseInt(item.substring(1, item.length - 1));\n                var replace;\n                if (intVal >= 0) {\n                    replace = args[intVal];\n                } else if (intVal === -1) {\n                    replace = \"{\";\n                } else if (intVal === -2) {\n                    replace = \"}\";\n                } else {\n                    replace = \"\";\n                }\n                return replace;\n            });\n        };\n        String.prototype.format.regex = new RegExp(\"{-?[0-9]+}\", \"g\");\n\n        // Sample usage.\n        var str = \"She {1} {0}{2} by the {0}{3}. {-1}^_^{-2}\";\n        str = str.format([\"sea\", \"sells\", \"shells\", \"shore\"]);\n        alert(str);\n
\n", + "upvotes": 644, + "upvoterUsernames": [], + "downvotes": 644, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af1", + "creator": "Raymond Powell", + "createdAt": 1359697212000, + "text": "

For use with jQuery.ajax() success functions. Pass only a single argument and string replace with the properties of that object as {propertyName}:

\n\n
String.prototype.format = function () {\n    var formatted = this;\n    for (var prop in arguments[0]) {\n        var regexp = new RegExp('\\\\{' + prop + '\\\\}', 'gi');\n        formatted = formatted.replace(regexp, arguments[0][prop]);\n    }\n    return formatted;\n};\n
\n\n

Example:

\n\n
var userInfo = (\"Email: {Email} - Phone: {Phone}\").format({ Email: \"someone@somewhere.com\", Phone: \"123-123-1234\" });\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af2", + "creator": "krichard", + "createdAt": 1362425219000, + "text": "

With sprintf.js in place - one can make a nifty little format-thingy

\n\n
String.prototype.format = function(){\n    var _args = arguments \n    Array.prototype.unshift.apply(_args,[this])\n    return sprintf.apply(undefined,_args)\n}   \n// this gives you:\n\"{%1$s}{%2$s}\".format(\"1\", \"0\")\n// {1}{0}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af3", + "creator": "Braden Best", + "createdAt": 1362948582000, + "text": "

Adding to zippoxer's answer, I use this function:

\n\n
String.prototype.format = function () {\n    var a = this, b;\n    for (b in arguments) {\n        a = a.replace(/%[a-z]/, arguments[b]);\n    }\n    return a; // Make chainable\n};\n\nvar s = 'Hello %s The magic number is %d.';\ns.format('world!', 12); // Hello World! The magic number is 12.\n
\n\n

I also have a non-prototype version which I use more often for its Java-like syntax:

\n\n
function format() {\n    var a, b, c;\n    a = arguments[0];\n    b = [];\n    for(c = 1; c < arguments.length; c++){\n        b.push(arguments[c]);\n    }\n    for (c in b) {\n        a = a.replace(/%[a-z]/, b[c]);\n    }\n    return a;\n}\nformat('%d ducks, 55 %s', 12, 'cats'); // 12 ducks, 55 cats\n
\n\n

ES 2015 update

\n\n

All the cool new stuff in ES 2015 makes this a lot easier:

\n\n
function format(fmt, ...args){\n    return fmt\n        .split(\"%%\")\n        .reduce((aggregate, chunk, i) =>\n            aggregate + chunk + (args[i] || \"\"), \"\");\n}\n\nformat(\"Hello %%! I ate %% apples today.\", \"World\", 44);\n// \"Hello World, I ate 44 apples today.\"\n
\n\n

I figured that since this, like the older ones, doesn't actually parse the letters, it might as well just use a single token %%. This has the benefit of being obvious and not making it difficult to use a single %. However, if you need %% for some reason, you would need to replace it with itself:

\n\n
format(\"I love percentage signs! %%\", \"%%\");\n// \"I love percentage signs! %%\"\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a06082fcc3049e92f36", + "creator": "Nick", + "createdAt": 1498336912000, + "text": "this answer was great for a quick copy paste into an existing function. No require no downloads etc.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92af4", + "creator": "Tengiz", + "createdAt": 1364829538000, + "text": "

bobjs can do this:

\n\n
var sFormat = \"My name is {0} and I am {1} years old.\"; \nvar result = bob.string.formatString(sFormat, \"Bob\", 29); \nconsole.log(result); \n//output: \n//========== \n// My name is Bob and I am 29 years old. \n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af5", + "creator": "user2240578", + "createdAt": 1366831096000, + "text": "
String.prototype.repeat = function(n) { \n    return new Array(++n).join(this); \n};\n\nString.prototype.pad = function(requiredLength, paddingStr, paddingType) {    \n    var n = requiredLength - this.length; \n\n    if (n) {\n        paddingType = paddingType ? paddingType.toLowerCase() : '';\n        paddingStr = paddingStr || ' ';\n        paddingStr = paddingStr.repeat( Math.ceil(n / paddingStr.length) ).substr(0, n);\n\n        if (paddingType == 'both') {\n            n /= 2;\n            return paddingStr.substr( 0, Math.ceil(n) ) + this + paddingStr.substr( 0, Math.floor(n) );\n        }   \n\n        if (paddingType == 'left') {\n            return paddingStr + this;\n        }\n\n        return this + paddingStr;\n    } \n\n    return this; \n}; \n\n// синтаксис аналогичен printf\n// 'Привет, %s!'.format('мир') -> \"Привет, мир!\"\n// '%.1s.%.1s. %s'.format('Иван', 'Иванович', 'Иванов') -> \"И.И. Иванов\"\nString.prototype.format = function() {\n    var i = 0, \n        params = arguments;\n\n    return this.replace(/%(?:%|(?:(|[+-]+)(|0|'.+?)([1-9]\\d*)?(?:\\.([1-9]\\d*))?)?(s|d|f))/g, function(match, sign, padding, width, precision, type) {\n        if (match == '%%') { \n            return '%'; \n        }\n\n        var v = params[i++];\n\n        if (type == 'd') { \n            v = Math.round(v); \n        }\n        else if (type == 'f') {\n            v = v.toFixed(precision ? precision : 6);\n        }\n\n        if (/\\+/.test(sign) && v > 0) {\n            v = '+' + v;\n        }\n\n        v += '';\n\n        if (type != 'f' && precision) {\n            v = v.substr(0, precision);\n        }\n\n        if (width) {\n            v = v.pad(width, padding == '' ? ' ' : padding[0] == \"'\" ? padding.substr(1) : padding, /-/.test(sign) ? 'right' : 'left'); \n        }\n\n        return v;\n    });\n};\n\n// this.name = 'Вася';\n// console.log( 'Привет, ${name}!'.template(this) );\n// \"Привет, Вася!\"\nString.prototype.template = function(context) {\n    return this.replace(/\\$\\{(.*?)\\}/g, function(match, name) {\n        return context[name];\n    });\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af6", + "creator": "eces", + "createdAt": 1369823298000, + "text": "

This is an implementation of https://stackoverflow.com/a/4673436/1258486 for CoffeeScript.

\n\n

https://gist.github.com/eces/5669361

\n\n
if String.prototype.format is undefined\n  String.prototype.format = () ->\n    _arguments = arguments\n    this.replace /{(\\d+)}/g, (match, number) ->\n      if typeof _arguments[number] isnt 'undefined' then _arguments[number] else match\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af7", + "creator": "qbolec", + "createdAt": 1375380860000, + "text": "

I needed a function which could format a price (given in cents) in a way preferred by the user, and the tricky part is that the format is specified by the user -- and I do not expect my users to understand printf-like syntax, or regexps, etc.\nMy solution is somewhat similar to that used in Basic, so the user just marks with # places for digits, for example:

\n\n
simple_format(1234567,\"$ ###,###,###.##\")\n\"$ 12,345.67\"\nsimple_format(1234567,\"### ### ###,## pln\")\n\"12 345,67 pln\"\n
\n\n

I believe this is quite easy to understand by user, and quite easy to implement:

\n\n
function simple_format(integer,format){\n  var text = \"\";\n  for(var i=format.length;i--;){\n    if(format[i]=='#'){\n      text = (integer%10) + text;\n      integer=Math.floor(integer/10);\n      if(integer==0){\n        return format.substr(0,i).replace(/#(.*#)?/,\"\")+text;\n      }\n    }else{\n      text = format[i] + text;\n    }\n  }\n  return text;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af9", + "creator": "redestructa", + "createdAt": 1395926355000, + "text": "

I use this one:

\n\n
String.prototype.format = function() {\n    var newStr = this, i = 0;\n    while (/%s/.test(newStr))\n        newStr = newStr.replace(\"%s\", arguments[i++])\n\n    return newStr;\n}\n
\n\n

Then I call it:

\n\n
\"<h1>%s</h1><p>%s</p>\".format(\"Header\", \"Just a test!\");\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92af8", + "creator": "Gabriel Nahmias", + "createdAt": 1376490015000, + "text": "

It's funny because Stack Overflow actually has their own formatting function for the String prototype called formatUnicorn. Try it! Go into the console and type something like:

\n
"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective: "OK"});\n
\n

\"Firebug\"

\n

You get this output:

\n

Hello, Gabriel, are you feeling OK?

\n

You can use objects, arrays, and strings as arguments! I got its code and reworked it to produce a new version of String.prototype.format:

\n
String.prototype.formatUnicorn = String.prototype.formatUnicorn ||\nfunction () {\n    "use strict";\n    var str = this.toString();\n    if (arguments.length) {\n        var t = typeof arguments[0];\n        var key;\n        var args = ("string" === t || "number" === t) ?\n            Array.prototype.slice.call(arguments)\n            : arguments[0];\n\n        for (key in args) {\n            str = str.replace(new RegExp("\\\\{" + key + "\\\\}", "gi"), args[key]);\n        }\n    }\n\n    return str;\n};\n
\n

Note the clever Array.prototype.slice.call(arguments) call -- that means if you throw in arguments that are strings or numbers, not a single JSON-style object, you get C#'s String.Format behavior almost exactly.

\n
"a{0}bcd{1}ef".formatUnicorn("FOO", "BAR"); // yields "aFOObcdBARef"\n
\n

That's because Array's slice will force whatever's in arguments into an Array, whether it was originally or not, and the key will be the index (0, 1, 2...) of each array element coerced into a string (eg, "0", so "\\\\{0\\\\}" for your first regexp pattern).

\n

Neat.

\n", + "upvotes": 879, + "upvoterUsernames": [], + "downvotes": 271, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a06082fcc3049e92f3c", + "creator": "Sneakyness", + "createdAt": 1390003329000, + "text": "It's pretty cool to answer a question on stackoverflow with code from stackoverflow, +1", + "upvotes": 899, + "upvoterUsernames": [], + "downvotes": 389, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f3d", + "creator": "James Manning", + "createdAt": 1397407900000, + "text": "Anyone understand why they're using a regex for the replace instead of just a string?", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 122, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f3f", + "creator": "RainChen", + "createdAt": 1433233526000, + "text": "I think there is a typo: var args = typeof arguments[0] should be var arg = typeof arguments[0]", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f41", + "creator": "Dinei", + "createdAt": 1439477599000, + "text": "@RainChen there is no typo. He are just reusing the same variable to store the typeof and then, in the next line, the arguments.", + "upvotes": 418, + "upvoterUsernames": [], + "downvotes": 418, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f43", + "creator": "toddmo", + "createdAt": 1473273235000, + "text": "This would be cooler if you could just put javascript expressions inside the curly braces and dispense with the names. c# 6.0 has this now.", + "upvotes": 3063, + "upvoterUsernames": [], + "downvotes": 3063, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f45", + "creator": "sam hocevar", + "createdAt": 1484564577000, + "text": "This seems awfully fragile, to be honest. What happens for instance if name is "blah {adjective} blah"?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f47", + "creator": "mwardm", + "createdAt": 1510155944000, + "text": ""If I had ever learnt, I should have been a great proficient." - Lady Catherine de Bourgh. :-)", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f49", + "creator": "A1rPun", + "createdAt": 1558009174000, + "text": "Too bad ReferenceError: formatUnicorn is not defined", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f32a06082fcc3049e92f4a", + "creator": "Sarath S Menon", + "createdAt": 1582791220000, + "text": "Why can't we use 'arguments.slice()' rather than Array.prototype.slice.call(arguments)?", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 67, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92afb", + "creator": "Thiago Mata", + "createdAt": 1399467822000, + "text": "

I have a solution very close to Peter's, but it deals with number and object case.

\n\n
if (!String.prototype.format) {\n  String.prototype.format = function() {\n    var args;\n    args = arguments;\n    if (args.length === 1 && args[0] !== null && typeof args[0] === 'object') {\n      args = args[0];\n    }\n    return this.replace(/{([^}]*)}/g, function(match, key) {\n      return (typeof args[key] !== \"undefined\" ? args[key] : match);\n    });\n  };\n}\n
\n\n

Maybe it could be even better to deal with the all deeps cases, but for my needs this is just fine.

\n\n
\"This is an example from {name}\".format({name:\"Blaine\"});\n\"This is an example from {0}\".format(\"Blaine\");\n
\n\n

PS: This function is very cool if you are using translations in templates frameworks like AngularJS:

\n\n
<h1> {{('hello-message'|translate).format(user)}} <h1>\n<h1> {{('hello-by-name'|translate).format( user ? user.name : 'You' )}} <h1>\n
\n\n

Where the en.json is something like

\n\n
{\n    \"hello-message\": \"Hello {name}, welcome.\",\n    \"hello-by-name\": \"Hello {0}, welcome.\"\n}\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a07082fcc3049e92f4d", + "creator": "rawiro", + "createdAt": 1412151978000, + "text": "the [^}] part in the regexp is unnecesary.. use {(.*?)} instead, or better {([\\s\\S]*?)} to match newline too.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92afa", + "creator": "George Eracleous", + "createdAt": 1397573943000, + "text": "

For Node.js users there is util.format which has printf-like functionality:

\n
util.format("%s world", "Hello")\n
\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a07082fcc3049e92f50", + "creator": "Max Krohn", + "createdAt": 1398345403000, + "text": "This doesn't support %x as of Node v0.10.26", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a07082fcc3049e92f52", + "creator": "FGM", + "createdAt": 1546608545000, + "text": "Doesn't support width and alignment modifiers either (e.g. %-20s %5.2f)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a07082fcc3049e92f54", + "creator": "Daniel Viglione", + "createdAt": 1586904645000, + "text": "I had to scroll all the way down the page to see this useful answer.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92afc", + "creator": "Bovard", + "createdAt": 1406232327000, + "text": "

I didn't see pyformat in the list so I thought I'd throw it in:

\n\n
console.log(pyformat( 'The {} {} jumped over the {}'\n                , ['brown' ,'fox' ,'foobar']\n                ))\nconsole.log(pyformat('The {0} {1} jumped over the {1}'\n                , ['brown' ,'fox' ,'foobar']\n                ))\nconsole.log(pyformat('The {color} {animal} jumped over the {thing}'\n                , [] ,{color: 'brown' ,animal: 'fox' ,thing: 'foobaz'}\n                ))\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92afd", + "creator": "Rob Audenaerde", + "createdAt": 1421002856000, + "text": "

I started porting the Java String.format (actually new Formatter().format()) to javascript. The initial version is available at:

\n\n

https://github.com/RobAu/javascript.string.format

\n\n

You can simple add the javscript and call StringFormat.format(\"%.2f\", [2.4]); etc.

\n\n

Please note it is NOT finished yet, but feedback is welcome :)

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a07082fcc3049e92f57", + "creator": "Dragas", + "createdAt": 1529215023000, + "text": "So why add it as a possible solution?", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + }, + { + "_id": "62f32a07082fcc3049e92f59", + "creator": "Rob Audenaerde", + "createdAt": 1529243280000, + "text": ""My basic requirement is a thousand separator format for numbers for now" - it handles that just fine", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92afe", + "creator": "Afshin Mehrabani", + "createdAt": 1421745357000, + "text": "

Just in case someone needs a function to prevent polluting global scope, here is the function that does the same:

\n\n
  function _format (str, arr) {\n    return str.replace(/{(\\d+)}/g, function (match, number) {\n      return typeof arr[number] != 'undefined' ? arr[number] : match;\n    });\n  };\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92aff", + "creator": "Monarch Wadia", + "createdAt": 1435075691000, + "text": "

I'm surprised no one used reduce, this is a native concise and powerful JavaScript function.

\n\n

ES6 (EcmaScript2015)

\n\n

\r\n
\r\n
String.prototype.format = function() {\r\n  return [...arguments].reduce((p,c) => p.replace(/%s/,c), this);\r\n};\r\n\r\nconsole.log('Is that a %s or a %s?... No, it\\'s %s!'.format('plane', 'bird', 'SOman'));
\r\n
\r\n
\r\n

\n\n

< ES6

\n\n
function interpolate(theString, argumentArray) {\n    var regex = /%s/;\n    var _r=function(p,c){return p.replace(regex,c);}\n    return argumentArray.reduce(_r, theString);\n}\n\ninterpolate(\"%s, %s and %s\", [\"Me\", \"myself\", \"I\"]); // \"Me, myself and I\"\n
\n\n

How it works:

\n\n
\n

reduce applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.

\n
\n\n

\r\n
\r\n
var _r= function(p,c){return p.replace(/%s/,c)};\r\n\r\nconsole.log(\r\n  [\"a\", \"b\", \"c\"].reduce(_r, \"[%s], [%s] and [%s]\") + '\\n',\r\n  [1, 2, 3].reduce(_r, \"%s+%s=%s\") + '\\n',\r\n  [\"cool\", 1337, \"stuff\"].reduce(_r, \"%s %s %s\")\r\n);
\r\n
\r\n
\r\n

\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a07082fcc3049e92f5c", + "creator": "dtasev", + "createdAt": 1519300792000, + "text": "And here is another one using ES6, in one line: (...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92b00", + "creator": "Kim", + "createdAt": 1440500583000, + "text": "

From ES6 on you could use template strings:

\n\n
let soMany = 10;\nconsole.log(`This is ${soMany} times easier!`);\n// \"This is 10 times easier!\n
\n\n

Be aware that template strings are surrounded by backticks ` instead of (single) quotes.

\n\n

For further information:

\n\n

https://developers.google.com/web/updates/2015/01/ES6-Template-Strings

\n\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings

\n\n

Note:\nCheck the mozilla-site to find a list of supported browsers.

\n", + "upvotes": 486, + "upvoterUsernames": [], + "downvotes": 197, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a07082fcc3049e92f5f", + "creator": "user993954", + "createdAt": 1479218794000, + "text": "this solution won't work for format string passed in variable (from server for example)", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92b01", + "creator": "Evgeny Gerbut", + "createdAt": 1449584302000, + "text": "

For basic formatting:

\n\n
var template = jQuery.validator.format(\"{0} is not a valid value\");\nvar result = template(\"abc\");\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b02", + "creator": "Lars Gyrup Brink Nielsen", + "createdAt": 1451772433000, + "text": "
/**\n * Format string by replacing placeholders with value from element with\n * corresponsing index in `replacementArray`.\n * Replaces are made simultaneously, so that replacement values like\n * '{1}' will not mess up the function.\n *\n * Example 1:\n * ('{2} {1} {0}', ['three', 'two' ,'one']) -> 'one two three'\n *\n * Example 2:\n * ('{0}{1}', ['{1}', '{0}']) -> '{1}{0}'\n */\nfunction stringFormat(formatString, replacementArray) {\n    return formatString.replace(\n        /\\{(\\d+)\\}/g, // Matches placeholders, e.g. '{1}'\n        function formatStringReplacer(match, placeholderIndex) {\n            // Convert String to Number\n            placeholderIndex = Number(placeholderIndex);\n\n            // Make sure that index is within replacement array bounds\n            if (placeholderIndex < 0 ||\n                placeholderIndex > replacementArray.length - 1\n            ) {\n                return placeholderIndex;\n            }\n\n            // Replace placeholder with value from replacement array\n            return replacementArray[placeholderIndex];\n        }\n    );\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b04", + "creator": "Yevgeniy Afanasyev", + "createdAt": 1524555841000, + "text": "

Using Lodash you can get template functionality:

\n\n

Use the ES template literal delimiter as an \"interpolate\" delimiter.\nDisable support by replacing the \"interpolate\" delimiter.

\n\n
var compiled = _.template('hello ${ user }!');\ncompiled({ 'user': 'pebbles' });\n// => 'hello pebbles!\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b03", + "creator": "AnyWhichWay", + "createdAt": 1453645595000, + "text": "

This is not an exact duplicate of sprintf; however, it is similar and more powerful: https://github.com/anywhichway/stringformatter

\n\n

Format expressions using this library take the form of embedded Javascript objects, e.g.

\n\n
format(\"I have {number: {currency: \"$\", precision:2}}.\",50.2); \n
\n\n

will return \"I have $50.20.\".

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 101, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b05", + "creator": "NISHANK KUMAR", + "createdAt": 1557491443000, + "text": "

\r\n
\r\n
String.prototype.format = function(){\r\n    var final = String(this);\r\n    for(let i=0; i<arguments.length;i++){\r\n        final = final.replace(`%s${i+1}`, arguments[i])\r\n    }\r\n    return final || ''\r\n}\r\n\r\nconsole.log((\"hello %s2 how %s3 you %s1\").format('hi', 'hello', 'how'));
\r\n
<h1 id=\"text\">\r\n   \r\n</h1>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b06", + "creator": "Murtaza Hussain", + "createdAt": 1560413068000, + "text": "

We can use a simple lightweight String.Format string operation library for Typescript.

\n\n

String.Format():

\n\n
var id = image.GetId()\nString.Format(\"image_{0}.jpg\", id)\noutput: \"image_2db5da20-1c5d-4f1a-8fd4-b41e34c8c5b5.jpg\";\n
\n\n

String Format for specifiers:

\n\n
var value = String.Format(\"{0:L}\", \"APPLE\"); //output \"apple\"\n\nvalue = String.Format(\"{0:U}\", \"apple\"); // output \"APPLE\"\n\nvalue = String.Format(\"{0:d}\", \"2017-01-23 00:00\"); //output \"23.01.2017\"\n\n\nvalue = String.Format(\"{0:s}\", \"21.03.2017 22:15:01\") //output \"2017-03-21T22:15:01\"\n\nvalue = String.Format(\"{0:n}\", 1000000);\n//output \"1.000.000\"\n\nvalue = String.Format(\"{0:00}\", 1);\n//output \"01\"\n
\n\n

String Format for Objects including specifiers:

\n\n
var fruit = new Fruit();\nfruit.type = \"apple\";\nfruit.color = \"RED\";\nfruit.shippingDate = new Date(2018, 1, 1);\nfruit.amount = 10000;\n\nString.Format(\"the {type:U} is {color:L} shipped on {shippingDate:s} with an amount of {amount:n}\", fruit);\n// output: the APPLE is red shipped on 2018-01-01 with an amount of 10.000\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b07", + "creator": "Mahendra Hirapra", + "createdAt": 1576475476000, + "text": "
export function stringFormate (str: string, ...args: string[]) {\n     return args.reduce((acc, curr, i) => acc.replace(new RegExp(\"\\\\{\" + i + \"\\\\}\", 'g'), curr), str);\n}\n
\n", + "upvotes": 1820, + "upvoterUsernames": [], + "downvotes": 1820, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b08", + "creator": "TheMisir", + "createdAt": 1583274545000, + "text": "

In typescript create a file named format.ts and import it whatever you need to use formatting.

\n\n
// contents of format.ts\n\ninterface String {\n  format(...args: any[]): string;\n}\n\nif (!String.prototype.format) {\n  String.prototype.format = function() {\n    let a = this;\n    let b: any;\n    // tslint:disable-next-line: forin\n    for (b in arguments) {\n      a = a.replace(/%[a-z]/, arguments[b]);\n    }\n    return a;\n  };\n}\n
\n\n

To format string use this code:

\n\n
import './format';\n\nconsole.log('Hello, %s!'.format('World'));\n
\n\n

Example

\n\n

\r\n
\r\n
String.prototype.format = function() {\r\n  let a = this;\r\n  let b;\r\n  for (b in arguments) {\r\n    a = a.replace(/%[a-z]/, arguments[b]);\r\n  }\r\n  return a;\r\n};\r\n\r\nconsole.log('Hello, %s!'.format('World'));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b09", + "creator": "Lucas Breitembach", + "createdAt": 1584985619000, + "text": "

another suggestion is you use the string template:

\n\n
const getPathDadosCidades = (id: string) =>  `/clientes/${id}`\n\nconst getPathDadosCidades = (id: string, role: string) =>  `/clientes/${id}/roles/${role}`\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a08082fcc3049e92f67", + "creator": "David Schmitt", + "createdAt": 1619166931000, + "text": "thanks, the idea of putting this into a lambda just saved me a lot of faff!", + "upvotes": 696, + "upvoterUsernames": [], + "downvotes": 696, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92b0a", + "creator": "user7090116", + "createdAt": 1599061795000, + "text": "

Not the most recommended function in the world, but it works.

\n

If you need sprintf, just copy & paste this same function and change return console.log(sb) to just return sb.

\n

\r\n
\r\n
printf = function(s, /*args...*/) {\n    a = arguments;\n    al = a.length;\n    \n    if (al <= 1) return -2;\n    if (al >= 2 && s.toLowerCase().search(/%[a-z]/) == -1) return -1;\n\n    sb = s;\n    for (i = 1; i <= al - 1; i++) {\n        sb = sb.replace(/%[a-z]/, a[i]);\n    }\n\n    return console.log(sb);\n}\n\nvar someString = \"Hello %s\\nIt's %s:%s %s now.\\nThe day is %s\\n\";\nprintf(someString, \"StackOverflowUser\", \"5\", \"48\", \"PM\", \"beautiful\");
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b0c", + "creator": "GHosT", + "createdAt": 1602818441000, + "text": "

sprintf() function analog in JavaScript as Vue filter and String.prototype.format() extension:

\n
/**\n * Returns a formatted string.\n *\n * @param template\n * @param values\n * @return string\n */\nString.format = function (template, ...values) {\n    let i = -1;\n\n    function callback(exp, p0, p1, p2, p3, p4) {\n        if (exp === '%%') return '%';\n        if (values[++i] === undefined) return undefined;\n\n        exp = p2 ? parseInt(p2.substr(1)) : undefined;\n\n        let base = p3 ? parseInt(p3.substr(1)) : undefined;\n        let val;\n\n        switch (p4) {\n            case 's': val = values[i]; break;\n            case 'c': val = values[i][0]; break;\n            case 'f': val = parseFloat(values[i]).toFixed(exp); break;\n            case 'p': val = parseFloat(values[i]).toPrecision(exp); break;\n            case 'e': val = parseFloat(values[i]).toExponential(exp); break;\n            case 'x': val = parseInt(values[i]).toString(base ? base : 16); break;\n            case 'd': val = parseFloat(parseInt(values[i], base ? base : 10).toPrecision(exp)).toFixed(0); break;\n        }\n        val = typeof (val) == 'object' ? JSON.stringify(val) : val.toString(base);\n        let sz = parseInt(p1); /* padding size */\n        let ch = p1 && p1[0] === '0' ? '0' : ' '; /* isnull? */\n\n        while (val.length < sz) val = p0 !== undefined ? val + ch : ch + val; /* isminus? */\n\n        return val;\n    }\n\n    let regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;\n\n    return template.replace(regex, callback);\n}\n\nString.prototype.format = function() {\n    return String.format(this, ...arguments);\n}\n\nconst StringFormat = {\n    install: (Vue, options) => {\n        Vue.filter('format', function () {\n            return String.format(...arguments);\n        });\n    },\n};\n\nexport default StringFormat;\n
\n

Original answer: JavaScript equivalent to printf/String.Format

\n", + "upvotes": 2472, + "upvoterUsernames": [], + "downvotes": 2472, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b0b", + "creator": "Nero", + "createdAt": 1600413559000, + "text": "

if you just need to format a string with %s specifier only

\n
function _sprintf(message){\n    const regexp = RegExp('%s','g');\n    let match;\n    let index = 1;\n    while((match = regexp.exec(message)) !== null) {\n        let replacement = arguments[index];\n        if (replacement) {\n            let messageToArray = message.split('');\n            messageToArray.splice(match.index, regexp.lastIndex - match.index, replacement);\n            message = messageToArray.join('');\n            index++;\n        } else {\n            break;\n        }\n    }\n\n    return message;\n}\n\n_sprintf("my name is %s, my age is %s", "bob", 50); // my name is bob, my age is 50\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b0e", + "creator": "ßiansor Å. Ålmerol", + "createdAt": 1624039583000, + "text": "

Right now, there is a package called locutus which translate the functions of other languages to Javascript such as php, python, ruby etc.

\n
const printf = require('locutus/php/strings/printf')\nprintf('Hello world');\n
\n

You can try this playground codesandbox

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a08082fcc3049e92f6d", + "creator": "Param Siddharth", + "createdAt": 1627116573000, + "text": "I found a bug and contributed a fix. It should work perfectly now. Thank you for mentioning!", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92b0d", + "creator": "Alex Povolotsky", + "createdAt": 1615540897000, + "text": "

If you need a printf, use printf

\n

Looks like 90% of commenters never used printf with more complex format than just %d. I wonder how do they output, for example, money values?

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b10", + "creator": "Satish Chandra Gupta", + "createdAt": 1635229864000, + "text": "

3 different ways to format javascript string

\n

There are 3 different ways to format a string by replacing placeholders with the variable value.

\n
    \n
  1. Using template literal (backticks ``)

    \n

    \r\n
    \r\n
    let name = 'John';\nlet age = 30;\n// using backticks\nconsole.log(`${name} is ${age} years old.`);\n// John is 30 years old.
    \r\n
    \r\n
    \r\n

    \n
  2. \n
  3. Using concatenation

    \n
  4. \n
\n

\r\n
\r\n
let name = 'John';\nlet age = 30;\n// using concatenation\nconsole.log(name + ' is ' + age + ' years old.');\n// John is 30 years old.
\r\n
\r\n
\r\n

\n
    \n
  1. Creating own format function
  2. \n
\n

\r\n
\r\n
String.prototype.format = function () {\n  var args = arguments;\n  return this.replace(/{([0-9]+)}/g, function (match, index) {\n    // check if the argument is there\n    return typeof args[index] == 'undefined' ? match : args[index];\n  });\n};\n\n\nconsole.log('{0} is {1} years old.'.format('John', 30));
\r\n
\r\n
\r\n

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a08082fcc3049e92f71", + "creator": "Amila Senadheera", + "createdAt": 1637305539000, + "text": "Thanks! 3rd option suits for my case.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a08082fcc3049e92f73", + "creator": "Prid", + "createdAt": 1656524388000, + "text": "3rd option is the only one that allows variables to be "injected" AFTER declaring the string.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92b0f", + "creator": "Sean Morris", + "createdAt": 1630926048000, + "text": "

Ok, so first we'll set up some variables to use:

\n
    const date = new Date();\n    \n    const locale = 'en-us';\n    \n    const wDay   = date.toLocaleString(locale, {weekday: 'short'});\n    const month  = date.toLocaleString(locale, {month: 'long'});\n    const year   = date.toLocaleString(locale, {year: 'numeric'});\n    const minute = date.toLocaleString(locale, {minute: 'numeric'});\n    const [hour, ap] = date.toLocaleString(locale, {hour: 'numeric', hour12:true}).split(' ');\n    \n    let mDay = date.toLocaleString(locale, {day: 'numeric'});\n    \n    switch(mDay % 10)\n    {\n        case 1:  mDay += 'st'; break;\n        case 2:  mDay += 'nd'; break;\n        case 3:  mDay += 'rd'; break;\n        default: mDay += 'th'; break;\n    }\n
\n

Now that we've got all that, we can format a string like so:

\n
    const formatter = (...a) => `${a[0]}, the ${a[1]} of ${a[2]} ${a[3]} at ${a[4]}:${a[5]} ${a[6]}`;\n    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);\n
\n

We could even use named paramaters for the "formatter" function:

\n
    const formatter = (wDay, mDay, month, year, hour, minute, ap) => `${wDay}, the ${mDay} of ${month} ${year} at ${hour}:${minute} ${ap}`;\n    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);\n
\n

If you'll notice, the JS templates above are both the results of callbacks. If the entire piece of code above were encapsulated within a function that was expected to return a formatted date, it would not be hard to imagine how to construct an arbitrary "formatter" function in the same manner, that could be passed in from outside.

\n

tl;dr you can re-use template literals if you put them inside callbacks and use the args as the replacements.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b12", + "creator": "Mkoes", + "createdAt": 1657948759000, + "text": "

Modified code of old answer https://stackoverflow.com/a/18234317/19531844 much more efficient (without slow RegExp) and shorter

\n
String.prototype.formatUnicorn = function () {\n    let str = this.toString();\n    if(!arguments.length) {\n        return;\n    };\n    const [args] = arguments;\n    for (const key of Object.keys(args)) {\n        str = str.replaceAll(`{${key}}`, args[key]);\n    };\n    return str;\n};\n
\n

usage:

\n
"{test} {test_2} {test}".formatUnicorn({"test": "hello", "test_2": "world"}); // yields hello world hello\n
\n

benchmark between new and old: https://jsben.ch/BRovx

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92b11", + "creator": "Jens Törnell", + "createdAt": 1644405868000, + "text": "

I use the template literal approach, like below:

\n
export const messages = {\n  foo: (arg1, arg2) => `Hello ${arg1} ${arg2}`,\n  bar: (arg1) => `Hello ${arg1}`,\n}\n
\n

From the file:

\n
console.log(messages.foo('Bar', 'World'))\nconsole.log(messages.bar('Foo'))\n
\n", + "upvotes": 447, + "upvoterUsernames": [], + "downvotes": 447, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92adb", + "creator": "Gumbo", + "createdAt": 1236171322000, + "text": "

Current JavaScript

\n

From ES6 on you could use template strings:

\n
let soMany = 10;\nconsole.log(`This is ${soMany} times easier!`);\n// "This is 10 times easier!\n
\n

See Kim's answer below for details.

\n
\n

Older answer

\n

Try sprintf() for JavaScript.

\n
\n

If you really want to do a simple format method on your own, don’t do the replacements successively but do them simultaneously.

\n

Because most of the other proposals that are mentioned fail when a replace string of previous replacement does also contain a format sequence like this:

\n
"{0}{1}".format("{1}", "{0}")\n
\n

Normally you would expect the output to be {1}{0} but the actual output is {1}{1}. So do a simultaneously replacement instead like in fearphage’s suggestion.

\n", + "upvotes": 2111, + "upvoterUsernames": [], + "downvotes": 600, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32a0e082fcc3049e92fdb", + "creator": "heltonbiker", + "createdAt": 1355786960000, + "text": "If only some simple number-to-string conversion is desired, num.toFixed() method might be enough!", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fdd", + "creator": "Evan Carroll", + "createdAt": 1397512287000, + "text": "@MaksymilianMajer that seems to be something massively different.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fdf", + "creator": "Evan Carroll", + "createdAt": 1397547791000, + "text": "@MaksymilianMajer right, just saying this answer is dead, and the link has decayed. It needs to be totally purged.", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe0", + "creator": "Octavia Togami", + "createdAt": 1400125805000, + "text": "@Evan the link is fine and goes through.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe2", + "creator": "Dude Pascalou", + "createdAt": 1451567930000, + "text": "fearphage's suggestion now handles this test correctly.", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe4", + "creator": "HugoPoi", + "createdAt": 1470229678000, + "text": "I think the best is _.template function from lodash or underscore.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe6", + "creator": "hrzafer", + "createdAt": 1645136690000, + "text": "This wouldn't work with constants.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32924082fcc3049e92adc", + "creator": "Pooria", + "createdAt": 1236171353000, + "text": "

There is \"sprintf\" for JavaScript which you can find at http://www.webtoolkit.info/javascript-sprintf.html.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92add", + "creator": "Chris S", + "createdAt": 1236171870000, + "text": "

I'll add my own discoveries which I've found since I asked:

\n\n\n\n

Sadly it seems sprintf doesn't handle thousand separator formatting like .NET's string format.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32924082fcc3049e92ade", + "creator": "17 of 26", + "createdAt": 1236172259000, + "text": "

If you are looking to handle the thousands separator, you should really use toLocaleString() from the JavaScript Number class since it will format the string for the user's region.

\n\n

The JavaScript Date class can format localized dates and times.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4e082fcc3049e93071", + "creator": "Chris S", + "createdAt": 1236172759000, + "text": "It's actually a set by the user as a setting in the application (not the machine their on) but I'll take a look, thanks", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a4e082fcc3049e93072", + "creator": "Bhushan Kawadkar", + "createdAt": 1403585829000, + "text": "add some examples so that everyone can understands it quickly.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f32924082fcc3049e92ad3", + "creator": "Braden Best", + "createdAt": 1454009906000, + "text": "I wrote a cheap one that uses C-like printf syntax.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2195592, + "uvac": 2195648 + } + }, + { + "_id": "62f321bb082fcc3049e8fec7", + "title": "How do I test for an empty JavaScript object?", + "title-lowercase": "how do i test for an empty javascript object?", + "creator": "falmp", + "createdAt": 1237945185000, + "status": "open", + "text": "

After an AJAX request, sometimes my application may return an empty object, like:

\n\n
var a = {};\n
\n\n

How can I check whether that's the case?

\n", + "upvotes": 5186, + "upvoterUsernames": [], + "downvotes": 1291, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3673500, + "answers": 47, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e904e2", + "creator": "Christoph", + "createdAt": 1237945764000, + "text": "

If ECMAScript 5 support is available, you can use Object.keys():

\n
function isEmpty(obj) {\n    return Object.keys(obj).length === 0;\n}\n
\n

For ES3 and older, there's no easy way to do this. You'll have to loop over the properties explicitly:

\n
function isEmpty(obj) {\n    for(var prop in obj) {\n        if(obj.hasOwnProperty(prop))\n            return false;\n    }\n\n    return true;\n}\n
\n", + "upvotes": 1667, + "upvoterUsernames": [], + "downvotes": 423, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ae082fcc3049e9190c", + "creator": "niczak", + "createdAt": 1269473037000, + "text": "This works fine, or more simply: function isEmpty(object) { for(var i in object) { return true; } return false; }", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e9190e", + "creator": "namtax", + "createdAt": 1273067166000, + "text": "Shouldnt true and false be reversed in this function?", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91910", + "creator": "Christoph", + "createdAt": 1273163470000, + "text": "@namtax: no - the function is named isEmpty(), so it should return false if it has a property", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91911", + "creator": "Michael Buen", + "createdAt": 1625469519000, + "text": "Alernatively: function isObjectEmpty(obj) { for (const i in obj) return false; return true; }", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904e4", + "creator": "ikettu", + "createdAt": 1240578542000, + "text": "
function isEmpty(obj) {\n  for(var i in obj) { return false; }\n  return true;\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904e3", + "creator": "Thevs", + "createdAt": 1237981731000, + "text": "
    \n
  1. Just a workaround. Can your server generate some special property in case of no data?

    \n\n

    For example:

    \n\n
    var a = {empty:true};\n
    \n\n

    Then you can easily check it in your AJAX callback code.

  2. \n
  3. Another way to check it:

    \n\n
    if (a.toSource() === \"({})\")  // then 'a' is empty\n
  4. \n
\n\n

EDIT:\nIf you use any JSON library (f.e. JSON.js) then you may try JSON.encode() function and test the result against empty value string.

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ae082fcc3049e91914", + "creator": "Christoph", + "createdAt": 1237983699000, + "text": "toSource() is non-standard and doesn't work in IE or Opera (and potentially other browsers I didn't check)", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91915", + "creator": "Thevs", + "createdAt": 1238014005000, + "text": "This is standard in ECMA-262. There are non-standard browsers though.", + "upvotes": 166, + "upvoterUsernames": [], + "downvotes": 166, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91917", + "creator": "Thevs", + "createdAt": 1238059259000, + "text": "@Christoph: How do you think 3 other browsers would implement the same 'non-standard' feature if that wouldn't be a standard? :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91918", + "creator": "Thevs", + "createdAt": 1240258390000, + "text": "BTW, toSource() is implemented in IE internally, but nor exposed to outside. Think why?", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91919", + "creator": "Peter Aron Zentai", + "createdAt": 1402077841000, + "text": "There is no such method as toSource()", + "upvotes": 5155, + "upvoterUsernames": [], + "downvotes": 5155, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e9191b", + "creator": "Thevs", + "createdAt": 1409400013000, + "text": "@Jasper: There is not bigger overhead than checking object in a cycle for key values like in the most popular answer here :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904e5", + "creator": "starikovs", + "createdAt": 1267187607000, + "text": "

In addition to Thevs answer:

\n\n
var o = {};\nalert($.toJSON(o)=='{}'); // true\n\nvar o = {a:1};\nalert($.toJSON(o)=='{}'); // false\n
\n\n

it's jquery + jquery.json

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ae082fcc3049e9191c", + "creator": "itdoesntwork", + "createdAt": 1357237739000, + "text": "I don't like using JSON because it can't work with circular object structures.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e9191e", + "creator": "skierpage", + "createdAt": 1423275421000, + "text": "If your page loads jQuery then use $.isEmptyObject(), don't waste cycles with non-obvious conversions.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904e6", + "creator": "Erik Töyrä Silfverswärd", + "createdAt": 1274278071000, + "text": "

For those of you who have the same problem but use jQuery, you can use jQuery.isEmptyObject.

\n", + "upvotes": 1026, + "upvoterUsernames": [], + "downvotes": 436, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ae082fcc3049e91921", + "creator": "Michał Miszczyszyn", + "createdAt": 1338887583000, + "text": "This won't work if you (or any plugin) modified Object.prototype.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324ae082fcc3049e91923", + "creator": "Paul Sanwald", + "createdAt": 1368807656000, + "text": "note that this works fine for the question asked, {}, but that jQuery.isEmptyObject([]) === true, presumably because an empty list is iterable.", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904e7", + "creator": "Anton Danilchenko", + "createdAt": 1295965542000, + "text": "

jQuery have special function isEmptyObject() for this case:

\n\n
jQuery.isEmptyObject({}) // true\njQuery.isEmptyObject({ foo: \"bar\" }) // false\n
\n\n

Read more on http://api.jquery.com/jQuery.isEmptyObject/

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904e8", + "creator": "Baggz", + "createdAt": 1300826144000, + "text": "

You can use Underscore.js.

\n\n
_.isEmpty({}); // true\n
\n", + "upvotes": 333, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904ea", + "creator": "NiKo", + "createdAt": 1320068359000, + "text": "

My take:

\n

\r\n
\r\n
function isEmpty(obj) {\n  return Object.keys(obj).length === 0;\n}\n\nvar a = {\n  a: 1,\n  b: 2\n}\nvar b = {}\n\nconsole.log(isEmpty(a)); // false\nconsole.log(isEmpty(b)); // true
\r\n
\r\n
\r\n

\n

Just, I don't think all browsers implement Object.keys() currently.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ae082fcc3049e91928", + "creator": "cjbarth", + "createdAt": 1456850421000, + "text": "Object.keys(new Date()).length === 0; so this answer can be misleading.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904e9", + "creator": "Ekim", + "createdAt": 1306201970000, + "text": "

Caveat! Beware of JSON's limitiations.

\n\n
javascript:\n  obj={  f:function(){}  };\n  alert( \"Beware!! obj is NOT empty!\\n\\nobj = {  f:function(){}  }\" + \n               \"\\n\\nJSON.stringify( obj )\\n\\nreturns\\n\\n\" +\n                        JSON.stringify( obj ) );\n
\n\n

displays

\n\n
\n    Beware!! obj is NOT empty!\n\n    obj = {  f:function(){}  }\n\n    JSON.stringify( obj )\n\n    returns\n\n    {}\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904eb", + "creator": "will Farrell", + "createdAt": 1331783554000, + "text": "

Here is a fast, simple, function:

\n\n
function isEmptyFunction () {\n  for (const i in this) return false\n  return true\n}\n
\n\n

Implemented as a getter:

\n\n
Object.defineProperty(Object.prototype, 'isEmpty', { get: isEmptyFunction })\n\nconsole.log({}.isEmpty) // true\n
\n\n

Implemented as a separate function:

\n\n
const isEmpty = Function.prototype.call.bind(isEmptyFunction)\n\nconsole.log(isEmpty({})) // true\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904ec", + "creator": "Erin", + "createdAt": 1335727548000, + "text": "

There is a simple way if you are on a newer browser.\nObject.keys(obj).length === 0

\n", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324af082fcc3049e9192c", + "creator": "user663031", + "createdAt": 1409750580000, + "text": "Where does the keys property come from?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324af082fcc3049e9192e", + "creator": "cjbarth", + "createdAt": 1456850409000, + "text": "Object.keys(new Date()).length === 0; so this answer can be misleading.", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + }, + { + "_id": "62f324af082fcc3049e9192f", + "creator": "Ikram Khizer", + "createdAt": 1646240816000, + "text": "Best and simple answer so far", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 248, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904ee", + "creator": "mikemaccana", + "createdAt": 1345110372000, + "text": "

Sugar.JS provides extended objects for this purpose. The code is clean and simple:

\n\n

Make an extended object:

\n\n
a = Object.extended({})\n
\n\n

Check it's size:

\n\n
a.size()\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904ef", + "creator": "Ateszki", + "createdAt": 1358971659000, + "text": "

How about using JSON.stringify? It is almost available in all modern browsers.

\n\n
function isEmptyObject(obj){\n    return JSON.stringify(obj) === '{}';\n}\n
\n", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 99, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324af082fcc3049e91932", + "creator": "davidhadas", + "createdAt": 1451298824000, + "text": "This is a very slow option - I suggest to use the (for...in) option instead", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324af082fcc3049e91934", + "creator": "Felix Kling", + "createdAt": 1529710054000, + "text": "And it doesn't work for objects that contain functions.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324af082fcc3049e91935", + "creator": "onestep.ua", + "createdAt": 1629380611000, + "text": "Please note that JSON.stringify(new Error('gotcha')) === '{}' is true", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904f1", + "creator": "es cologne", + "createdAt": 1383745705000, + "text": "
if(Object.getOwnPropertyNames(obj).length === 0){\n  //is empty\n}\n
\n\n

see http://bencollier.net/2011/04/javascript-is-an-object-empty/

\n", + "upvotes": 180, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324af082fcc3049e91938", + "creator": "user663031", + "createdAt": 1409751228000, + "text": "This includes non-enumerable properties, in case you care.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f324af082fcc3049e91939", + "creator": "cjbarth", + "createdAt": 1456850251000, + "text": "Object.getOwnPropertyNames(new Date()).length === 0; so this answer can be misleading.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904f0", + "creator": "Jonathan Petitcolas", + "createdAt": 1359629758000, + "text": "

Old question, but just had the issue. Including JQuery is not really a good idea if your only purpose is to check if the object is not empty. Instead, just deep into JQuery's code, and you will get the answer:

\n\n
function isEmptyObject(obj) {\n    var name;\n    for (name in obj) {\n        if (obj.hasOwnProperty(name)) {\n            return false;\n        }\n    }\n    return true;\n}\n
\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904ed", + "creator": "kiranvj", + "createdAt": 1339142647000, + "text": "

I am using this.

\n
function isObjectEmpty(object) {\n  var isEmpty = true;\n  for (keys in object) {\n     isEmpty = false;\n     break; // exiting since we found that the object is not empty\n  }\n  return isEmpty;\n}\n
\n

Eg:

\n
var myObject = {}; // Object is empty\nvar isEmpty  = isObjectEmpty(myObject); // will return true;\n \n// populating the object\nmyObject = {"name":"John Smith","Address":"Kochi, Kerala"}; \n \n// check if the object is empty\nisEmpty  = isObjectEmpty(myObject); // will return false;\n
\n

from here

\n

Update

\n

OR

\n

you can use the jQuery implementation of isEmptyObject

\n
function isEmptyObject(obj) {\n  var name;\n  for (name in obj) {\n    return false;\n  }\n  return true;\n}\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f3", + "creator": "CatTest", + "createdAt": 1402077486000, + "text": "

I was returning an empty JSON response for an AJAX call and in IE8 jQuery.isEmptyObject() was not validating correctly. I added an additional check that seems to catch it properly.

\n\n
.done(function(data)\n{  \n    // Parse json response object\n    var response = jQuery.parseJSON(data);\n\n    // In IE 8 isEmptyObject doesn't catch the empty response, so adding additional undefined check\n    if(jQuery.isEmptyObject(response) || response.length === 0)\n    {\n        //empty\n    }\n    else\n    {\n        //not empty\n    }\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f2", + "creator": "chandu", + "createdAt": 1397891948000, + "text": "

As of jQuery 1.4 isEmptyObject() method checks both properties on the object itself and properties inherited from prototypes (in that it doesn't use hasOwnProperty). The argument should always be a plain JavaScript Object as other types of object (DOM elements, primitive strings/numbers, host objects) may not give consistent results across browsers. To determine if an object is a plain JavaScript object, use $.isPlainObject().

\n\n
jQuery.isPlainObject({}) // true\n\njQuery.isPlainObject( \"test\" ) // false\n
\n\n

Jquery api

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f4", + "creator": "NiRUS", + "createdAt": 1413804504000, + "text": "

This one line code helps with fallback to older browsers too.

\n\n
var a = {}; //if empty returns false\n(Object.getOwnPropertyNames ? Object.getOwnPropertyNames(a).length !== 0 : (function(){ for(var key in a) break; return !!key })()) //Returns False\n\nvar a = {b:2}; //if not empty returns true\n(Object.getOwnPropertyNames ? Object.getOwnPropertyNames(a).length !== 0 : (function(){ for(var key in a) break; return !!key })()) //Returns true\n
\n\n
\n\n

Object.getOwnPropertyNames is implemented in ECMA-5. the above line works in older browsers with a fallback function.

\n\n
\n\n
\n

Another quick solution is checking the length property of\n Object.keys, Object.entries or Object.values

\n
\n\n

Knowledge article: Follow this SO post for detailed difference between Object.keys Vs Object.getOwnPropertyNames

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f6", + "creator": "cwadding", + "createdAt": 1433524588000, + "text": "

Another alternative is to use is.js (14kB) as opposed to jquery (32kB), lodash (50kB), or underscore (16.4kB). is.js proved to be the fastest library among aforementioned libraries that could be used to determine whether an object is empty.

\n\n

http://jsperf.com/check-empty-object-using-libraries

\n\n

Obviously all these libraries are not exactly the same so if you need to easily manipulate the DOM then jquery might still be a good choice or if you need more than just type checking then lodash or underscore might be good. As for is.js, here is the syntax:

\n\n
var a = {};\nis.empty(a); // true\nis.empty({\"hello\": \"world\"}) // false\n
\n\n

Like underscore's and lodash's _.isObject(), this is not exclusively for objects but also applies to arrays and strings.

\n\n

Under the hood this library is using Object.getOwnPropertyNames which is similar to Object.keys but Object.getOwnPropertyNames is a more thorough since it will return enumerable and non-enumerable properties as described here.

\n\n
is.empty = function(value) {\n    if(is.object(value)){\n        var num = Object.getOwnPropertyNames(value).length;\n        if(num === 0 || (num === 1 && is.array(value)) || (num === 2 && is.arguments(value))){\n            return true;\n        }\n        return false;\n    } else {\n        return value === '';\n    }\n};\n
\n\n

If you don't want to bring in a library (which is understandable) and you know that you are only checking objects (not arrays or strings) then the following function should suit your needs.

\n\n
function isEmptyObject( obj ) {\n    return Object.getOwnPropertyNames(obj).length === 0;\n}\n
\n\n

This is only a bit faster than is.js though just because you aren't checking whether it is an object.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f5", + "creator": "Rahul Malhotra", + "createdAt": 1424255145000, + "text": "
    isEmpty = function(obj) {\n      if (obj == null) return true;\n      if (obj.constructor.name == \"Array\" || obj.constructor.name == \"String\") return obj.length === 0;\n      for (var key in obj) if (isEmpty(obj[key])) return true;\n      return false;\n    }\n
\n\n

This will check the emptiness of String, Array or Object (Maps).

\n\n

Usage :

\n\n
var a = {\"a\":\"xxx\",\"b\":[1],\"c\":{\"c_a\":\"\"}}\nisEmpty(a); // true, because a.c.c_a is empty.\nisEmpty(\"I am a String\"); //false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b0082fcc3049e91941", + "creator": "Kamil Kiełczewski", + "createdAt": 1579210086000, + "text": "isEmpty({}) gives FALSE here is test", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904f7", + "creator": "Slavik Meltser", + "createdAt": 1433936372000, + "text": "

You can define you own object prototype, just before its usage or at the beginning of your code.

\n\n

The definition should look like this:

\n\n

\r\n
\r\n
Object.prototype.hasOwnProperties = function()\r\n{ \r\n  for (var k in this)\r\n  { \r\n    if ( this.hasOwnProperty(k) )\r\n    { \r\n      return true;\r\n    } \r\n  }\r\n  return false;\r\n}
\r\n
\r\n
\r\n

\n\n

Here is a usage example:

\n\n

\r\n
\r\n
var a = {};\r\n\r\nwhile ( a.status !== \"finished\" )\r\n{  \r\n  if ( status === \"processing\" )\r\n  {\r\n    a.status = \"finished\";  \r\n  }\r\n  \r\n  if ( status === \"starting\" )\r\n  {\r\n    a.status = \"processing\";  \r\n  }\r\n  \r\n  if ( !a.hasOwnProperties() )\r\n  {\r\n    a.status = \"starting\";\r\n  }\r\n}
\r\n
\r\n
\r\n

\n\n

Enjoy! :-)

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904f8", + "creator": "Adam Zerner", + "createdAt": 1440034372000, + "text": "

ECMA 5+:

\n
// because Object.keys(new Date()).length === 0;\n// we have to do some additional check\nobj // 👈 null and undefined check\n&& Object.keys(obj).length === 0\n&& Object.getPrototypeOf(obj) === Object.prototype\n
\n

Note, though, that this creates an unnecessary array (the return value of keys).

\n

Pre-ECMA 5:

\n
function isEmpty(obj) {\n  for(var prop in obj) {\n    if(Object.prototype.hasOwnProperty.call(obj, prop)) {\n      return false;\n    }\n  }\n\n  return JSON.stringify(obj) === JSON.stringify({});\n}\n
\n

jQuery:

\n
jQuery.isEmptyObject({}); // true\n
\n

lodash:

\n
_.isEmpty({}); // true\n
\n

Underscore:

\n
_.isEmpty({}); // true\n
\n

Hoek

\n
Hoek.deepEqual({}, {}); // true\n
\n

ExtJS

\n
Ext.Object.isEmpty({}); // true\n
\n

AngularJS (version 1)

\n
angular.equals({}, {}); // true\n
\n

Ramda

\n
R.isEmpty({}); // true\n
\n", + "upvotes": 7917, + "upvoterUsernames": [], + "downvotes": 774, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f324b0082fcc3049e91944", + "creator": "cjbarth", + "createdAt": 1456850078000, + "text": "Object.keys(new Date()).length === 0; so this answer can be misleading.", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e91945", + "creator": "Nate", + "createdAt": 1636679451000, + "text": "This returns true for an object that is not empty, but all its keys are symbols.", + "upvotes": 2104, + "upvoterUsernames": [], + "downvotes": 2104, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e91947", + "creator": "Đinh Carabus", + "createdAt": 1640480173000, + "text": "The ECMA 5+ version will give incorrect results for empty objects created using Object.create(null).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e91949", + "creator": "Dipanshu Mahla", + "createdAt": 1640881117000, + "text": "I think javascript should create something to check whether an object is empty or add length property to the object.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e9194b", + "creator": "Wronski", + "createdAt": 1647729384000, + "text": "Is JSON.stringify(obj) === "{}" a potential effective solution?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e9194d", + "creator": "Ry-", + "createdAt": 1647730446000, + "text": "@Wronski Yes, potentially, and it’s already in the answer. It’s not a very good solution, though (slow, no more reliable and sometimes less).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904f9", + "creator": "synthet1c", + "createdAt": 1448281835000, + "text": "

I can't believe after two years of programming js it never clicked that empty objects and array's aren't falsey, the weirdest thing is it never caught me out.

\n\n

this will return true if the input is falsey by default or if it's an empty object or array. the inverse is the trueish function

\n\n

http://codepen.io/synthet1c/pen/pjmoWL

\n\n
function falsish( obj ){\n    if( (typeof obj === 'number' && obj > 0) || obj === true ){\n        return false;\n    }\n    return !!obj\n        ? !Object.keys( obj ).length\n        : true;\n}\n\nfunction trueish( obj ){\n    return !falsish( obj );\n}\n\nfalsish({})           //=> true\nfalsish({foo:'bar'})  //=> false\nfalsish([])           //=> true\nfalsish(['foo'])      //=> false\nfalsish(false)        //=> true\nfalsish(true)         //=> false\n// the rest are on codepen\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904fa", + "creator": "davidhadas", + "createdAt": 1451299259000, + "text": "

Using Object.keys(obj).length (as suggested above for ECMA 5+) is 10 times slower for empty objects! keep with the old school (for...in) option.

\n\n

Tested under Node, Chrome, Firefox and IE 9, it becomes evident that for most use cases:

\n\n\n\n

Bottom line performance wise, use:

\n\n
function isEmpty(obj) { \n   for (var x in obj) { return false; }\n   return true;\n}\n
\n\n

or

\n\n
function isEmpty(obj) {\n   for (var x in obj) { if (obj.hasOwnProperty(x))  return false; }\n   return true;\n}\n
\n\n

See detailed testing results and test code at Is object empty?

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904fb", + "creator": "GibboK", + "createdAt": 1490784156000, + "text": "

The following example show how to test if a JavaScript object is empty, if by empty we means has no own properties to it.

\n\n

The script works on ES6.

\n\n

\r\n
\r\n
const isEmpty = (obj) => {\r\n    if (obj === null ||\r\n        obj === undefined ||\r\n        Array.isArray(obj) ||\r\n        typeof obj !== 'object'\r\n    ) {\r\n        return true;\r\n    }\r\n    return Object.getOwnPropertyNames(obj).length === 0;\r\n};\r\nconsole.clear();\r\nconsole.log('-----');\r\nconsole.log(isEmpty(''));           // true\r\nconsole.log(isEmpty(33));           // true\r\nconsole.log(isEmpty([]));           // true\r\nconsole.log(isEmpty({}));           // true\r\nconsole.log(isEmpty({ length: 0, custom_property: [] })); // false\r\nconsole.log('-----');\r\nconsole.log(isEmpty('Hello'));      // true\r\nconsole.log(isEmpty([1, 2, 3]));    // true\r\nconsole.log(isEmpty({ test: 1 }));  // false\r\nconsole.log(isEmpty({ length: 3, custom_property: [1, 2, 3] })); // false\r\nconsole.log('-----');\r\nconsole.log(isEmpty(new Date()));   // true\r\nconsole.log(isEmpty(Infinity));     // true\r\nconsole.log(isEmpty(null));         // true\r\nconsole.log(isEmpty(undefined));    // true
\r\n
\r\n
\r\n

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904fc", + "creator": "Igor Kokotko", + "createdAt": 1510139177000, + "text": "

Try Destructuring

\n\n
const a = {};\nconst { b } = a;\nconst emptryOrNot = (b) ? 'not Empty' : 'empty';\nconsole.log(emptryOrNot)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b0082fcc3049e9194f", + "creator": "gman", + "createdAt": 1517408882000, + "text": "did you actually try this? Put anything in a and it still says empty", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324b0082fcc3049e91950", + "creator": "Gust van de Wal", + "createdAt": 1581687848000, + "text": "That is not how destructuring works. This will only work for objects containing an entry with a key 'b'", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e904fe", + "creator": "Dmitry Sheiko", + "createdAt": 1530098641000, + "text": "

It's weird that I haven't encountered a solution that compares the object's values as opposed to the existence of any entry (maybe I missed it among the many given solutions).
\nI would like to cover the case where an object is considered empty if all its values are undefined:

\n

\r\n
\r\n
    const isObjectEmpty = obj => Object.values(obj).every(val => typeof val === \"undefined\")\n\n    console.log(isObjectEmpty({}))                                 // true\n    console.log(isObjectEmpty({ foo: undefined, bar: undefined })) // true\n    console.log(isObjectEmpty({ foo: false,     bar: null }))      // false
\r\n
\r\n
\r\n

\n

Example usage

\n

Let's say, for the sake of example, you have a function (paintOnCanvas) that destructs values from its argument (x, y and size). If all of them are undefined, they are to be left out of the resulting set of options. If not they are not, all of them are included.

\n
function paintOnCanvas ({ brush, x, y, size }) {\n  const baseOptions = { brush }\n  const areaOptions = { x, y, size }\n  const options = isObjectEmpty(areaOptions) ? baseOptions : { ...baseOptions, areaOptions }\n  // ...\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904fd", + "creator": "Jesse", + "createdAt": 1523266661000, + "text": "

The correct answer is:

\n
function isEmptyObject(obj) {\n  return (\n    Object.getPrototypeOf(obj) === Object.prototype &&\n    Object.getOwnPropertyNames(obj).length === 0 &&\n    Object.getOwnPropertySymbols(obj).length === 0\n  );\n}\n
\n

This checks that:

\n\n

In other words, the object is indistinguishable from one created with {}.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90500", + "creator": "Jplus2", + "createdAt": 1553133401000, + "text": "
export function isObjectEmpty(obj) {\n  return (\n    Object.keys(obj).length === 0 &&\n    Object.getOwnPropertySymbols(obj).length === 0 &&\n    obj.constructor === Object\n  );\n}\n
\n\n

This include checking for objects containing symbol properties.

\n\n

Object.keys does not retrieve symbol properties.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e904ff", + "creator": "Jonathan", + "createdAt": 1534283391000, + "text": "

This is what I came up with, to tell if there are any non-null values in the object.

\n\n
function isEmpty(obj: Object): Boolean {\n    for (const prop in obj) {\n        if (obj.hasOwnProperty(prop)) {\n            if (obj[prop] instanceof Object) {\n                const rtn = this.isEmpty(obj[prop]);\n                if (rtn === false) {\n                  return false;\n                }\n            } else if (obj[prop] || obj[prop] === false) {\n                return false;\n            }\n        }\n    }\n    return true;\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90502", + "creator": "Anthony D'Amato", + "createdAt": 1558598876000, + "text": "

To really accept ONLY {}, the best way to do it in Javascript using Lodash is:

\n\n
_.isEmpty(value) && _.isPlainObject(value)\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90501", + "creator": "João Pimentel Ferreira", + "createdAt": 1555695106000, + "text": "

Pure Vanilla Javascript, and full backward compatibility

\n\n

\r\n
\r\n
function isObjectDefined (Obj) {\r\n  if (Obj === null || typeof Obj !== 'object' ||\r\n    Object.prototype.toString.call(Obj) === '[object Array]') {\r\n    return false\r\n  } else {\r\n    for (var prop in Obj) {\r\n      if (Obj.hasOwnProperty(prop)) {\r\n        return true\r\n      }\r\n    }\r\n    return JSON.stringify(Obj) !== JSON.stringify({})\r\n  }\r\n}\r\n\r\nconsole.log(isObjectDefined()) // false\r\nconsole.log(isObjectDefined('')) // false\r\nconsole.log(isObjectDefined(1)) // false\r\nconsole.log(isObjectDefined('string')) // false\r\nconsole.log(isObjectDefined(NaN)) // false\r\nconsole.log(isObjectDefined(null)) // false\r\nconsole.log(isObjectDefined({})) // false\r\nconsole.log(isObjectDefined([])) // false\r\nconsole.log(isObjectDefined({a: ''})) // true
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90504", + "creator": "Роман Татаринов", + "createdAt": 1571844544000, + "text": "

isEmpty for value any type

\n\n
/* eslint-disable no-nested-ternary */\n\nconst isEmpty = value => {\n  switch (typeof value) {\n    case 'undefined':\n      return true;\n    case 'object':\n      return value === null\n        ? true\n        : Array.isArray(value)\n        ? !value.length\n        : Object.entries(value).length === 0 && value.constructor === Object;\n    case 'string':\n      return !value.length;\n    default:\n      return false;\n  }\n};\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90503", + "creator": "Alireza", + "createdAt": 1571391818000, + "text": "

That's similar way of how it gets checked in lodash source for object :

\n\n
const isEmpty = value => {\n  for (const key in value) {\n    if (hasOwnProperty.call(value, key)) {\n      return false\n    }\n  }\n  return true;\n}\n
\n\n

But there are many other ways to do that.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90506", + "creator": "Tarandeep Singh", + "createdAt": 1573841192000, + "text": "

Perfect and failsafe solution

\n\n

I think the first accepted solution works in most cases but is not Failsafe.

\n\n

The better and failsafe solution will be.

\n\n
function isEmptyObject() { \n  return toString.call(obj) === \"[object Object]\" \n  && Object.keys(obj).length === 0;\n}\n
\n\n

or in ES6/7

\n\n
const isEmptyObject = () => toString.call(obj) === \"[object Object]\" \n  && Object.keys(obj).length === 0;\n
\n\n

With this approach if the obj is set to undefined or null, the code does not break.\nand return null.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90505", + "creator": "Ashikur Rahman", + "createdAt": 1573827146000, + "text": "
    let jsObject = JSON.parse(JSON.stringify(obj), (key, value) => {\n                if (value === null ||\n                    value === '' ||\n                    (value.constructor === Object && Object.entries(value).length === 0) ||\n                    (value.constructor === Array && value.length === 0)) {\n                    return undefined\n                }\n                return value\n            })\n
\n\n

This will filter out all the invalid fields recursively.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90508", + "creator": "Aakash Handa", + "createdAt": 1586236424000, + "text": "

The new Way to check value is \n if(Object.entries(this.props.myarticle).length===0){\n }

\n\n

here myarticles is object

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90507", + "creator": "Juan Vieira", + "createdAt": 1582241384000, + "text": "

I know this doesn't answer 100% your question, but I have faced similar issues before and here's how I use to solve them:

\n\n

I have an API that may return an empty object. Because I know what fields to expect from the API, I only check if any of the required fields are present or not.

\n\n

For example:

\n\n

API returns {} or {agentID: '1234' (required), address: '1234 lane' (opt),...}.\nIn my calling function, I'll only check

\n\n
if(response.data && response.data.agentID) { \n  do something with my agentID \n} else { \n  is empty response\n}\n
\n\n

This way I don't need to use those expensive methods to check if an object is empty. The object will be empty for my calling function if it doesn't have the agentID field.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90509", + "creator": "Alex Tudor", + "createdAt": 1587498280000, + "text": "

Best one-liner solution I could find (updated):

\n

\r\n
\r\n
isEmpty = obj => !Object.values(obj).filter(e => typeof e !== 'undefined').length;\n\nconsole.log(isEmpty({}))                                        // true\nconsole.log(isEmpty({a: undefined, b: undefined}))              // true\nconsole.log(isEmpty({a: undefined, b: void 1024, c: void 0}))   // true\n\nconsole.log(isEmpty({a: [undefined, undefined]}))               // false\nconsole.log(isEmpty({a: 1}))                                    // false\nconsole.log(isEmpty({a: ''}))                                   // false\nconsole.log(isEmpty({a: null, b: undefined}))                   // false
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b2082fcc3049e9195d", + "creator": "Damien", + "createdAt": 1587605631000, + "text": "Greatest answer overlooked", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324b2082fcc3049e9195f", + "creator": "Ivanka Todorova", + "createdAt": 1588854808000, + "text": "@Damien, tbf the question is 11 years old & this answer was posted 2 weeks ago.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b2082fcc3049e91961", + "creator": "Kunal Tyagi", + "createdAt": 1595311199000, + "text": "What if the object is like this: { 0 : null }, I am getting a key whose value is null. What to do in such cases?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9050a", + "creator": "Ericgit", + "createdAt": 1595138009000, + "text": "

1. Using Object.keys

\n

Object.keys will return an Array, which contains the property names of the object. If the length of the array is 0, then we know that the object is empty.

\n
function isEmpty(obj) {\n    return Object.keys(obj).length === 0 && empty.constructor === Object;\n}\n
\n

We can also check this using Object.values and Object.entries.\nThis is typically the easiest way to determine if an object is empty.

\n

2. Looping over object properties with for…in

\n

The for…in statement will loop through the enumerable property of object.

\n
function isEmpty(obj) {\n    for(var prop in obj) {\n        if(obj.hasOwnProperty(prop))\n            return false;\n    }\n\n    return true;\n}\n
\n

In the above code, we will loop through object properties and if an object has at least one property, then it will enter the loop and return false. If the object doesn’t have any properties then it will return true.

\n

#3. Using JSON.stringify\nIf we stringify the object and the result is simply an opening and closing bracket, we know the object is empty.

\n
function isEmptyObject(obj){\n    return JSON.stringify(obj) === '{}';\n}\n
\n

4. Using jQuery

\n
jQuery.isEmptyObject(obj); \n
\n

5. Using Underscore and Lodash

\n
_.isEmpty(obj);\n
\n

Resource

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9050b", + "creator": "ganesh phirke", + "createdAt": 1595178526000, + "text": "

We can check with vanilla js with handling null or undefined check also as follows,

\n

\r\n
\r\n
function isEmptyObject(obj) {\n  return !!obj && Object.keys(obj).length === 0 && obj.constructor === Object;\n}\n\n//tests\n\nisEmptyObject(new Boolean());  // false \nisEmptyObject(new Array());    // false \nisEmptyObject(new RegExp());   // false \nisEmptyObject(new String());   // false \nisEmptyObject(new Number());   // false \nisEmptyObject(new Function()); // false \nisEmptyObject(new Date());     // false\nisEmptyObject(null);          // false\nisEmptyObject(undefined);     // false\nisEmptyObject({});            // true
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9050c", + "creator": "010011100101", + "createdAt": 1597476568000, + "text": "

I liked this one I came up with, with the help of some other answers here. Thought I'd share it.

\n

\r\n
\r\n
Object.defineProperty(Object.prototype, 'isEmpty', {\n    get() {\n        for(var p in this) {\n            if (this.hasOwnProperty(p)) {return false}\n        }\n        return true;\n    }\n});\n\n\nlet users = {};\nlet colors = {primary: 'red'};\nlet sizes = {sm: 100, md: 200, lg: 300};\n\nconsole.log(\n'\\nusers =', users,\n'\\nusers.isEmpty ==> ' + users.isEmpty,\n'\\n\\n-------------\\n',\n'\\ncolors =', colors,\n'\\ncolors.isEmpty ==> ' + colors.isEmpty,\n'\\n\\n-------------\\n',\n'\\nsizes =', sizes,\n'\\nsizes.isEmpty ==> ' + sizes.isEmpty,\n'\\n',\n''\n);
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9050d", + "creator": "Ron Jonk", + "createdAt": 1599032919000, + "text": "

Mostly what you want to know is, if the object has properties before using it. So instead of asking isEmpty and then always check the negation like if(!isEmpty(obj)) you can just test if the object is not null and has properties instead

\n
export function hasProperties(obj): boolean {\n  return obj && obj.constructor === Object && Object.keys(obj).length >= 1;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9050e", + "creator": "hardik", + "createdAt": 1616228344000, + "text": "

You can use lodash library instead of making a plain JS function.

\n

_.isEmpty({}) // true

\n

This will check array and object either they do have values and return boolean.

\n", + "upvotes": 8600, + "upvoterUsernames": [], + "downvotes": 8600, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9050f", + "creator": "Ankur Soni", + "createdAt": 1622542176000, + "text": "
isEmptyObject(value) {\n  return value && value.constructor === Object && Object.keys(value).length === 0;\n}\n
\n

The above code is enough to check the empty-ness of an Object.

\n

A very good article is written at how-to-check-if-object-is-empty

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b2082fcc3049e91969", + "creator": "Isaac Pak", + "createdAt": 1626462224000, + "text": "This looks pretty much like the accepted answer", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f324b2082fcc3049e9196b", + "creator": "Ankur Soni", + "createdAt": 1626684247000, + "text": "sequence of check matters.", + "upvotes": 962, + "upvoterUsernames": [], + "downvotes": 962, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90510", + "creator": "Bekim Bacaj", + "createdAt": 1631094755000, + "text": "

IsEmpty Object, unexpectedly lost its meaning i.e.: it's programming semantics, when our famous guru from Yahoo introduced the customized non-enumerable Object properties to ECMA and they got accepted.

\n

[ If you don't like history - feel free to skip right to the working code ]

\n

I'm seeing lots of good answers \\ solutions to this question \\ problem.\nHowever, grabbing the most recent extensions to ECMA Script is not the honest way to go. We used to hold back the Web back in the day to keep Netscape 4.x, and Netscape based pages work and projects alive, which (by the way) were extremely primitive backwards and idiosyncratic, refusing to use new W3C standards and propositions [ which were quite revolutionary for that time and coder friendly ] while now being brutal against our own legacy.

\n

Killing Internet Explorer 11 is plain wrong! Yes, some old warriors that infiltrated Microsoft remaining dormant since the "Cold War" era, agreed to it - for all the wrong reasons. - But that doesn't make it right!

\n

Making use, of a newly introduced method\\property in your answers and handing it over as a discovery ("that was always there but we didn't notice it"), rather than a new invention (for what it really is), is somewhat 'green' and harmful. I used to make such mistakes some 20 years ago when I still couldn't tell what's already in there and treated everything I could find a reference for, as a common working solution...

\n

Backward compatibility is important !

\n

We just don't know it yet. That's the reason I got the need to share my 'centuries old' generic solution which remains backward and forward compatible to the unforeseen future.

\n

There were lots of attacks on the in operator but I think the guys doing that have finally come to senses and really started to understand and appreciate a true Dynamic Type Language such as JavaScript and its beautiful nature.

\n

My methods aim to be simple and nuclear and for reasons mentioned above, I don't call it "empty" because the meaning of that word is no longer accurate. Is Enumerable, seems to be the word with the exact meaning.

\n
function isEnum( x ) { for( var p in x )return!0; return!1 };\n
\n

Some use cases:

\n
isEnum({1:0})\ntrue\n\nisEnum({})\nfalse\n\nisEnum(null)\nfalse\n
\n

Thanks for reading!

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b2082fcc3049e9196d", + "creator": "Neil", + "createdAt": 1646301409000, + "text": "It's disgusting what people have done to the web", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3678686, + "uvac": 3678733 + } + }, + { + "_id": "62f321bb082fcc3049e8fecf", + "title": "How do I loop through or enumerate a JavaScript object?", + "title-lowercase": "how do i loop through or enumerate a javascript object?", + "creator": "Tanmoy", + "createdAt": 1238047307000, + "status": "open", + "text": "

I have a JavaScript object like the following:

\n
var p = {\n    "p1": "value1",\n    "p2": "value2",\n    "p3": "value3"\n};\n
\n

How do I loop through all of p's elements (p1, p2, p3...) and get their keys and values?

\n", + "upvotes": 4069, + "upvoterUsernames": [], + "downvotes": 655, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2566927, + "answers": 43, + "answerItems": [ + { + "_id": "62f321c2082fcc3049e906aa", + "creator": "Bryan", + "createdAt": 1238047501000, + "text": "

You can just iterate over it like:

\n\n
for (var key in p) {\n  alert(p[key]);\n}\n
\n\n

Note that key will not take on the value of the property, it's just an index value.

\n", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32550082fcc3049e91c64", + "creator": "Vatsal", + "createdAt": 1464898707000, + "text": "This is repeated and not even entirely correct. You need to have a check of hasOwnProperty to make this work properly", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906a9", + "creator": "Richard Levasseur", + "createdAt": 1238047450000, + "text": "
for(key in p) {\n  alert( p[key] );\n}\n
\n\n

Note: you can do this over arrays, but you'll iterate over the length and other properties, too.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32550082fcc3049e91c67", + "creator": "Bryan", + "createdAt": 1238047633000, + "text": "When using a for loop like that, key will just take on an index value, so that will just alert 0, 1, 2, etc... You need to access p[key].", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32550082fcc3049e91c68", + "creator": "Sk8erPeter", + "createdAt": 1388537713000, + "text": "@Pencroff: the problem is that the question is not about looping through arrays... ;)", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906ac", + "creator": "Andreas Grech", + "createdAt": 1238047936000, + "text": "

You have to use the for-in loop

\n\n

But be very careful when using this kind of loop, because this will loop all the properties along the prototype chain.

\n\n

Therefore, when using for-in loops, always make use of the hasOwnProperty method to determine if the current property in iteration is really a property of the object you're checking on:

\n\n
for (var prop in p) {\n    if (!p.hasOwnProperty(prop)) {\n        //The current property is not a direct property of p\n        continue;\n    }\n    //Do your logic with the property here\n}\n
\n", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 286, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32550082fcc3049e91c6b", + "creator": "Andreas Grech", + "createdAt": 1312546916000, + "text": "Yes, I prefer keeping the { } mainly to avoid confusion if one later on needs to add something to the if scope.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906ab", + "creator": "levik", + "createdAt": 1238047933000, + "text": "

You can use the for-in loop as shown by others. However, you also have to make sure that the key you get is an actual property of an object, and doesn't come from the prototype.

\n\n

Here is the snippet:\n

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\n\r\nfor (var key in p) {\r\n    if (p.hasOwnProperty(key)) {\r\n        console.log(key + \" -> \" + p[key]);\r\n    }\r\n}
\r\n
\r\n
\r\n

\n\n

For-of with Object.keys() alternative:

\n\n

\r\n
\r\n
var p = {\r\n    0: \"value1\",\r\n    \"b\": \"value2\",\r\n    key: \"value3\"\r\n};\r\n\r\nfor (var key of Object.keys(p)) {\r\n    console.log(key + \" -> \" + p[key])\r\n}
\r\n
\r\n
\r\n

\n\n

Notice the use of for-of instead of for-in, if not used it will return undefined on named properties, and Object.keys() ensures the use of only the object's own properties without the whole prototype-chain properties

\n\n

Using the new Object.entries() method:

\n\n

Note: This method is not supported natively by Internet Explorer. You may consider using a Polyfill for older browsers.

\n\n
const p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\nfor (let [key, value] of Object.entries(p)) {\n  console.log(`${key}: ${value}`);\n}\n
\n", + "upvotes": 5021, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32550082fcc3049e91c6c", + "creator": "ERROR 401", + "createdAt": 1635164703000, + "text": "How can I add the values that was looped? Thanks", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 101, + "downvoterUsernames": [] + }, + { + "_id": "62f32550082fcc3049e91c6e", + "creator": "mercury", + "createdAt": 1636069422000, + "text": "(for..in) for objects, (for.. of) for arrays", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f32550082fcc3049e91c70", + "creator": "Ariful Islam", + "createdAt": 1639080961000, + "text": "Thanks for simple answer. Its saved my time.", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [] + }, + { + "_id": "62f32550082fcc3049e91c72", + "creator": "Irvan Hilmi", + "createdAt": 1651255105000, + "text": "is it possible to even do this using while loops? like while(let x in objectA || objectA[x].hasOwnProperty("childPointer")", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f32550082fcc3049e91c74", + "creator": "codrelphi", + "createdAt": 1654588865000, + "text": "I was in the need of this method. Thank you !", + "upvotes": 192, + "upvoterUsernames": [], + "downvotes": 192, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906ae", + "creator": "Francis Lewis", + "createdAt": 1313700622000, + "text": "

After looking through all the answers in here, hasOwnProperty isn't required for my own usage because my json object is clean; there's really no sense in adding any additional javascript processing. This is all I'm using:

\n\n
for (var key in p) {\n    console.log(key + ' => ' + p[key]);\n    // key is key\n    // value is p[key]\n}\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32550082fcc3049e91c76", + "creator": "Juan Mendes", + "createdAt": 1460633871000, + "text": "It can be completely clean if created with Object.create(null)", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906ad", + "creator": "Axel Rauschmayer", + "createdAt": 1303336759000, + "text": "

Under ECMAScript 5, you can combine Object.keys() and Array.prototype.forEach():

\n\n
var obj = { first: \"John\", last: \"Doe\" };\n\nObject.keys(obj).forEach(function(key) {\n    console.log(key, obj[key]);\n});\n
\n\n

ECMAScript 6 adds for...of:

\n\n
for (const key of Object.keys(obj)) {\n    console.log(key, obj[key]);\n}\n
\n\n

ECMAScript 8 adds Object.entries() which avoids having to look up each value in the original object:

\n\n
Object.entries(obj).forEach(\n    ([key, value]) => console.log(key, value)\n);\n
\n\n

You can combine for...of, destructuring, and Object.entries:

\n\n
for (const [key, value] of Object.entries(obj)) {\n    console.log(key, value);\n}\n
\n\n

Both Object.keys() and Object.entries() iterate properties in the same order as a for...in loop but ignore the prototype chain. Only the object's own enumerable properties are iterated.

\n", + "upvotes": 2372, + "upvoterUsernames": [], + "downvotes": 1070, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b1", + "creator": "VisioN", + "createdAt": 1358704706000, + "text": "

The question won't be complete if we don't mention about alternative methods for looping through objects.

\n\n

Nowadays many well known JavaScript libraries provide their own methods for iterating over collections, i.e. over arrays, objects, and array-like objects. These methods are convenient to use and are entirely compatible with any browser.

\n\n
    \n
  1. If you work with jQuery, you may use jQuery.each() method. It can be used to seamlessly iterate over both objects and arrays:

    \n\n
    $.each(obj, function(key, value) {\n    console.log(key, value);\n});\n
  2. \n
  3. In Underscore.js you can find method _.each(), which iterates over a list of elements, yielding each in turn to a supplied function (pay attention to the order of arguments in iteratee function!):

    \n\n
    _.each(obj, function(value, key) {\n    console.log(key, value);\n});\n
  4. \n
  5. Lo-Dash provides several methods for iterating over object properties. Basic _.forEach() (or it's alias _.each()) is useful for looping through both objects and arrays, however (!) objects with length property are treated like arrays, and to avoid this behavior it is suggested to use _.forIn() and _.forOwn() methods (these also have value argument coming first):

    \n\n
    _.forIn(obj, function(value, key) {\n    console.log(key, value);\n});\n
    \n\n

    _.forIn() iterates over own and inherited enumerable properties of an object, while _.forOwn() iterates only over own properties of an object (basically checking against hasOwnProperty function). For simple objects and object literals any of these methods will work fine.

  6. \n
\n\n

Generally all described methods have the same behaviour with any supplied objects. Besides using native for..in loop will usually be faster than any abstraction, such as jQuery.each(), these methods are considerably easier to use, require less coding and provide better error handling.

\n", + "upvotes": 269, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32551082fcc3049e91c79", + "creator": "Ravi Ram", + "createdAt": 1370702497000, + "text": "To get to the value: $.each(obj, function (key, value) { console.log(value.title); });", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32551082fcc3049e91c7b", + "creator": "ppasler", + "createdAt": 1504855464000, + "text": "Just funny how underscore and jquery changed parameters :)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906b3", + "creator": "B.F.", + "createdAt": 1402206906000, + "text": "

Besause the asker's ['ultimate goal is to loop through some key value pairs'] and finally don't looking for a loop.

\n\n
var p ={\"p1\":\"value1\",\"p2\":\"value2\",\"p3\":\"value3\"};\nif('p1' in p){\n  var val=p['p1'];\n  ...\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32551082fcc3049e91c7d", + "creator": "Sebastian", + "createdAt": 1407220814000, + "text": "No! He wrote "Now I want to loop through all p elements" so he realy need a loop like in his accepted answer.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906af", + "creator": "ParaMeterz", + "createdAt": 1329823361000, + "text": "

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\n\r\nfor (var key in p) {\r\n    if (p.hasOwnProperty(key)) {\r\n        console.log(key + \" = \" + p[key]);\r\n    }\r\n}
\r\n
<p>\r\n  Output:<br>\r\n  p1 = values1<br>\r\n  p2 = values2<br>\r\n  p3 = values3\r\n</p>
\r\n
\r\n
\r\n

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b0", + "creator": "strider", + "createdAt": 1355029551000, + "text": "

via prototype with forEach() which should skip the prototype chain properties:

\n\n
Object.prototype.each = function(f) {\n    var obj = this\n    Object.keys(obj).forEach( function(key) { \n        f( key , obj[key] ) \n    });\n}\n\n\n//print all keys and values\nvar obj = {a:1,b:2,c:3}\nobj.each(function(key,value) { console.log(key + \" \" + value) });\n// a 1\n// b 2\n// c 3\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b2", + "creator": "Pencroff", + "createdAt": 1384631989000, + "text": "

In ECMAScript 5 you have new approach in iteration fields of literal - Object.keys

\n\n

More information you can see on MDN

\n\n

My choice is below as a faster solution in current versions of browsers (Chrome30, IE10, FF25)

\n\n
var keys = Object.keys(p),\n    len = keys.length,\n    i = 0,\n    prop,\n    value;\nwhile (i < len) {\n    prop = keys[i];\n    value = p[prop];\n    i += 1;\n}\n
\n\n

You can compare performance of this approach with different implementations on jsperf.com:

\n\n\n\n

Browser support you can see on Kangax's compat table

\n\n

For old browser you have simple and full polyfill

\n\n

UPD:

\n\n

performance comparison for all most popular cases in this question on perfjs.info:

\n\n

object literal iteration

\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32551082fcc3049e91c82", + "creator": "Jamie Hutber", + "createdAt": 1395356738000, + "text": "Indeed, I just wanted to post this method. But you beat me to it :(", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906b4", + "creator": "mohamed-ibrahim", + "createdAt": 1429617753000, + "text": "

Only JavaScript code without dependencies:

\n\n
var p = {\"p1\": \"value1\", \"p2\": \"value2\", \"p3\": \"value3\"};\nkeys = Object.keys(p);   // [\"p1\", \"p2\", \"p3\"]\n\nfor(i = 0; i < keys.length; i++){\n  console.log(keys[i] + \"=\" + p[keys[i]]);   // p1=value1, p2=value2, p3=value3\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b6", + "creator": "Lewis", + "createdAt": 1436430166000, + "text": "

I would do this rather than checking obj.hasOwnerProperty within every for ... in loop.

\n\n
var obj = {a : 1};\nfor(var key in obj){\n    //obj.hasOwnProperty(key) is not needed.\n    console.log(key);\n}\n//then check if anybody has messed the native object. Put this code at the end of the page.\nfor(var key in Object){\n    throw new Error(\"Please don't extend the native object\");\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b5", + "creator": "Dmitry Sheiko", + "createdAt": 1434966724000, + "text": "
\n

Object.keys(obj) : Array

\n \n

retrieves all string-valued keys of all enumerable own (non-inherited) properties.

\n
\n\n

So it gives the same list of keys as you intend by testing each object key with hasOwnProperty. You don't need that extra test operation than and Object.keys( obj ).forEach(function( key ){}) is supposed to be faster. Let's prove it:

\n\n

\r\n
\r\n
var uniqid = function(){\r\n\t\t\tvar text = \"\",\r\n\t\t\t\t\ti = 0,\r\n\t\t\t\t\tpossible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\r\n\t\t\tfor( ; i < 32; i++ ) {\r\n\t\t\t\t\ttext += possible.charAt( Math.floor( Math.random() * possible.length ) );\r\n\t\t\t}\r\n\t\t\treturn text;\r\n\t\t}, \r\n\t\tCYCLES = 100000,\r\n\t\tobj = {}, \r\n\t\tp1,\r\n\t\tp2,\r\n\t\tp3,\r\n\t\tkey;\r\n\r\n// Populate object with random properties\r\nArray.apply( null, Array( CYCLES ) ).forEach(function(){\r\n\tobj[ uniqid() ] = new Date()\r\n});\r\n\r\n// Approach #1\r\np1 = performance.now();\r\nObject.keys( obj ).forEach(function( key ){\r\n\tvar waste = obj[ key ];\r\n});\r\n\r\np2 = performance.now();\r\nconsole.log( \"Object.keys approach took \" + (p2 - p1) + \" milliseconds.\");\r\n\r\n// Approach #2\r\nfor( key in obj ) {\r\n\tif ( obj.hasOwnProperty( key ) ) {\r\n\t\tvar waste = obj[ key ];\r\n\t}\r\n}\r\n\r\np3 = performance.now();\r\nconsole.log( \"for...in/hasOwnProperty approach took \" + (p3 - p2) + \" milliseconds.\");
\r\n
\r\n
\r\n

\n\n

In my Firefox I have following results

\n\n\n\n

PS. on Chrome the difference even bigger http://codepen.io/dsheiko/pen/JdrqXa

\n\n

PS2: In ES6 (EcmaScript 2015) you can iterate iterable object nicer:

\n\n

\r\n
\r\n
let map = new Map().set('a', 1).set('b', 2);\r\nfor (let pair of map) {\r\n    console.log(pair);\r\n}\r\n\r\n// OR \r\nlet map = new Map([\r\n    [false, 'no'],\r\n    [true,  'yes'],\r\n]);\r\nmap.forEach((value, key) => {\r\n    console.log(key, value);\r\n});
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b7", + "creator": "Dheeraj Vepakomma", + "createdAt": 1442075500000, + "text": "

If you want to iterate over non-enumerable properties as well, you can use Object.getOwnPropertyNames(obj) to return an array of all properties (enumerable or not) found directly upon a given object.

\n\n

\r\n
\r\n
var obj = Object.create({}, {\r\n  // non-enumerable property\r\n  getFoo: {\r\n    value: function() { return this.foo; },\r\n    enumerable: false\r\n  }\r\n});\r\n\r\nobj.foo = 1; // enumerable property\r\n\r\nObject.getOwnPropertyNames(obj).forEach(function (name) {\r\n  document.write(name + ': ' + obj[name] + '<br/>');\r\n});
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906b8", + "creator": "Marius Bakowski", + "createdAt": 1460633240000, + "text": "

Since ES2015 you can use the for of loop, to access the element directly:

\n\n
// before ES2015\nfor(var key of elements){\n  console.log(elements[key]);\n}\n\n\n// ES2015\nfor(let element of elements){\n  console.log(element);\n}\n
\n\n

Hope this helps someone.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32551082fcc3049e91c87", + "creator": "Hashbrown", + "createdAt": 1467107011000, + "text": "you can actually make this work, @EvanCarroll", + "upvotes": 480, + "upvoterUsernames": [], + "downvotes": 480, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906b9", + "creator": "Nicolas Bouvrette", + "createdAt": 1466215912000, + "text": "

Loops can be pretty interesting when using pure JavaScript. It seems that only ECMA6 (New 2015 JavaScript specification) got the loops under control. Unfortunately as I'm writing this, both Browsers and popular Integrated development environment (IDE) are still struggling to support completely the new bells and whistles.

\n\n

At a glance here is what a JavaScript object loop look like before ECMA6:

\n\n
for (var key in object) {\n  if (p.hasOwnProperty(key)) {\n    var value = object[key];\n    console.log(key); // This is the key;\n    console.log(value); // This is the value;\n  }\n}\n
\n\n

Also, I know this is out of scope with this question but in 2011, ECMAScript 5.1 added the forEach method for Arrays only which basically created a new improved way to loop through arrays while still leaving non iterable objects with the old verbose and confusing for loop. But the odd part is that this new forEach method does not support break which led to all sorts of other problems.

\n\n

Basically in 2011, there is not a real solid way to loop in JavaScript other than what many popular libraries (jQuery, Underscore, etc.) decided to re-implement.

\n\n

As of 2015, we now have a better out of the box way to loop (and break) any object type (including Arrays and Strings). Here is what a loop in JavaScript will eventually look like when the recommendation becomes mainstream:

\n\n
for (let [key, value] of Object.entries(object)) {\n    console.log(key); // This is the key;\n    console.log(value); // This is the value;\n}\n
\n\n

Note that most browsers won't support the code above as of June 18th 2016. Even in Chrome you need to enable this special flag for it to work: chrome://flags/#enable-javascript-harmony

\n\n

Until this becomes the new standard, the old method can still be used but there are also alternatives in popular libraries or even lightweight alternatives for those who aren't using any of these libraries.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32552082fcc3049e91c89", + "creator": "abalter", + "createdAt": 1466266072000, + "text": "I'm in chrome and getting Uncaught TypeError: Object.entries is not a function. Is it not implemented in chrome yet?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32552082fcc3049e91c8b", + "creator": "abalter", + "createdAt": 1466314746000, + "text": "Sorry I missed that about the flag. I see it's not a fully implemented feature yet.", + "upvotes": 4533, + "upvoterUsernames": [], + "downvotes": 4533, + "downvoterUsernames": [] + }, + { + "_id": "62f32552082fcc3049e91c8c", + "creator": "abalter", + "createdAt": 1466370964000, + "text": "I just usually use $.each or forEach along with hasOwnProperty.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32552082fcc3049e91c8d", + "creator": "Tjorriemorrie", + "createdAt": 1468335407000, + "text": "Should mention this is also appropriate if using Babel.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906bb", + "creator": "FieryCod", + "createdAt": 1472288788000, + "text": "

Since es2015 is getting more and more popular I am posting this answer which include usage of generator and iterator to smoothly iterate through [key, value] pairs. As it is possible in other languages for instance Ruby.

\n

Ok here is a code:

\n

\r\n
\r\n
const MyObject = {\n  'a': 'Hello',\n  'b': 'it\\'s',\n  'c': 'me',\n  'd': 'you',\n  'e': 'looking',\n  'f': 'for',\n  [Symbol.iterator]: function*() {\n    for (const i of Object.keys(this)) {\n      yield [i, this[i]];\n    }\n  }\n};\n\nfor (const [k, v] of MyObject) {\n  console.log(`Here is key ${k} and here is value ${v}`);\n}
\r\n
\r\n
\r\n

\n

All information about how can you do an iterator and generator you can find at developer Mozilla page.

\n

Hope It helped someone.

\n

EDIT:

\n

ES2017 will include Object.entries which will make iterating over [key, value] pairs in objects even more easier. It is now known that it will be a part of a standard according to the ts39 stage information.

\n

I think it is time to update my answer to let it became even more fresher than it's now.

\n

\r\n
\r\n
const MyObject = {\n  'a': 'Hello',\n  'b': 'it\\'s',\n  'c': 'me',\n  'd': 'you',\n  'e': 'looking',\n  'f': 'for',\n};\n\nfor (const [k, v] of Object.entries(MyObject)) {\n  console.log(`Here is key ${k} and here is value ${v}`);\n}
\r\n
\r\n
\r\n

\n

You can find more about usage on\nMDN page

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906ba", + "creator": "Tadas V.", + "createdAt": 1469159303000, + "text": "

If anybody needs to loop through arrayObjects with condition:

\n\n

\r\n
\r\n
var arrayObjects = [{\"building\":\"A\", \"status\":\"good\"},{\"building\":\"B\",\"status\":\"horrible\"}];\r\n\r\nfor (var i=0; i< arrayObjects.length; i++) {\r\n  console.log(arrayObjects[i]);\r\n  \r\n  for(key in arrayObjects[i]) {      \r\n    \r\n      if (key == \"status\" && arrayObjects[i][key] == \"good\") {\r\n        \r\n          console.log(key + \"->\" + arrayObjects[i][key]);\r\n      }else{\r\n          console.log(\"nothing found\");\r\n      }\r\n   }\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906bc", + "creator": "Artyom Pranovich", + "createdAt": 1472760942000, + "text": "

Considering ES6 I'd like to add my own spoon of sugar and provide one more approach to iterate over object's properties.

\n\n

Since plain JS object isn't iterable just out of box, we aren't able to use for..of loop for iterating over its content. But no one can stop us to make it iterable.

\n\n

Let's we have book object.

\n\n
let book = {\n  title: \"Amazing book\",\n  author: \"Me\",\n  pages: 3\n}\n\nbook[Symbol.iterator] = function(){\n\n  let properties = Object.keys(this); // returns an array with property names\n  let counter = 0;\n  let isDone = false;\n\n  let next = () => {\n    if(counter >= properties.length){\n      isDone = true;\n    }\n    return { done: isDone, value: this[properties[counter++]] }\n  }\n\n  return { next };\n}\n
\n\n

Since we've made it we can use it this way:

\n\n
for(let pValue of book){\n  console.log(pValue);\n}\n------------------------\nAmazing book\nMe\n3\n
\n\n

Or if you know the power of ES6 generators, so you certainly can make the code above much shorter.

\n\n
book[Symbol.iterator] = function *(){\n\n  let properties = Object.keys(this);\n  for (let p of properties){\n    yield this[p];\n  }\n\n}\n
\n\n

Sure, you can apply such behavior for all objects with making Object iterable on prototype level.

\n\n
Object.prototype[Symbol.iterator] = function() {...}\n
\n\n

Also, objects that comply with the iterable protocol can be used with the new ES2015 feature spread operator thus we can read object property values as an array.

\n\n
let pValues = [...book];\nconsole.log(pValues);\n-------------------------\n[\"Amazing book\", \"Me\", 3]\n
\n\n

Or you can use destructuring assignment:

\n\n
let [title, , pages] = book; // notice that we can just skip unnecessary values\nconsole.log(title);\nconsole.log(pages);\n------------------\nAmazing book\n3\n
\n\n

You can check out JSFiddle with all code I've provided above.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32552082fcc3049e91c92", + "creator": "Pika", + "createdAt": 1473305658000, + "text": "I found the code will generate the values but without keys. Is it possible to iterate the values with keys?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906bf", + "creator": "Jaime Rios", + "createdAt": 1507661853000, + "text": "

I had a similar problem when using Angular, here is the solution that I've found.

\n\n

Step 1. Get all the object keys. using Object.keys. This method returns an array of a given object’s own enumerable properties.

\n\n

Step 2. Create an empty array. This is an where all the properties are going to live, since your new ngFor loop is going to point to this array, we gotta catch them all.\nStep 3. Iterate throw all keys, and push each one into the array you created.\nHere’s how that looks like in code.

\n\n
    // Evil response in a variable. Here are all my vehicles.\nlet evilResponse = { \n  \"car\" : \n    { \n       \"color\" : \"red\",\n       \"model\" : \"2013\"\n    },\n   \"motorcycle\": \n    { \n       \"color\" : \"red\",\n       \"model\" : \"2016\"\n    },\n   \"bicycle\": \n    { \n       \"color\" : \"red\",\n       \"model\" : \"2011\"\n    }\n}\n// Step 1. Get all the object keys.\nlet evilResponseProps = Object.keys(evilResponse);\n// Step 2. Create an empty array.\nlet goodResponse = [];\n// Step 3. Iterate throw all keys.\nfor (prop of evilResponseProps) { \n    goodResponse.push(evilResponseProps[prop]);\n}\n
\n\n

Here is a link to the original post. https://medium.com/@papaponmx/looping-over-object-properties-with-ngfor-in-angular-869cd7b2ddcc

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906be", + "creator": "Biber", + "createdAt": 1479847591000, + "text": "

You can add a simple forEach function to all objects, so you can automatically loop through any object:

\n\n
Object.defineProperty(Object.prototype, 'forEach', {\n    value: function (func) {\n        for (var key in this) {\n            if (!this.hasOwnProperty(key)) {\n                // skip loop if the property is from prototype\n                continue;\n            }\n            var value = this[key];\n            func(key, value);\n        }\n    },\n    enumerable: false\n});\n
\n\n

For those people who don't like the \"for ... in\"-method:

\n\n
Object.defineProperty(Object.prototype, 'forEach', {\n    value: function (func) {\n        var arr = Object.keys(this);\n        for (var i = 0; i < arr.length; i++) {\n            var key = arr[i];\n            func(key, this[key]);\n        }\n    },\n    enumerable: false\n});\n
\n\n

Now, you can simple call:

\n\n
p.forEach (function(key, value){\n    console.log (\"Key: \" + key);\n    console.log (\"Value: \" + value);\n});\n
\n\n

If you don't want to get conflicts with other forEach-Methods you can name it with your unique name.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906bd", + "creator": "Bamieh", + "createdAt": 1474362274000, + "text": "

In ES6 we have well-known symbols to expose some previously internal methods, you can use it to define how iterators work for this object:

\n\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\",\n    *[Symbol.iterator]() {\n        yield *Object.keys(this);\n    }\n};\n\n[...p] //[\"p1\", \"p2\", \"p3\"]\n
\n\n

this will give the same result as using for...in es6 loop.

\n\n
for(var key in p) {\n    console.log(key);\n}\n
\n\n

But its important to know the capabilities you now have using es6!

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32552082fcc3049e91c97", + "creator": "ooo", + "createdAt": 1556861555000, + "text": "A custom object iterator calls the built-in array iterator of an array that generated by Object.keys() and allocated in memory... Cool!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906c1", + "creator": "Muhammad Usman", + "createdAt": 1510862679000, + "text": "

The Object.keys() method returns an array of a given object's own enumerable properties. Read more about it here

\n\n

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\n\r\nObject.keys(p).map((key)=> console.log(key + \"->\" + p[key]))
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906c0", + "creator": "Dan Alboteanu", + "createdAt": 1510589112000, + "text": "

An object becomes an iterator when it implements the .next() method

\n

\r\n
\r\n
const james = {\n  name: 'James',\n  height: `5'10\"`,\n  weight: 185,\n\n  [Symbol.iterator]() {\n    let properties = []\n    for (let key of Object.keys(james)) {\n      properties.push(key);\n    }\n\n    index = 0;\n    return {\n      next: () => {\n        let key = properties[index];\n        let value = this[key];\n        let done = index >= properties.length - 1;\n        index++;\n        return {\n          key,\n          value,\n          done\n        };\n      }\n    };\n  }\n\n};\n\n\nconst iterator = james[Symbol.iterator]();\n\nconsole.log(iterator.next().value); // 'James'\nconsole.log(iterator.next().value); // `5'10`\nconsole.log(iterator.next().value); // 185
\r\n
\r\n
\r\n

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906c2", + "creator": "Harsh Patel", + "createdAt": 1513744977000, + "text": "

Here is another method to iterate through an object.

\n\n

\r\n
\r\n
   var p = {\r\n\"p1\": \"value1\",\r\n\"p2\": \"value2\",\r\n\"p3\": \"value3\"\r\n};\r\n\r\n\r\nObject.keys(p).forEach(key => { console.log(key, p[key]) })
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32552082fcc3049e91c9c", + "creator": "Rolf", + "createdAt": 1520641031000, + "text": "This is pretty cool, however for large objects, the for method might be more performant.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906c4", + "creator": "Zectbumo", + "createdAt": 1519612945000, + "text": "\n\n

\r\n
\r\n
var p = {\"p1\": \"value1\", \"p2\": \"value2\", \"p3\": \"value3\"};\r\n\r\nfor (var key in p) if (p.hasOwnProperty(key)) {\r\n  var value = p[key];\r\n  console.log(key, value);\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906c3", + "creator": "Matas Vaitkevicius", + "createdAt": 1518002538000, + "text": "

If you want to iterate only over properties use one of the answers above, however if you want to iterate over everything including functions, then you might want to use Object.getOwnPropertyNames(obj)

\n\n

\r\n
\r\n
for (let o of Object.getOwnPropertyNames(Math)) {\r\n  console.log(o);\r\n}
\r\n
\r\n
\r\n

\n\n

I sometimes use this to fast test all functions on objects with simple inputs and outputs.

\n", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906c6", + "creator": "yehonatan yehezkel", + "createdAt": 1531152957000, + "text": "

since ES06 you can get the values of an object as array with

\n\n
let arrValues = Object.values( yourObject) ;\n
\n\n

it return the an array of the object values and it not extract values from Prototype!!

\n\n

MDN DOCS Object.values()

\n\n

and for keys ( allready answerd before me here )

\n\n
let arrKeys   = Object.keys(yourObject);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32553082fcc3049e91ca0", + "creator": "Sean Lindo", + "createdAt": 1534878223000, + "text": "The answers asks for a solution that returns both keys and values.", + "upvotes": 1971, + "upvoterUsernames": [], + "downvotes": 1971, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906c7", + "creator": "Ankit", + "createdAt": 1536645301000, + "text": "

In latest ES script, you can do something like this:

\n\n

\r\n
\r\n
let p = {foo: \"bar\"};\r\nfor (let [key, value] of Object.entries(p)) {\r\n  console.log(key, value);\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32553082fcc3049e91ca3", + "creator": "Harsh Phoujdar", + "createdAt": 1600201339000, + "text": "Works as a stand-alone, but does not work if this function returns a value for each for condition", + "upvotes": 5425, + "upvoterUsernames": [], + "downvotes": 5425, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906c5", + "creator": "senthil", + "createdAt": 1530023683000, + "text": "

\r\n
\r\n
    var p =[{\"username\":\"ordermanageadmin\",\"user_id\":\"2\",\"resource_id\":\"Magento_Sales::actions\"},\r\n{\"username\":\"ordermanageadmin_1\",\"user_id\":\"3\",\"resource_id\":\"Magento_Sales::actions\"}]\r\nfor(var value in p) {\r\n    for (var key in value) {\r\n        if (p.hasOwnProperty(key)) {\r\n            console.log(key + \" -> \" + p[key]);\r\n        }\r\n    }\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906c8", + "creator": "nrb", + "createdAt": 1540410663000, + "text": "

Object.entries() function:

\n\n

\r\n
\r\n
var p = {\r\n\t    \"p1\": \"value1\",\r\n\t    \"p2\": \"value2\",\r\n\t    \"p3\": \"value3\"\r\n\t};\r\n\r\nfor (var i in Object.entries(p)){\r\n\tvar key = Object.entries(p)[i][0];\r\n\tvar value = Object.entries(p)[i][1];\r\n\tconsole.log('key['+i+']='+key+' '+'value['+i+']='+value);\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32553082fcc3049e91ca7", + "creator": "nrb", + "createdAt": 1540840961000, + "text": "please don't remove the snippet if you edit my entry", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32553082fcc3049e91ca9", + "creator": "Kamil Kiełczewski", + "createdAt": 1583403911000, + "text": "to increase performance use var e=Object.entries(p) and change Object.entries(p) to e everywhere", + "upvotes": 162, + "upvoterUsernames": [], + "downvotes": 162, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906c9", + "creator": "PythonProgrammi", + "createdAt": 1549517327000, + "text": "

This is how to loop through a javascript object and put the data into a table.\n

\r\n
\r\n
<body>\r\n<script>\r\nfunction createTable(objectArray, fields, fieldTitles) {\r\n  let body = document.getElementsByTagName('body')[0];\r\n  let tbl = document.createElement('table');\r\n  let thead = document.createElement('thead');\r\n  let thr = document.createElement('tr');\r\n\r\n  for (p in objectArray[0]){\r\n    let th = document.createElement('th');\r\n    th.appendChild(document.createTextNode(p));\r\n    thr.appendChild(th);\r\n    \r\n  }\r\n \r\n  thead.appendChild(thr);\r\n  tbl.appendChild(thead);\r\n\r\n  let tbdy = document.createElement('tbody');\r\n  let tr = document.createElement('tr');\r\n  objectArray.forEach((object) => {\r\n    let n = 0;\r\n    let tr = document.createElement('tr');\r\n    for (p in objectArray[0]){\r\n      var td = document.createElement('td');\r\n      td.appendChild(document.createTextNode(object[p]));\r\n      tr.appendChild(td);\r\n      n++;\r\n    };\r\n    tbdy.appendChild(tr);    \r\n  });\r\n  tbl.appendChild(tbdy);\r\n  body.appendChild(tbl)\r\n  return tbl;\r\n}\r\n\r\ncreateTable([\r\n              {name: 'Banana', price: '3.04'}, // k[0]\r\n              {name: 'Orange', price: '2.56'},  // k[1]\r\n              {name: 'Apple', price: '1.45'}\r\n           ])\r\n</script>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906cb", + "creator": "Nicolas Cabanas", + "createdAt": 1572469502000, + "text": "

Using a for-of on Object.keys()

\n

Like:

\n

\r\n
\r\n
let object = {\n  \"key1\": \"value1\",\n  \"key2\": \"value2\",\n  \"key3\": \"value3\"\n};\n\nfor (let key of Object.keys(object)) {\n  console.log(key + \" : \" + object[key])\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906ca", + "creator": "Onera", + "createdAt": 1565680485000, + "text": "

You can also use Object.keys() and iterate over the object keys like below to get the value:

\n\n

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\n\r\nObject.keys(p).forEach((key)=> {\r\n console.log(key +' -> '+ p[key]);\r\n});
\r\n
\r\n
\r\n

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32553082fcc3049e91cac", + "creator": "Swap-IOS-Android", + "createdAt": 1568817588000, + "text": "You saved my time, Thank you", + "upvotes": 995, + "upvoterUsernames": [], + "downvotes": 995, + "downvoterUsernames": [] + }, + { + "_id": "62f32553082fcc3049e91cae", + "creator": "Onera", + "createdAt": 1569491532000, + "text": "Happy to know:)", + "upvotes": 470, + "upvoterUsernames": [], + "downvotes": 470, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906cc", + "creator": "akhtarvahid", + "createdAt": 1578136427000, + "text": "

Multiple way to iterate object in javascript

\n\n

Using for...in loop

\n\n

\r\n
\r\n
 var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\nfor (let key in p){\r\n   if(p.hasOwnProperty(key)){\r\n     console.log(`${key} : ${p[key]}`)\r\n   }\r\n}
\r\n
\r\n
\r\n

\n\n

Using for...of loop

\n\n

\r\n
\r\n
 var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\nfor (let key of Object.keys(p)){\r\n     console.log(`key: ${key} & value: ${p[key]}`)\r\n}
\r\n
\r\n
\r\n

\n\n

Using forEach() with Object.keys, Object.values, Object.entries

\n\n

\r\n
\r\n
var p = {\r\n    \"p1\": \"value1\",\r\n    \"p2\": \"value2\",\r\n    \"p3\": \"value3\"\r\n};\r\nObject.keys(p).forEach(key=>{\r\n   console.log(`${key} : ${p[key]}`);\r\n});\r\nObject.values(p).forEach(value=>{\r\n   console.log(value);\r\n});\r\nObject.entries(p).forEach(([key,value])=>{\r\n    console.log(`${key}:${value}`)\r\n})
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906cd", + "creator": "AmerllicA", + "createdAt": 1582403916000, + "text": "

A good way for looping on an enumerable JavaScript object which could be awesome and common for ReactJS is using Object.keys or Object.entries with using map function. like below:

\n\n
// assume items:\n\nconst items = {\n  first: { name: 'phone', price: 400 },\n  second: { name: 'tv', price: 300 },\n  third: { name: 'sofa', price: 250 },\n};\n
\n\n

For looping and show some UI on ReactJS act like below:

\n\n
~~~\n<div>\n  {Object.entries(items).map(([key, ({ name, price })]) => (\n    <div key={key}>\n     <span>name: {name}</span>\n     <span>price: {price}</span>\n    </div>\n  ))}\n</div>\n
\n\n

Actually, I use the destructuring assignment twice, once for getting key once for getting name and price.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906ce", + "creator": "Akhil Ramani", + "createdAt": 1630155072000, + "text": "

Single line and more readable code can be..

\n
Object.entries(myObject).map(([key, value]) => console.log(key, value))\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32554082fcc3049e91cb0", + "creator": "Nivethan", + "createdAt": 1651585666000, + "text": "good answer and it's more readable than the above solutions but can you explain what happens on the .map(([key, value]) in your answer?", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e906cf", + "creator": "lodey", + "createdAt": 1643901002000, + "text": "
Object.entries(myObject).map(([key, value]) => console.log(key, value))\n
\n

You can try like this. myObject will be {name: "", phone: ""} so and so, this will generate key and value. So key here is name, phone and value are like dog, 123123.

\n

Example {name: "dog"}

\n

Here key is name and value is dog.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906d0", + "creator": "antelove", + "createdAt": 1653390380000, + "text": "

\r\n
\r\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\nvar myMap = new Map( Object.entries(p) );\n\nfor ( let [k, v] of myMap.entries() ) {\n\n  console.log( `${k}: ${v}` )\n  \n}
\r\n
<h3>ECMAScript 2017</h3>\n<p><b>Object.entries()</b> makes it simple to convert <b>Object to Map</b>:</p>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906d1", + "creator": "Ran Turner", + "createdAt": 1654678316000, + "text": "

There are a couple of options:

\n
    \n
  1. You can use for..in for that
  2. \n
\n

\r\n
\r\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\nfor (const item in p) {\n  console.log(`key = ${item}, value = ${p[item]}`);\n}
\r\n
\r\n
\r\n

\n
    \n
  1. You can also call Object.entries() to create an array with all its enumerable properties. after that you can and loop through it using map, foreach or for..of
  2. \n
\n

\r\n
\r\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\n\nObject.entries(p).map(item => {\n  console.log(item)\n})
\r\n
\r\n
\r\n

\n

\r\n
\r\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\n\nObject.entries(p).forEach(item => {\n  console.log(item)\n})
\r\n
\r\n
\r\n

\n

\r\n
\r\n
var p = {\n    \"p1\": \"value1\",\n    \"p2\": \"value2\",\n    \"p3\": \"value3\"\n};\n\n\nfor (const item of Object.entries(p)) {\n  console.log(item)\n}
\r\n
\r\n
\r\n

\n

More about Object.entries()can be found here

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906d2", + "creator": "Isaac Jandalala", + "createdAt": 1655215708000, + "text": "

Pass your object to Object.keys(). This will return an array containing all the keys in the object. You can then loop through the array using map. Using obj[key] where obj is your object and key is the current value in the map iteration, you can get the value for that key/property.

\n
const obj = { name: "Jane", age: 50 };\n\nObject.keys(obj).map( key => {\n    console.log(key, obj[key]);\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e906d3", + "creator": "kerelape", + "createdAt": 1656687264000, + "text": "

You may use my library monadic-objects for that. It's designed to simplify object modificatons and make it behave like arrays:

\n
p.forEach((key, value) => console.log(key, value))\n
\n

Installation:\nnpm i monadic-objects

\n

This library also includes methods:

\n\n

See README for more information!

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2570996, + "uvac": 2571039 + } + }, + { + "_id": "62f321bb082fcc3049e8fecd", + "title": "How do I correctly clone a JavaScript object?", + "title-lowercase": "how do i correctly clone a javascript object?", + "creator": "soundly_typed", + "createdAt": 1239159666000, + "status": "open", + "text": "

I have an object x. I'd like to copy it as object y, such that changes to y do not modify x. I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties. This isn't a problem, since I'm copying one of my own literal-constructed objects.

\n\n

How do I correctly clone a JavaScript object?

\n", + "upvotes": 6170, + "upvoterUsernames": [], + "downvotes": 2622, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2203925, + "answers": 72, + "answerItems": [ + { + "_id": "62f321c2082fcc3049e9063e", + "creator": "Calvin", + "createdAt": 1239160426000, + "text": "

From this article: How to copy arrays and objects in Javascript by Brian Huisman:

\n\n
Object.prototype.clone = function() {\n  var newObj = (this instanceof Array) ? [] : {};\n  for (var i in this) {\n    if (i == 'clone') continue;\n    if (this[i] && typeof this[i] == \"object\") {\n      newObj[i] = this[i].clone();\n    } else newObj[i] = this[i]\n  } return newObj;\n};\n
\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250a082fcc3049e91ba7", + "creator": "iPadDeveloper2011", + "createdAt": 1346109596000, + "text": "Adding to the object prototype like this broke jQuery for me. Even when I renamed to clone2.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f3250a082fcc3049e91ba9", + "creator": "mikemaccana", + "createdAt": 1349088863000, + "text": "@Calvin: this should be created an a non-enumerable property, otherwise 'clone' will appear in 'for' loops.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3250a082fcc3049e91baa", + "creator": "Dan P.", + "createdAt": 1397333813000, + "text": "why isn't var copiedObj = Object.create(obj); a great way as well?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90640", + "creator": "Dan Lew", + "createdAt": 1239160607000, + "text": "

If there are no circular dependencies in your object, I suggest using one of the other answers or jQuery's copy methods, as they all seem quite effective.

\n\n

If there are circular dependencies (i.e., two sub-objects link to each other), you are kind of screwed as there is (from a theoretical perspective) no way to solve this issue elegantly.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9063f", + "creator": "picardo", + "createdAt": 1239160574000, + "text": "
function clone(obj) {\n    if(obj == null || typeof(obj) != 'object')\n        return obj;    \n    var temp = new obj.constructor(); \n    for(var key in obj)\n        temp[key] = clone(obj[key]);    \n    return temp;\n}\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250b082fcc3049e91bb0", + "creator": "james_womack", + "createdAt": 1383338192000, + "text": "Not perfect, but nice for those basic cases. E.g. allowing simple cloning of an argument that can be a basic Object, Array or String.", + "upvotes": 786, + "upvoterUsernames": [], + "downvotes": 786, + "downvoterUsernames": [] + }, + { + "_id": "62f3250b082fcc3049e91bb2", + "creator": "GetFree", + "createdAt": 1434263362000, + "text": "Upvoted for correctly calling the constructor using new. The accepted answer does not.", + "upvotes": 258, + "upvoterUsernames": [], + "downvotes": 258, + "downvoterUsernames": [] + }, + { + "_id": "62f3250b082fcc3049e91bb4", + "creator": "user956584", + "createdAt": 1500158332000, + "text": "works on node everything else ! still left reference links", + "upvotes": 133, + "upvoterUsernames": [], + "downvotes": 133, + "downvoterUsernames": [] + }, + { + "_id": "62f3250b082fcc3049e91bb6", + "creator": "Q10Viking", + "createdAt": 1577705538000, + "text": "The recursive thought is great.But If the value is array,it will work?", + "upvotes": 2070, + "upvoterUsernames": [], + "downvotes": 2070, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90642", + "creator": "Kris Walker", + "createdAt": 1256847846000, + "text": "

One particularly inelegant solution is to use JSON encoding to make deep copies of objects that do not have member methods. The methodology is to JSON encode your target object, then by decoding it, you get the copy you are looking for. You can decode as many times as you want to make as many copies as you need.

\n\n

Of course, functions do not belong in JSON, so this only works for objects without member methods.

\n\n

This methodology was perfect for my use case, since I'm storing JSON blobs in a key-value store, and when they are exposed as objects in a JavaScript API, each object actually contains a copy of the original state of the object so we can calculate the delta after the caller has mutated the exposed object.

\n\n
var object1 = {key:\"value\"};\nvar object2 = object1;\n\nobject2 = JSON.stringify(object1);\nobject2 = JSON.parse(object2);\n\nobject2.key = \"a change\";\nconsole.log(object1);// returns value\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250b082fcc3049e91bb9", + "creator": "the_drow", + "createdAt": 1256848637000, + "text": "Why don't functions belong to JSON? I've seen them transfered as JSON more then once...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3250b082fcc3049e91bbb", + "creator": "abarnert", + "createdAt": 1344909519000, + "text": "@mark: { 'foo': function() { return 1; } } is a literal-constructed object.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90641", + "creator": "Steve Harrison", + "createdAt": 1239162934000, + "text": "

From the Apple JavaScript Coding Guidelines:

\n\n
// Create an inner object with a variable x whose default\n// value is 3.\nfunction innerObj()\n{\n        this.x = 3;\n}\ninnerObj.prototype.clone = function() {\n    var temp = new innerObj();\n    for (myvar in this) {\n        // this object does not contain any objects, so\n        // use the lightweight copy code.\n        temp[myvar] = this[myvar];\n    }\n    return temp;\n}\n\n// Create an outer object with a variable y whose default\n// value is 77.\nfunction outerObj()\n{\n        // The outer object contains an inner object.  Allocate it here.\n        this.inner = new innerObj();\n        this.y = 77;\n}\nouterObj.prototype.clone = function() {\n    var temp = new outerObj();\n    for (myvar in this) {\n        if (this[myvar].clone) {\n            // This variable contains an object with a\n            // clone operator.  Call it to create a copy.\n            temp[myvar] = this[myvar].clone();\n        } else {\n            // This variable contains a scalar value,\n            // a string value, or an object with no\n            // clone function.  Assign it directly.\n            temp[myvar] = this[myvar];\n        }\n    }\n    return temp;\n}\n\n// Allocate an outer object and assign non-default values to variables in\n// both the outer and inner objects.\nouter = new outerObj;\nouter.inner.x = 4;\nouter.y = 16;\n\n// Clone the outer object (which, in turn, clones the inner object).\nnewouter = outer.clone();\n\n// Verify that both values were copied.\nalert('inner x is '+newouter.inner.x); // prints 4\nalert('y is '+newouter.y); // prints 16\n
\n\n

Steve

\n", + "upvotes": 345, + "upvoterUsernames": [], + "downvotes": 345, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90643", + "creator": "Pascal", + "createdAt": 1299047773000, + "text": "

With jQuery, you can shallow copy with extend:

\n\n
var copiedObject = jQuery.extend({}, originalObject)\n
\n\n

subsequent changes to the copiedObject will not affect the originalObject, and vice versa.

\n\n

Or to make a deep copy:

\n\n
var copiedObject = jQuery.extend(true, {}, originalObject)\n
\n", + "upvotes": 1185, + "upvoterUsernames": [], + "downvotes": 375, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90644", + "creator": "Jan Turoň", + "createdAt": 1327601570000, + "text": "

A.Levy's answer is almost complete, here is my little contribution: there is a way how to handle recursive references, see this line

\n\n

if(this[attr]==this) copy[attr] = copy;

\n\n

If the object is XML DOM element, we must use cloneNode instead

\n\n

if(this.cloneNode) return this.cloneNode(true);

\n\n

Inspired by A.Levy's exhaustive study and Calvin's prototyping approach, I offer this solution:

\n\n
Object.prototype.clone = function() {\n  if(this.cloneNode) return this.cloneNode(true);\n  var copy = this instanceof Array ? [] : {};\n  for(var attr in this) {\n    if(typeof this[attr] == \"function\" || this[attr]==null || !this[attr].clone)\n      copy[attr] = this[attr];\n    else if(this[attr]==this) copy[attr] = copy;\n    else copy[attr] = this[attr].clone();\n  }\n  return copy;\n}\n\nDate.prototype.clone = function() {\n  var copy = new Date();\n  copy.setTime(this.getTime());\n  return copy;\n}\n\nNumber.prototype.clone = \nBoolean.prototype.clone =\nString.prototype.clone = function() {\n  return this;\n}\n
\n\n

See also Andy Burke's note in the answers.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250b082fcc3049e91bbf", + "creator": "RobG", + "createdAt": 1417522843000, + "text": "Date.prototype.clone = function() {return new Date(+this)};", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90646", + "creator": "Andy Burke", + "createdAt": 1333087437000, + "text": "

Jan Turoň's answer above is very close, and may be the best to use in a browser due to compatibility issues, but it will potentially cause some strange enumeration issues. For instance, executing:

\n\n
for ( var i in someArray ) { ... }\n
\n\n

Will assign the clone() method to i after iterating through the elements of the array. Here's an adaptation that avoids the enumeration and works with node.js:

\n\n
Object.defineProperty( Object.prototype, \"clone\", {\n    value: function() {\n        if ( this.cloneNode )\n        {\n            return this.cloneNode( true );\n        }\n\n        var copy = this instanceof Array ? [] : {};\n        for( var attr in this )\n        {\n            if ( typeof this[ attr ] == \"function\" || this[ attr ] == null || !this[ attr ].clone )\n            {\n                copy[ attr ] = this[ attr ];\n            }\n            else if ( this[ attr ] == this )\n            {\n                copy[ attr ] = copy;\n            }\n            else\n            {\n                copy[ attr ] = this[ attr ].clone();\n            }\n        }\n        return copy;\n    }\n});\n\nObject.defineProperty( Date.prototype, \"clone\", {\n    value: function() {\n        var copy = new Date();\n        copy.setTime( this.getTime() );\n        return copy;\n    }\n});\n\nObject.defineProperty( Number.prototype, \"clone\", { value: function() { return this; } } );\nObject.defineProperty( Boolean.prototype, \"clone\", { value: function() { return this; } } );\nObject.defineProperty( String.prototype, \"clone\", { value: function() { return this; } } );\n
\n\n

This avoids making the clone() method enumerable because defineProperty() defaults enumerable to false.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90645", + "creator": "itpastorn", + "createdAt": 1332170272000, + "text": "

There are many answers, but none that mentions Object.create from ECMAScript 5, which admittedly does not give you an exact copy, but sets the source as the prototype of the new object.

\n\n

Thus, this is not an exact answer to the question, but it is a one-line solution and thus elegant. And it works best for 2 cases:

\n\n
    \n
  1. Where such inheritance is useful (duh!)
  2. \n
  3. Where the source object won't be modified, thus making the relation between the 2 objects a non issue.
  4. \n
\n\n

Example:

\n\n
var foo = { a : 1 };\nvar bar = Object.create(foo);\nfoo.a; // 1\nbar.a; // 1\nfoo.a = 2;\nbar.a; // 2 - prototype changed\nbar.a = 3;\nfoo.a; // Still 2, since setting bar.a makes it an \"own\" property\n
\n\n

Why do I consider this solution to be superior? It's native, thus no looping, no recursion. However, older browsers will need a polyfill.

\n", + "upvotes": 222, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90647", + "creator": "Rob Evans", + "createdAt": 1338039940000, + "text": "

You can clone an object and remove any reference from the previous one using a single line of code. Simply do:

\n\n
var obj1 = { text: 'moo1' };\nvar obj2 = Object.create(obj1); // Creates a new clone without references\n\nobj2.text = 'moo2'; // Only updates obj2's text property\n\nconsole.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}\n
\n\n

For browsers / engines that do not currently support Object.create you can use this polyfill:

\n\n
// Polyfill Object.create if it does not exist\nif (!Object.create) {\n    Object.create = function (o) {\n        var F = function () {};\n        F.prototype = o;\n        return new F();\n    };\n}\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250b082fcc3049e91bc3", + "creator": "René Nyffenegger", + "createdAt": 1404139764000, + "text": "+1 Object.create(...) seems definitely the way to go.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3250b082fcc3049e91bc5", + "creator": "Ian Lunn", + "createdAt": 1412861101000, + "text": "Works well but what browsers does the polyfill work in?", + "upvotes": 547, + "upvoterUsernames": [], + "downvotes": 547, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90648", + "creator": "heinob", + "createdAt": 1338715791000, + "text": "

I just wanted to add to all the Object.create solutions in this post, that this does not work in the desired way with nodejs.

\n\n

In Firefox the result of

\n\n
var a = {\"test\":\"test\"};\nvar b = Object.create(a);\nconsole.log(b);´\n
\n\n

is

\n\n

{test:\"test\"}.

\n\n

In nodejs it is

\n\n
{}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250b082fcc3049e91bc7", + "creator": "d13", + "createdAt": 1389888947000, + "text": "This is prototypal inheritance, not cloning.", + "upvotes": 118, + "upvoterUsernames": [], + "downvotes": 118, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90649", + "creator": "heinob", + "createdAt": 1338716219000, + "text": "

If you do not use Dates, functions, undefined, regExp or Infinity within your object, a very simple one liner is JSON.parse(JSON.stringify(object)):

\n\n

\r\n
\r\n
const a = {\r\n  string: 'string',\r\n  number: 123,\r\n  bool: false,\r\n  nul: null,\r\n  date: new Date(),  // stringified\r\n  undef: undefined,  // lost\r\n  inf: Infinity,  // forced to 'null'\r\n}\r\nconsole.log(a);\r\nconsole.log(typeof a.date);  // Date object\r\nconst clone = JSON.parse(JSON.stringify(a));\r\nconsole.log(clone);\r\nconsole.log(typeof clone.date);  // result of .toISOString()
\r\n
\r\n
\r\n

\n\n

This works for all kind of objects containing objects, arrays, strings, booleans and numbers.

\n\n

See also this article about the structured clone algorithm of browsers which is used when posting messages to and from a worker. It also contains a function for deep cloning.

\n", + "upvotes": 1855, + "upvoterUsernames": [], + "downvotes": 720, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250c082fcc3049e91bc9", + "creator": "dewd", + "createdAt": 1637685031000, + "text": "sometimes the best answers are the simplest. genius.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3250c082fcc3049e91bcb", + "creator": "Diego Favero", + "createdAt": 1657047819000, + "text": "does the job, but, this is goes against any good programming practice. In brazil, we call that a 'Gambiarra'", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9064a", + "creator": "dule", + "createdAt": 1339631050000, + "text": "

If you're okay with a shallow copy, the underscore.js library has a clone method.

\n\n
y = _.clone(x);\n
\n\n

or you can extend it like

\n\n
copiedObject = _.extend({},originalObject);\n
\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250c082fcc3049e91bcd", + "creator": "Turbo", + "createdAt": 1427862623000, + "text": "Thanks. Using this technique on a Meteor server.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9064c", + "creator": "Bert Regelink", + "createdAt": 1354128527000, + "text": "

Since mindeavor stated that the object to be cloned is a 'literal-constructed' object, a solution might be to simply generate the object multiple times rather than cloning an instance of the object:

\n\n
function createMyObject()\n{\n    var myObject =\n    {\n        ...\n    };\n    return myObject;\n}\n\nvar myObjectInstance1 = createMyObject();\nvar myObjectInstance2 = createMyObject();\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9064e", + "creator": "Alec", + "createdAt": 1357583405000, + "text": "

In my code I frequently define a function (_) to handle copies so that I can pass by value to functions. This code creates a deep copy but maintains inheritance. It also keeps track of sub-copies so that self-referential objects can be copied without an infinite loop. Feel free to use it.

\n\n

It might not be the most elegant, but it hasn't failed me yet.

\n\n
_ = function(oReferance) {\n  var aReferances = new Array();\n  var getPrototypeOf = function(oObject) {\n    if(typeof(Object.getPrototypeOf)!==\"undefined\") return Object.getPrototypeOf(oObject);\n    var oTest = new Object();\n    if(typeof(oObject.__proto__)!==\"undefined\"&&typeof(oTest.__proto__)!==\"undefined\"&&oTest.__proto__===Object.prototype) return oObject.__proto__;\n    if(typeof(oObject.constructor)!==\"undefined\"&&typeof(oTest.constructor)!==\"undefined\"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!==\"undefined\") return oObject.constructor.prototype;\n    return Object.prototype;\n  };\n  var recursiveCopy = function(oSource) {\n    if(typeof(oSource)!==\"object\") return oSource;\n    if(oSource===null) return null;\n    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];\n    var Copy = new Function();\n    Copy.prototype = getPrototypeOf(oSource);\n    var oCopy = new Copy();\n    aReferances.push([oSource,oCopy]);\n    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);\n    return oCopy;\n  };\n  return recursiveCopy(oReferance);\n};\n\n// Examples:\nWigit = function(){};\nWigit.prototype.bInThePrototype = true;\nA = new Wigit();\nA.nCoolNumber = 7;\nB = _(A);\nB.nCoolNumber = 8; // A.nCoolNumber is still 7\nB.bInThePrototype // true\nB instanceof Wigit // true\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9064d", + "creator": "VaZaA", + "createdAt": 1355357107000, + "text": "

Using Lodash:

\n\n
var y = _.clone(x, true);\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250c082fcc3049e91bd2", + "creator": "Dan Ross", + "createdAt": 1378889339000, + "text": "OMG it would be insane to reinvent cloning. This is the only sane answer.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3250c082fcc3049e91bd4", + "creator": "garbanzio", + "createdAt": 1418931617000, + "text": "I prefer _.cloneDeep(x) as it essentially is the same thing as above, but reads better.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9064f", + "creator": "user663031", + "createdAt": 1366080977000, + "text": "

Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data for the W3C's \"Safe passing of structured data\" algorithm, intended to be implemented by browsers for passing data to eg web workers. However, it has some limitations, in that it does not handle functions. See https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm for more information, including an alternative algorithm in JS which gets you part of the way there.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9064b", + "creator": "user1547016", + "createdAt": 1343079828000, + "text": "
function clone(src, deep) {\n\n    var toString = Object.prototype.toString;\n    if(!src && typeof src != \"object\"){\n        //any non-object ( Boolean, String, Number ), null, undefined, NaN\n        return src;\n    }\n\n    //Honor native/custom clone methods\n    if(src.clone && toString.call(src.clone) == \"[object Function]\"){\n        return src.clone(deep);\n    }\n\n    //DOM Elements\n    if(src.nodeType && toString.call(src.cloneNode) == \"[object Function]\"){\n        return src.cloneNode(deep);\n    }\n\n    //Date\n    if(toString.call(src) == \"[object Date]\"){\n        return new Date(src.getTime());\n    }\n\n    //RegExp\n    if(toString.call(src) == \"[object RegExp]\"){\n        return new RegExp(src);\n    }\n\n    //Function\n    if(toString.call(src) == \"[object Function]\"){\n        //Wrap in another method to make sure == is not true;\n        //Note: Huge performance issue due to closures, comment this :)\n        return (function(){\n            src.apply(this, arguments);\n        });\n\n    }\n\n    var ret, index;\n    //Array\n    if(toString.call(src) == \"[object Array]\"){\n        //[].slice(0) would soft clone\n        ret = src.slice();\n        if(deep){\n            index = ret.length;\n            while(index--){\n                ret[index] = clone(ret[index], true);\n            }\n        }\n    }\n    //Object\n    else {\n        ret = src.constructor ? new src.constructor() : {};\n        for (var prop in src) {\n            ret[prop] = deep\n                ? clone(src[prop], true)\n                : src[prop];\n        }\n    }\n\n    return ret;\n};\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250c082fcc3049e91bd7", + "creator": "MikeM", + "createdAt": 1365413664000, + "text": "if(!src && typeof src != "object"){. I think that should be || not &&.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90653", + "creator": "Shuaib", + "createdAt": 1377916903000, + "text": "
function clone(obj)\n{\n    var cloneObj = Object.create(obj);\n\n    return cloneObj;\n}\n
\n\n

In Javascript objects individually inherit another object (Prototypal inheritance). Object.create(obj) returns an object that is a sub-object or child object of obj. In the above function it will effectively return a copy of the object.

\n\n

However, This is a very odd way to clone because I am not using inheritance for its real purpose.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32549082fcc3049e91bd9", + "creator": "d13", + "createdAt": 1389888728000, + "text": "This is prototypal inheritance, not cloning. The new object has none of it's own properties, it simply uses those on the prototype.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32549082fcc3049e91bda", + "creator": "Shuaib", + "createdAt": 1390380953000, + "text": "@d13 I dont need new properties on the new object, i just want properties of the parent object in the new one, so why not inherit?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32549082fcc3049e91bdc", + "creator": "l00k", + "createdAt": 1479136037000, + "text": "@JimboJonny how about recursive Object.create() ?", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90654", + "creator": "Qohelet", + "createdAt": 1378563797000, + "text": "

I came to this page due to the same question but I'm neither using JQuery and none of the clone-Methods worked for my own objects.

\n\n

I'm aware my answer isn't related too strong to this question because it's a different approach. Instead of using clone-functions I use a create function. It worked for me for the following (unfortunately restricting) purposes:

\n\n
    \n
  1. I use mostly JSP-generated Javascript
  2. \n
  3. I know in the beginning which Object must be generated (In my case it's Information from a Database which gets fetched once and needs to be deployed more often in the JS.
  4. \n
\n\n

First I defined my Objects like this:

\n\n
var obj= new Object();\nobj.Type='Row';\nobj.ID=1;\nobj.Value='Blah blah';\n
\n\n

Now I moved everything like:

\n\n
function getObjSelektor(id_nummer,selected){\nvar obj = document.createElement(\"select\");\nobj.setAttribute(\"id\",\"Selektor_\"+id_nummer);\nobj.setAttribute(\"name\",\"Selektor\");\nobj.setAttribute(\"size\",\"1\");\n\nvar obj_opt_1 = document.createElement(\"option\");\nobj_opt_1.setAttribute(\"value\",\"1\");\nif(1==selected)\n    posopval_opt_1.setAttribute(\"selected\",\"selected\");\nobj_opt_1.innerHTML=\"Blah blah\";\nobj.appendChild(obj_opt_1);\n\nvar obj_opt_2 = document.createElement(\"option\");\nobj_opt_2.setAttribute(\"value\",\"2\");\nif(2==selected)\n    obj_opt_2.setAttribute(\"selected\",\"selected\");\nobj_opt_2.innerHTML=\"2nd Row\";\nobj.appendChild(obj_opt_2);\n\n...\n\nreturn obj;\n}\n
\n\n

And call the function in the regular code:

\n\n
myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));\n
\n\n

As said this is a different approach which solved my issue for my purposes.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90655", + "creator": "basis", + "createdAt": 1380091693000, + "text": "

If you got an Object with Functions you can do it with JSONfn, see http://www.eslinstructor.net/jsonfn/.

\n\n
var obj= {\n    name:'Marvin',\n    getName :  function(){\n      return this.name;\n    }\n}\nvar cobj = JSONfn.parse(JSONfn.stringify(obj));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90656", + "creator": "d13", + "createdAt": 1389909169000, + "text": "

You can use functional closure to gain all the benefits of a deep copy, without a deep copy. It's a very different paradigm, but works well. Instead of trying to copy an existing object, just use a function to instantiate a new object when you need one.

\n\n

First, create an function that returns an object

\n\n
function template() {\n  return {\n    values: [1, 2, 3],\n    nest: {x: {a: \"a\", b: \"b\"}, y: 100}\n  };\n}\n
\n\n

Then create a simple shallow copy function

\n\n
function copy(a, b) {\n  Object.keys(b).forEach(function(key) {\n    a[key] = b[key];\n  });\n}\n
\n\n

Create a new object, and copy the template's properties onto it

\n\n
var newObject = {}; \ncopy(newObject, template());\n
\n\n

But the above copy step is not necessary. All you need to do is this:

\n\n
var newObject = template();\n
\n\n

Now that you have a new object, test to see what its properties are:

\n\n
console.log(Object.keys(newObject));\n
\n\n

This displays:

\n\n
[\"values\", \"nest\"]\n
\n\n

Yes, those are the newObject's own properties, not references to properties on another object.\nLet's just check:

\n\n
console.log(newObject.nest.x.b);\n
\n\n

This displays:

\n\n
\"b\"\n
\n\n

The newObject has acquired all of the template object's properties, but is free of any dependency chain.

\n\n

http://jsbin.com/ISUTIpoC/1/edit?js,console

\n\n

I added this example to encourage some debate, so please add some comments :)

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90657", + "creator": "James Koss", + "createdAt": 1391941017000, + "text": "

The problem with copying an object that, eventually, may point at itself, can be solved with a simple check. Add this check, every time there is a copy action. It may be slow, but it should work.

\n\n

I use a toType() function to return the object type, explicitly. I also have my own copyObj() function, which is rather similar in logic, which answers all three Object(), Array(), and Date() cases.

\n\n

I run it in NodeJS.

\n\n

NOT TESTED, YET.

\n\n
// Returns true, if one of the parent's children is the target.\n// This is useful, for avoiding copyObj() through an infinite loop!\nfunction isChild(target, parent) {\n  if (toType(parent) == '[object Object]') {\n    for (var name in parent) {\n      var curProperty = parent[name];\n\n      // Direct child.\n      if (curProperty = target) return true;\n\n      // Check if target is a child of this property, and so on, recursively.\n      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {\n        if (isChild(target, curProperty)) return true;\n      }\n    }\n  } else if (toType(parent) == '[object Array]') {\n    for (var i=0; i < parent.length; i++) {\n      var curItem = parent[i];\n\n      // Direct child.\n      if (curItem = target) return true;\n\n      // Check if target is a child of this property, and so on, recursively.\n      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {\n        if (isChild(target, curItem)) return true;\n      }\n    }\n  }\n\n  return false;     // Not the target.\n}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90659", + "creator": "John Sonderson", + "createdAt": 1414523175000, + "text": "

I've tried this in the case of a scalar object and it works for me:

\n\n
function binder(i) {\n  return function () {\n    return i;\n  };\n}\n\na=1;\nb=binder(a)(); // copy value of a into b\n\nalert(++a); // 2\nalert(b); // still 1\n
\n\n

Regards.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32549082fcc3049e91be1", + "creator": "dandavis", + "createdAt": 1444534390000, + "text": "a=1;b=a;alert(++a);alert(b); // still 1", + "upvotes": 6183, + "upvoterUsernames": [], + "downvotes": 6183, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90658", + "creator": "Lukas Jelinek", + "createdAt": 1409771308000, + "text": "

For those using AngularJS, there is also direct method for cloning or extending of the objects in this library.

\n\n
var destination = angular.copy(source);\n
\n\n

or

\n\n
angular.copy(source, destination);\n
\n\n

More in angular.copy documentation...

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32549082fcc3049e91be3", + "creator": "zamnuts", + "createdAt": 1411122427000, + "text": "This is a deep copy FYI.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9065a", + "creator": "yazjisuhail", + "createdAt": 1415755516000, + "text": "

I've written my own implementation. Not sure if it counts as a better solution:

\n\n
/*\n    a function for deep cloning objects that contains other nested objects and circular structures.\n    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.\n                                    index (z)\n                                         |\n                                         |\n                                         |\n                                         |\n                                         |\n                                         |                      depth (x)\n                                         |_ _ _ _ _ _ _ _ _ _ _ _\n                                        /_/_/_/_/_/_/_/_/_/\n                                       /_/_/_/_/_/_/_/_/_/\n                                      /_/_/_/_/_/_/...../\n                                     /................./\n                                    /.....            /\n                                   /                 /\n                                  /------------------\n            object length (y)    /\n*/\n
\n\n

Following is the implementation:

\n\n
function deepClone(obj) {\n    var depth = -1;\n    var arr = [];\n    return clone(obj, arr, depth);\n}\n\n/**\n *\n * @param obj source object\n * @param arr 3D array to store the references to objects\n * @param depth depth of the current object relative to the passed 'obj'\n * @returns {*}\n */\nfunction clone(obj, arr, depth){\n    if (typeof obj !== \"object\") {\n        return obj;\n    }\n\n    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'\n\n    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object\n    if(result instanceof Array){\n        result.length = length;\n    }\n\n    depth++; // depth is increased because we entered an object here\n\n    arr[depth] = []; // this is the x-axis, each index here is the depth\n    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)\n    // start the depth at current and go down, cyclic structures won't form on depths more than the current one\n    for(var x = depth; x >= 0; x--){\n        // loop only if the array at this depth and length already have elements\n        if(arr[x][length]){\n            for(var index = 0; index < arr[x][length].length; index++){\n                if(obj === arr[x][length][index]){\n                    return obj;\n                }\n            }\n        }\n    }\n\n    arr[depth][length].push(obj); // store the object in the array at the current depth and length\n    for (var prop in obj) {\n        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);\n    }\n\n    return result;\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32549082fcc3049e91be6", + "creator": "Sajuuk", + "createdAt": 1546410802000, + "text": "not working for my object, although my case is a little bit complex.", + "upvotes": 5605, + "upvoterUsernames": [], + "downvotes": 5605, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9065b", + "creator": "Vitalii Fedorenko", + "createdAt": 1430790388000, + "text": "

In ECMAScript 6 there is Object.assign method, which copies values of all enumerable own properties from one object to another. For example:

\n
var x = {myProp: "value"};\nvar y = Object.assign({}, x); \n
\n

But be aware this is a shallow copy - nested objects are still copied as reference.

\n", + "upvotes": 1025, + "upvoterUsernames": [], + "downvotes": 217, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9065c", + "creator": "Mohammed Akdim", + "createdAt": 1440667224000, + "text": "

Interested in cloning simple objects:

\n\n

JSON.parse(JSON.stringify(json_original));

\n\n

Source : How to copy JavaScript object to new variable NOT by reference?

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32549082fcc3049e91be9", + "creator": "Matt H", + "createdAt": 1558725407000, + "text": "Very nice - simple.", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [] + }, + { + "_id": "62f32549082fcc3049e91beb", + "creator": "1-14x0r", + "createdAt": 1586382792000, + "text": "well thats one way. ty never thought of that", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9065d", + "creator": "Eugene Tiurin", + "createdAt": 1450197746000, + "text": "

An elegant way to clone a Javascript object in one line of code

\n

An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.

\n
var clone = Object.assign({}, obj);\n
\n
\n

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

\n
\n

Read more...

\n

The polyfill to support older browsers:

\n
if (!Object.assign) {\n  Object.defineProperty(Object, 'assign', {\n    enumerable: false,\n    configurable: true,\n    writable: true,\n    value: function(target) {\n      'use strict';\n      if (target === undefined || target === null) {\n        throw new TypeError('Cannot convert first argument to object');\n      }\n\n      var to = Object(target);\n      for (var i = 1; i < arguments.length; i++) {\n        var nextSource = arguments[i];\n        if (nextSource === undefined || nextSource === null) {\n          continue;\n        }\n        nextSource = Object(nextSource);\n\n        var keysArray = Object.keys(nextSource);\n        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {\n          var nextKey = keysArray[nextIndex];\n          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);\n          if (desc !== undefined && desc.enumerable) {\n            to[nextKey] = nextSource[nextKey];\n          }\n        }\n      }\n      return to;\n    }\n  });\n}\n
\n", + "upvotes": 277, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91bee", + "creator": "Marcus Junius Brutus", + "createdAt": 1469453272000, + "text": "this will only perform a shallow "cloning"", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91bf0", + "creator": "WinEunuuchs2Unix", + "createdAt": 1650238688000, + "text": "I learned the hard way that objA = objB; causes all kinds of headaches. This seems to have solved the problem, at least for now...", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90660", + "creator": "user3071643", + "createdAt": 1470422977000, + "text": "

Use deepcopy from npm. Works in both the browser and in node as an npm module...

\n\n

https://www.npmjs.com/package/deepcopy

\n\n

let a = deepcopy(b)

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9065f", + "creator": "Gus", + "createdAt": 1468284671000, + "text": "

To handle circular objects that that JSON.stringify can't handle, you can bring in a library called JSOG, that serializes and deserializes arbitrary graphs into JSON format.

\n\n
var clone = JSOG.parse(JSOG.stringify(original));\n
\n\n

It might also be interesting to try patching JSOG for cloning with this trick (don't have time at the moment, but if someone wants to give it a shot...):

\n\n

Serialize a simple function :

\n\n
foo.f = function(a) { return a }\nvar stringForm = foo.f.toString() // \"function (a) { return a }\"\n
\n\n

Deserialize a function :

\n\n
eval(\"foo.f = \" + stringForm)\n
\n\n

Some conventions (probably in the name of the property) to identify functions vs regular strings would be needed (@func_f perhaps).

\n\n

Of course if the function calls a second function the second function will need to exist just as it would for the original.

\n\n

The above however is quite dangerous if you are to accept the serialized form from an untrusted source, but then accepting any function in any form from an untrusted source would be dangerous, so if you're interested in cloning functions trust must have already been established (or you're already intent on writing a security flaw!).

\n\n

Disclaimer: I have not tested the speed of JSOG stringify/parse vs JSON stringify/parse, but It does work on the simple (circular) objects I tested it with.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9065e", + "creator": "musemind", + "createdAt": 1460562214000, + "text": "

You can simply use a spread property to copy an object without references. But be careful (see comments), the 'copy' is just on the lowest object/array level. Nested properties are still references!

\n\n
\n\n

Complete clone:

\n\n
let x = {a: 'value1'}\nlet x2 = {...x}\n\n// => mutate without references:\n\nx2.a = 'value2'\nconsole.log(x.a)    // => 'value1'\n
\n\n

Clone with references on second level:

\n\n
const y = {a: {b: 'value3'}}\nconst y2 = {...y}\n\n// => nested object is still a references:\n\ny2.a.b = 'value4'\nconsole.log(y.a.b)    // => 'value4'\n
\n\n
\n\n

JavaScript actually does not support deep clones natively. Use an utility function. For example Ramda:

\n\n
\n

http://ramdajs.com/docs/#clone

\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91bf2", + "creator": "Kamil Kiełczewski", + "createdAt": 1460622195000, + "text": "This not working... it would work probably when x will be an array for instance x= [ 'ab','cd',...]", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91bf3", + "creator": "Bugs Bunny", + "createdAt": 1463490064000, + "text": "This works, but bear in mind this is a SHALLOW copy, therefore any deep references to others objects remain references!", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91bf5", + "creator": "Cristian Traìna", + "createdAt": 1534181859000, + "text": "A partial clone can also happen in this way: const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90662", + "creator": "Tareq", + "createdAt": 1478538878000, + "text": "

Per MDN:

\n\n\n\n

There is no need for external libraries but you need to check browser compatibility first.

\n", + "upvotes": 471, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91bf7", + "creator": "Tosh", + "createdAt": 1659097225000, + "text": "The problem happens when you have functions in your object JSON.parse(JSON.stringify(a))", + "upvotes": 163, + "upvoterUsernames": [], + "downvotes": 163, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90661", + "creator": "Maciej Bukowski", + "createdAt": 1471571169000, + "text": "

I think, that recurrence with caching is the best what we can do it here without libraries.

\n\n

And underestimated WeakMap comes to the problem of cycles, wherein storing pairs of references to old and new object can help us to recreate pretty easily whole tree.

\n\n

I prevented deep cloning of the DOM elements, probably you don't want to clone entire page :)

\n\n
function deepCopy(object) {\n    const cache = new WeakMap(); // Map of old - new references\n\n    function copy(obj) {\n        if (typeof obj !== 'object' ||\n            obj === null ||\n            obj instanceof HTMLElement\n        )\n            return obj; // primitive value or HTMLElement\n\n        if (obj instanceof Date) \n            return new Date().setTime(obj.getTime());\n\n        if (obj instanceof RegExp) \n            return new RegExp(obj.source, obj.flags);\n\n        if (cache.has(obj)) \n            return cache.get(obj);\n\n        const result = obj instanceof Array ? [] : {};\n\n        cache.set(obj, result); // store reference to object before the recursive starts\n\n        if (obj instanceof Array) {\n            for(const o of obj) {\n                 result.push(copy(o));\n            }\n            return result;\n        }\n\n        const keys = Object.keys(obj); \n\n        for (const key of keys)\n            result[key] = copy(obj[key]);\n\n        return result;\n    }\n\n    return copy(object);\n}\n
\n\n

Some tests:

\n\n
// #1\nconst obj1 = { };\nconst obj2 = { };\nobj1.obj2 = obj2;\nobj2.obj1 = obj1; // Trivial circular reference\n\nvar copy = deepCopy(obj1);\ncopy == obj1 // false\ncopy.obj2 === obj1.obj2 // false\ncopy.obj2.obj1.obj2 // and so on - no error (correctly cloned).\n\n// #2\nconst obj = { x: 0 }\nconst clone = deepCopy({ a: obj, b: obj });\nclone.a == clone.b // true\n\n// #3\nconst arr = [];\narr[0] = arr; // A little bit weird but who cares\nclone = deepCopy(arr)\nclone == arr // false;\nclone[0][0][0][0] == clone // true;\n
\n\n

NOTE: I'm using constants, for of loop, => operator and WeakMaps to create more essential code. This syntax (ES6) is supported by today's browsers

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90664", + "creator": "Erich Horn", + "createdAt": 1486221421000, + "text": "

Ok so this might be the very best option for shallow copying. If follows the many examples using assign, but it also keeps the inheritance and prototype. It's so simple too and works for most array-like and Objects except those with constructor requirements or read-only properties. But that means it fails miserably for TypedArrays, RegExp, Date, Maps, Sets and Object versions of primitives (Boolean, String, etc..).

\n\n
function copy ( a ) { return Object.assign( new a.constructor, a ) }\n
\n\n

Where a can be any Object or class constructed instance, but again not be reliable for thingies that use specialized getters and setters or have constructor requirements, but for more simple situations it rocks. It does work on arguments as well.

\n\n

You can also apply it to primitives to get strange results, but then... unless it just ends up being a useful hack, who cares.

\n\n

results from basic built-in Object and Array...

\n\n
> a = { a: 'A', b: 'B', c: 'C', d: 'D' }\n{ a: 'A', b: 'B', c: 'C', d: 'D' }\n> b = copy( a )\n{ a: 'A', b: 'B', c: 'C', d: 'D' }\n> a = [1,2,3,4]\n[ 1, 2, 3, 4 ]\n> b = copy( a )\n[ 1, 2, 3, 4 ]\n
\n\n

And fails because of mean get/setters, constructor required arguments or read-only properties, and sins against the father.

\n\n
> a = /\\w+/g\n/\\w+/g\n> b = copy( a )  // fails because source and flags are read-only\n/(?:)/\n> a = new Date ( '1/1/2001' )\n2000-12-31T16:00:00.000Z\n> b = copy( a )  // fails because Date using methods to get and set things\n2017-02-04T14:44:13.990Z\n> a = new Boolean( true )\n[Boolean: true]\n> b = copy( a )  // fails because of of sins against the father\n[Boolean: false]\n> a = new Number( 37 )\n[Number: 37]\n> b = copy( a )  // fails because of of sins against the father\n[Number: 0]\n> a = new String( 'four score and seven years ago our four fathers' )\n[String: 'four score and seven years ago our four fathers']\n> b = copy( a )  // fails because of of sins against the father\n{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90663", + "creator": "Charles Merriam", + "createdAt": 1481888073000, + "text": "

New answer to an old question! If you have the pleasure of having using ECMAScript 2016 (ES6) with Spread Syntax, it's easy.

\n\n
keepMeTheSame = {first: \"Me!\", second: \"You!\"};\ncloned = {...keepMeTheSame}\n
\n\n

This provides a clean method for a shallow copy of an object. Making a deep copy, meaning makign a new copy of every value in every recursively nested object, requires on of the heavier solutions above.

\n\n

JavaScript keeps evolving.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91bfb", + "creator": "Petr Marek", + "createdAt": 1486332414000, + "text": "it doesn't work when you have functions defined on objects", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91bfd", + "creator": "manikant gautam", + "createdAt": 1512454660000, + "text": "@Oleh so use ` {... obj} instead of [...obj];`", + "upvotes": 1177, + "upvoterUsernames": [], + "downvotes": 1177, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90665", + "creator": "João Oliveira", + "createdAt": 1492114631000, + "text": "

In ES-6 you can simply use Object.assign(...).\nEx:

\n\n
let obj = {person: 'Thor Odinson'};\nlet clone = Object.assign({}, obj);\n
\n\n

A good reference is here:\nhttps://googlechrome.github.io/samples/object-assign-es6/

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91c00", + "creator": "August", + "createdAt": 1496295106000, + "text": "It does not deep clone the object.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91c02", + "creator": "HoldOffHunger", + "createdAt": 1502899502000, + "text": "That's an assignment, not a copy. clone.Title = "just a clone" means that obj.Title = "just a clone".", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90666", + "creator": "flori", + "createdAt": 1494586179000, + "text": "
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)\n
\n\n

ES6 solution if you want to (shallow) clone a class instance and not just a property object.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254a082fcc3049e91c05", + "creator": "ceztko", + "createdAt": 1558437925000, + "text": "How this is different from let cloned = Object.assign({}, obj) ?", + "upvotes": 2129, + "upvoterUsernames": [], + "downvotes": 2129, + "downvoterUsernames": [] + }, + { + "_id": "62f3254a082fcc3049e91c07", + "creator": "flori", + "createdAt": 1609617494000, + "text": "@ceztko When obj is a class instance, Object.assign() does not clone e.g. class methods (because they are not enumerable).", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90667", + "creator": "Marco", + "createdAt": 1495203034000, + "text": "

Ok, I know it has many answers, but no one pointed out, EcmaScript5 has assign method, work on FF and Chrome, it copies enumerable and own properties and Symbols.

\n\n

Object Assign

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254b082fcc3049e91c09", + "creator": "Naomi", + "createdAt": 1502740204000, + "text": "No support in IE :(", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90668", + "creator": "Alireza", + "createdAt": 1499349003000, + "text": "

OK, imagine you have this object below and you want to clone it:

\n\n
let obj = {a:1, b:2, c:3}; //ES6\n
\n\n

or

\n\n
var obj = {a:1, b:2, c:3}; //ES5\n
\n\n

the answer is mainly depeneds on which ECMAscript you using, in ES6+, you can simply use Object.assign to do the clone:

\n\n
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};\n
\n\n

or using spread operator like this:

\n\n
let cloned = {...obj}; //new {a:1, b:2, c:3};\n
\n\n

But if you using ES5, you can use few methods, but the JSON.stringify, just make sure you not using for a big chunk of data to copy, but it could be one line handy way in many cases, something like this:

\n\n
let cloned = JSON.parse(JSON.stringify(obj)); \n//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over\n
\n", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 67, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254b082fcc3049e91c0c", + "creator": "user1063287", + "createdAt": 1536923087000, + "text": "Can you please give example of what big chunk of data would equate to? 100kb? 100MB? Thanks!", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + }, + { + "_id": "62f3254b082fcc3049e91c0e", + "creator": "Bogdan D", + "createdAt": 1559137374000, + "text": "Object.assign makes a shallow copy (just as the spread, @Alizera)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3254b082fcc3049e91c10", + "creator": "Womble", + "createdAt": 1588345005000, + "text": "You can't use let in es5 :^) @Alireza", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90669", + "creator": "knowingpark", + "createdAt": 1508219194000, + "text": "

I don't know which cases this doesn't work for, but it got me a copy of an array. I think its cute :) Hope it helps

\n\n
copiedArr = origArr.filter(function(x){return true})\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066a", + "creator": "ryanpcmcquen", + "createdAt": 1508912109000, + "text": "

Here's a modern solution that doesn't have the pitfalls of Object.assign() (does not copy by reference):

\n\n
const cloneObj = (obj) => {\n    return Object.keys(obj).reduce((dolly, key) => {\n        dolly[key] = (obj[key].constructor === Object) ?\n            cloneObj(obj[key]) :\n            obj[key];\n        return dolly;\n    }, {});\n};\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066c", + "creator": "Edward Brey", + "createdAt": 1519420934000, + "text": "

If you are using TypeScript, need to support older web browsers (and so can't use Object.assign), and aren't using a library with a clone method build in, you can make yourself a combine helper in a few lines of code. It combines objects, and if you have only one, just clones it.

\n\n
/** Creates a new object that combines the properties of the specified objects. */\nfunction combine(...objs: {}[]) {\n    const combined = {};\n    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));\n    return combined;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066b", + "creator": "j rdl", + "createdAt": 1510304878000, + "text": "

If your object is a class (e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes):

\n\n
var copiedObject = jQuery.extend(true, {}, originalObject);\ncopiedObject.__proto__ = originalObject.__proto__;\n
\n\n

Then in copiedObject you have a deep-copied instance of originalObject class with all its methods.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066d", + "creator": "Rajib Chy", + "createdAt": 1522151070000, + "text": "

You may clone your Object without modification parent Object -

\n\n
    /** [Object Extend]*/\n    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {\n        for ( var property in source )\n            destination[property] = source[property];\n        return destination;\n    } ) );\n    /** [/Object Extend]*/\n    /** [Object clone]*/\n    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {\n        return this.extend( {}, object );\n    } ) );\n    /** [/Object clone]*/\n\n    let myObj = {\n        a:1, b:2, c:3, d:{\n            a:1, b:2, c:3\n        }\n    };\n\n    let clone = Object.clone( myObj );\n\n    clone.a = 10;\n\n    console.log('clone.a==>', clone.a); //==> 10\n\n    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here\n\n    let clone2 = Object.clone( clone );\n\n    clone2.a = 20;\n\n    console.log('clone2.a==>', clone2.a); //==> 20\n\n    console.log('clone.a==>', clone.a); //==> 10 // object not modified here\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066e", + "creator": "HoldOffHunger", + "createdAt": 1524840284000, + "text": "

I'm providing an answer to this question, because I do not see any native, recursive implementations here that resolve the problem of DOM elements.

\n\n

The problem there is that <element> has parent and child attributes, that link to other elements with parent and child values, which point back to the original <element>, causing either an infinite recursive or cyclic redundancy.

\n\n

If your object is something safe and simple like

\n\n
{\n    '123':456\n}\n
\n\n

...then any other answer here will probably work.

\n\n

But if you have...

\n\n
{\n    '123':<reactJSComponent>,\n    '456':document.createElement('div'),\n}\n
\n\n

...then you need something like this:

\n\n
    // cloneVariable() : Clone variable, return null for elements or components.\nvar cloneVariable = function (args) {\n    const variable = args.variable;\n\n    if(variable === null) {\n            return null;\n    }\n\n    if(typeof(variable) === 'object') {\n            if(variable instanceof HTMLElement || variable.nodeType > 0) {\n                    return null;\n            }\n\n            if(Array.isArray(variable)) {\n                    var arrayclone = [];\n\n                    variable.forEach((element) => {\n                            arrayclone.push(cloneVariable({'variable':element}));\n                    });\n\n                    return arrayclone;\n            }\n\n            var objectclone = {};\n\n            Object.keys(variable).forEach((field) => {\n                    objectclone[field] = cloneVariable({'variable':variable[field]});\n            });\n\n            return objectclone;\n    }\n\n    return variable;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9066f", + "creator": "Pavan Garre", + "createdAt": 1533311705000, + "text": "
const objClone = { ...obj };\n
\n

Be aware that nested objects are still copied as a reference.

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254b082fcc3049e91c15", + "creator": "Dan Dascalescu", + "createdAt": 1560469044000, + "text": "This is ES2016, not 2018, and this answer was given two years earlier.", + "upvotes": 696, + "upvoterUsernames": [], + "downvotes": 696, + "downvoterUsernames": [] + }, + { + "_id": "62f3254b082fcc3049e91c16", + "creator": "Sunil Garg", + "createdAt": 1567584723000, + "text": "so what if i want copy of nested property as well", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [] + }, + { + "_id": "62f3254b082fcc3049e91c17", + "creator": "Pavan Garre", + "createdAt": 1567777531000, + "text": "@SunilGarg To copy nested property as well you can use const objDeepClone = JSON.parse(JSON.stringify(obj));", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90670", + "creator": "webpreneur", + "createdAt": 1541766245000, + "text": "

According to the Airbnb JavaScript Style Guide with 404 contributors:

\n\n
\n

Prefer the object spread operator over Object.assign to shallow-copy\n objects. Use the object rest operator to get a new object with certain\n properties omitted.

\n
\n\n
// very bad\nconst original = { a: 1, b: 2 };\nconst copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ\ndelete copy.a; // so does this\n\n// bad\nconst original = { a: 1, b: 2 };\nconst copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }\n\n// good\nconst original = { a: 1, b: 2 };\nconst copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }\n\nconst { a, ...noA } = copy; // noA => { b: 2, c: 3 }\n
\n\n

Also I'd like to warn you that even though Airbnb hardly recommends the object spread operator approach. Keep in mind that Microsoft Edge still does not support this 2018 feature yet.

\n\n

ES2016+ Compat table >>

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90672", + "creator": "aberaud", + "createdAt": 1548272347000, + "text": "

Using defaults (historically specific to nodejs but now usable from the browser thanks to modern JS):

\n\n
import defaults from 'object.defaults';\n\nconst myCopy = defaults({}, myObject);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90671", + "creator": "ConductedClever", + "createdAt": 1547276658000, + "text": "

I think there is a simple and working answer. In deep copying there are two concerns:

\n\n
    \n
  1. Keep properties independent to each other.
  2. \n
  3. And keep the methods alive on cloned object.
  4. \n
\n\n

So I think one simple solution will be to first serialize and deserialize and then do an assign on it to copy functions too.

\n\n
let deepCloned = JSON.parse(JSON.stringify(source));\nlet merged = Object.assign({}, source);\nObject.assign(merged, deepCloned);\n
\n\n

Although this question has many answers, I hope this one helps too.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254c082fcc3049e91c1b", + "creator": "ConductedClever", + "createdAt": 1547359307000, + "text": "Although if I am permitted to import lodash, I prefer using lodash cloneDeep.", + "upvotes": 1416, + "upvoterUsernames": [], + "downvotes": 1416, + "downvoterUsernames": [] + }, + { + "_id": "62f3254c082fcc3049e91c1d", + "creator": "Misha", + "createdAt": 1550850624000, + "text": "I'm using JSON.parse(JSON.stringify(source)). Always working.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3254c082fcc3049e91c1f", + "creator": "ConductedClever", + "createdAt": 1550899619000, + "text": "@Misha, this way you will miss the functions. The term 'works' has many meanings.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90673", + "creator": "Nishant Dwivedi", + "createdAt": 1558341311000, + "text": "

For a deep copy and clone, JSON.stringify then JSON.parse the object:

\n\n
obj = { a: 0 , b: { c: 0}};\nlet deepClone = JSON.parse(JSON.stringify(obj));\nobj.a = 5;\nobj.b.c = 5;\nconsole.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254c082fcc3049e91c20", + "creator": "Aleks ", + "createdAt": 1563674173000, + "text": "pretty clever... any downsides to this approach?", + "upvotes": 1461, + "upvoterUsernames": [], + "downvotes": 1461, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90674", + "creator": "Pouria Moosavi", + "createdAt": 1560700700000, + "text": "

Just as this link says use this code:

\n\n
let clone = Object.create(Object.getPrototypeOf(obj),\n Object.getOwnPropertyDescriptors(obj));\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254c082fcc3049e91c23", + "creator": "Exlord", + "createdAt": 1560918211000, + "text": "this is exactly the same as Object.assign(newObj, obj), its a shallow copy", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90676", + "creator": "Ashok R", + "createdAt": 1565839442000, + "text": "
\n

Use lodash _.cloneDeep().

\n
\n\n

Shallow Copy: lodash _.clone()

\n\n

A shallow copy can be made by simply copying the reference.

\n\n
let obj1 = {\n    a: 0,\n    b: {\n        c: 0,\n        e: {\n            f: 0\n        }\n    }\n};\nlet obj3 = _.clone(obj1);\nobj1.a = 4;\nobj1.b.c = 4;\nobj1.b.e.f = 100;\n\nconsole.log(JSON.stringify(obj1));\n//{\"a\":4,\"b\":{\"c\":4,\"e\":{\"f\":100}}}\n\nconsole.log(JSON.stringify(obj3));\n//{\"a\":0,\"b\":{\"c\":4,\"e\":{\"f\":100}}}\n
\n\n

\"Shallow

\n\n

Deep Copy: lodash _.cloneDeep()

\n\n

fields are dereferenced: rather than references to objects being copied

\n\n
let obj1 = {\n    a: 0,\n    b: {\n        c: 0,\n        e: {\n            f: 0\n        }\n    }\n};\nlet obj3 = _.cloneDeep(obj1);\nobj1.a = 100;\nobj1.b.c = 100;\nobj1.b.e.f = 100;\n\nconsole.log(JSON.stringify(obj1));\n{\"a\":100,\"b\":{\"c\":100,\"e\":{\"f\":100}}}\n\nconsole.log(JSON.stringify(obj3));\n{\"a\":0,\"b\":{\"c\":0,\"e\":{\"f\":0}}}\n
\n\n

\"Deep

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90677", + "creator": "GANESH CHOKHARE", + "createdAt": 1568183838000, + "text": "

Object copy using ( ... )

\n\n
//bad\nconst original = { a: 1, b: 2 };\nconst copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }\n\n//good\nconst originalObj = { id: 5, name: 'San Francisco'};\nconst copyObject = {...originalObj, pincode: 4444};\nconsole.log(copyObject)  //{ id: 5, name: 'San Francisco', pincode: 4444 }\n
\n\n

Same can be use for copying array from one to other

\n\n
const itemsCopy = [...items];\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254c082fcc3049e91c27", + "creator": "MrSegFaulty", + "createdAt": 1624025040000, + "text": "Why is Object.assign way labeled as 'bad'? Could someone elaborate?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90678", + "creator": "Fouad Boukredine", + "createdAt": 1574547495000, + "text": "
var x = {'e': 2, 'd': 8, 'b': 5};\n\nconst y = {};\nfor(let key in x) {\n    y[key] = x[key];\n}\nconsole.log(y); // =>>> {e: 2, d: 8, b: 5}\n\nconst z = {};\nObject.keys(x).forEach(key => {\n    z[key] = x[key];\n});\nconsole.log(z); // =>>> {e: 2, d: 8, b: 5}\n\nconst w = {};\nfor(let i = 0; i < Object.keys(x).length; i++) {\n    w[Object.keys(x)[i]] = x[Object.keys(x)[i]];\n}\nconsole.log(w); // =>>> {e: 2, d: 8, b: 5}\n\nconst v = {};\nfor(let key of Object.keys(x)) {\n    v[key] = x[key];\n}\nconsole.log(v); // =>>> {e: 2, d: 8, b: 5}\n\nx['q'] = 100;   // Altering x will not affect the other objects\n\nconsole.log(x); // =>>> {e: 2, d: 8, b: 5, q: 100}\nconsole.log(y); // =>>> {e: 2, d: 8, b: 5}\nconsole.log(z); // =>>> {e: 2, d: 8, b: 5}\nconsole.log(w); // =>>> {e: 2, d: 8, b: 5}\nconsole.log(v); // =>>> {e: 2, d: 8, b: 5}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90675", + "creator": "Louis Christopher", + "createdAt": 1565612594000, + "text": "

Simple

\n\n
var restore = { name:'charlesi',\nage:9}\nvar prev_data ={\nname: 'charles'\nage : 10\n}\n\nvar temp = JSON.stringify(prev_data)\nrestore = JSON.parse(temp)\n\nrestore = {\nname:'charlie',\nage : 12}\n
\n\n

output prev_data:

\n\n
{\nname: 'charles'\nage : 10\n} \n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90679", + "creator": "Raskolnikov", + "createdAt": 1577850532000, + "text": "

Simple recursive method to clone an object. Also could use lodash.clone.

\n\n

\r\n
\r\n
let clone = (obj) => {\r\n\tlet obj2 = Array.isArray(obj) ? [] : {};\r\n\tfor(let k in obj) {\r\n          obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) :  obj[k];\r\n        }\r\n        return obj2;\r\n    }\r\n\r\nlet w = { name: \"Apple\", types: [\"Fuji\", \"Gala\"]};\r\nlet x = clone(w);\r\nw.name = \"Orange\";\r\nw.types = [\"Navel\"];\r\nconsole.log(x);\r\nconsole.log(w);
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9067a", + "creator": "asoni94", + "createdAt": 1579511385000, + "text": "

The solution JSON.parse(JSON.stringify(orig_obj) as stated by many peers here for deep_cloning has several issues which I found, and they are listed below:

\n\n
    \n
  1. It discards the entries while copying whose values are undefined in the original object,
  2. \n
  3. If there are some values like Infinity, NaN etc, they will be converted into null while copying,
  4. \n
  5. If there is a Date type in the original object, it will be stringified in the cloned object (typeof date_entry --> string).
  6. \n
\n\n

Found an effective way for cloning an object, and it worked well for me in all sort of scenarios. Please have a look at below code, as it has resolved all above mentioned pitfalls of JSON.parse(...), yet resulting in proper deep-cloning:

\n\n
var orig_obj = {\n  string: 'my_str',\n  number: 123,\n  bool: false,\n  nul: null,\n  nested : {\n    value : true\n  },\n  nan : NaN,\n  date: new Date(), \n  undef: undefined,\n  inf: Infinity,\n}\nconsole.log(\"original_obj before modification: \", orig_obj, \"\\n\");\nconsole.log(typeof orig_obj.date, \"\\n\");\n\nvar clone_obj = Object.assign({}, orig_obj);\n\n//this below loop will help in deep cloning and solving above issues\nfor(let prop in orig_obj) {\n    if(typeof orig_obj[prop] === \"object\") {\n        if(orig_obj[prop] instanceof Date)\n            clone_obj[prop] = orig_obj[prop];\n        else {\n            clone_obj[prop] = JSON.parse(JSON.stringify(orig_obj[prop]));\n        }\n    }\n}\n\nconsole.log(\"cloned_obj before modification: \", orig_obj, \"\\n\");\n\nclone_obj.bool = true;\nclone_obj.nested.value = \"false\";\n\nconsole.log(\"original_obj post modification: \", orig_obj, \"\\n\");\nconsole.log(\"cloned_obj post modification: \", clone_obj, \"\\n\");\nconsole.log(typeof clone_obj.date);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9067b", + "creator": "Mohsen Alyafei", + "createdAt": 1594017914000, + "text": "

Update 06 July 2020

\n

There are three (3) ways to clone objects in JavaScript. As objects in JavaScript are reference values, you can't simply just copy using the =.

\n

The ways are:

\n
const food = { food: 'apple', drink: 'milk' }\n\n\n// 1. Using the "Spread"\n// ------------------\n\n{ ...food }\n\n\n// 2. Using "Object.assign"\n// ------------------\n\nObject.assign({}, food)\n\n\n// 3. "JSON"\n// ------------------\n\nJSON.parse(JSON.stringify(food))\n\n// RESULT:\n// { food: 'apple', drink: 'milk' }\n\n
\n

This can be used as a reference summary.

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254d082fcc3049e91c2e", + "creator": "Andreas", + "createdAt": 1594023885000, + "text": "And this adds what new/unique information to this question?", + "upvotes": 1788, + "upvoterUsernames": [], + "downvotes": 1788, + "downvoterUsernames": [] + }, + { + "_id": "62f3254d082fcc3049e91c2f", + "creator": "Andreas", + "createdAt": 1594023918000, + "text": "The JSON approach would remove any methods of the object", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3254d082fcc3049e91c30", + "creator": "Tchakabam", + "createdAt": 1622802697000, + "text": "the spread syntax is syntactical sugar (for Object.assign in this case). this answer is misleading. –", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9067c", + "creator": "rela589n", + "createdAt": 1597324787000, + "text": "

The most correct to copy object is use Object.create:

\n
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n
\n

Such notation will make identically the same object with correct prototype and hidden properties.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9067d", + "creator": "Dere Sagar", + "createdAt": 1622600754000, + "text": "

Ways to Copy Objects in JavaScript

\n
    \n
  1. Use the spread (...) syntax
  2. \n
  3. Use the Object.assign() method
  4. \n
  5. Use the JSON.stringify() and JSON.parse() methods
  6. \n
\n
const person = {\n    firstName: 'John',\n    lastName: 'Doe'\n};\n\n// using spread ...\nlet p1 = {\n    ...person\n};\n\n// using  Object.assign() method\nlet p2 = Object.assign({}, person);\n\n// using JSON\nlet p3 = JSON.parse(JSON.stringify(person));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254d082fcc3049e91c33", + "creator": "Tchakabam", + "createdAt": 1622802508000, + "text": "the spread syntax is syntactical sugar (for Object.assign in this case). this answer is misleading.", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [] + }, + { + "_id": "62f3254d082fcc3049e91c34", + "creator": "Eduardo Mior", + "createdAt": 1624405209000, + "text": "For quick testing Object.assign({}, person); it's perfect", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9067e", + "creator": "Vivek sharma", + "createdAt": 1624374007000, + "text": "

You can use rest operator to clone arrays or objects

\n
let myObj = {1: 100, 'a': 200};\n\nlet clone = {...myObj}; \n\nclone.a = 300;\n\nconsole.log(clone.a) // Output :- 300\nconsole.log(myObj.a) // Output :- 200\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9067f", + "creator": "Radim Šafrán", + "createdAt": 1630052869000, + "text": "

This makes new copy of your obj (not just reference).

\n
let myCopy = JSON.parse(JSON.stringify(obj)); \n
\n

..Works much efficiently then the _.cloneDeep(obj).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90680", + "creator": "FAHAD SIDDIQUI", + "createdAt": 1630053531000, + "text": "

I have gone through all above solutions and they are quite well. However, there is another approach that you can use to clone object (with values not reference). Object.assign

\n
let x = {\n    a: '1',\n    b: '2'\n}\n\nlet y = Object.assign({}, x)\ny.a = "3"\n\nconsole.log(x)\n
\n

The output will be

\n
{ a: '1', b: '2' }\n
\n

Moreover, you can also clone array with the same approach.

\n
clonedArray = Object.assign([], array)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90681", + "creator": "chickens", + "createdAt": 1641139232000, + "text": "

Native JS:

\n
const shallowClone = {...originalObj};\nconst deepClone = JSON.parse(JSON.stringify(originalObj));\n
\n

Using Libraries:

\n
// Lodash\nconst shallowClone = _.clone(originalObj);\nconst deepClone = _. cloneDeep(originalObj);\n\n// JQuery\nconst shallowClone = jQuery.extend({}, originalObj);\nconst deepClone = jQuery.extend(true, {}, originalObj);\n\n// Angular\nconst deepClone = angular.copy(originalObj);\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90682", + "creator": "barhatsor", + "createdAt": 1646162922000, + "text": "

Short and sweet:

\n
let clone = Object.fromEntries(Object.entries(obj));\n
\n

Demo:

\n

\r\n
\r\n
let obj = {a: 'b'};\nlet clone = Object.fromEntries(Object.entries(obj));\n\nclone.a = 'c';\n\nconsole.log(obj, clone);
\r\n
\r\n
\r\n

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90683", + "creator": "Ran Turner", + "createdAt": 1650616173000, + "text": "

Using the spread syntax performs a shallow copy of the object. This means that none of the nested object instances are cloned as you can see in the following example with the nested object child

\n

\r\n
\r\n
const user1 = { \n    name: 'Alex',\n    address: '15th Park Avenue',\n    age: 43,\n    child:{\n        name: 'John'\n    }\n}\n\nconst user2 = {...user1};\n\nuser1.child.name = 'chris';\n\nconsole.log(user1);\nconsole.log(user2);
\r\n
\r\n
\r\n

\n

To solve this nested object problem and perform a deep copy we can use JSON.parse(JSON.stringify(someObject))

\n

\r\n
\r\n
const user1 = { \n    name: 'Alex',\n    address: '15th Park Avenue',\n    age: 43,\n    child:{\n        name: 'John'\n    }\n}\n\nconst user2 = JSON.parse(JSON.stringify(user1));\n\nuser1.child.name = 'chris';\n\nconsole.log(user1);\nconsole.log(user2);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90684", + "creator": "uingtea", + "createdAt": 1656720542000, + "text": "

The different

\n

Only copy top level: {...object} and Object.assign({}, object)

\n

\r\n
\r\n
let objA = {\n  a: \"keyA\",\n  b: {\n    c: \"keyC\",\n  }\n}\nlet objB = Object.assign({}, objA); // or  {...objB}\n// change objB\nobjB.a = \"Change objA.a (top)\"\nconsole.log(\"objA.a (top) No Change:\\n\" + JSON.stringify(objA, false, 2));\n\nobjB.b.c = \"change should be only for objB.b.c but it in objA.b.c\"\nconsole.log(\"objA.a.c second level has Change:\\n\" + JSON.stringify(objA, false, 2));
\r\n
\r\n
\r\n

\n

for deep copy use structuredClone() 2022 or JSON.parse(JSON.stringify(object)) for old browser, easy without hack.

\n

\r\n
\r\n
let objA = {\n  a: \"keyA\",\n  b: {\n    c: \"keyC\",\n  }\n}\nlet objB = typeof structuredClone == 'function' ?\n  structuredClone(objA) : JSON.parse(JSON.stringify(objA));\n// change objB\nobjB.a = \"Change objA.a (top)\"\nobjB.b.c = \"change should be only for objB.c but it in objA.c\"\n\nconsole.log(\"objA has no Change:\\n\" + JSON.stringify(objA, false, 2));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90685", + "creator": "Pulkit Chaudhri", + "createdAt": 1657621360000, + "text": "

I've had an issue when copying objects. This is because when you do following, you're only making a 'reference' to the object and when the source object value is updated later, the copy object that was cloned also changes value because it was merely a 'reference' and hence you see multiple values of the last changes to the source object.

\n
let x = { a: 1 };\nlet y = x; // y is a reference to x, so if x changes y also changes and v/v\n
\n

So, to tackle this issue you do the following:

\n
let y = JSON.parse(JSON.stringify(x)); //see Note below\n
\n

The other way to prevent references is by doing the following:

\n
let x = { a: 1 };\nlet y = Object.assign({}, x); // Object.assign(target, ...sources)\n\ny.a = 2;\nconsole.log(x); // { a: 1 }\nconsole.log(y); // { a: 2 }\n
\n

Note: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90651", + "creator": "public override", + "createdAt": 1374940408000, + "text": "
//\n// creates 'clone' method on context object\n//\n//  var \n//     clon = Object.clone( anyValue );\n//\n!((function (propertyName, definition) {\n    this[propertyName] = definition();\n}).call(\n    Object,\n    \"clone\",\n    function () {\n        function isfn(fn) {\n            return typeof fn === \"function\";\n        }\n\n        function isobj(o) {\n            return o === Object(o);\n        }\n\n        function isarray(o) {\n            return Object.prototype.toString.call(o) === \"[object Array]\";\n        }\n\n        function fnclon(fn) {\n            return function () {\n                fn.apply(this, arguments);\n            };\n        }\n\n        function owns(obj, p) {\n            return obj.hasOwnProperty(p);\n        }\n\n        function isemptyobj(obj) {\n            for (var p in obj) {\n                return false;\n            }\n            return true;\n        }\n\n        function isObject(o) {\n            return Object.prototype.toString.call(o) === \"[object Object]\";\n        }\n        return function (input) {\n            if (isfn(input)) {\n                return fnclon(input);\n            } else if (isobj(input)) {\n                var cloned = {};\n                for (var p in input) {\n                    owns(Object.prototype, p)\n                    || (\n                        isfn(input[p])\n                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )\n                        || ( cloned[p] = input[p] )\n                    );\n                }\n                if (isarray(input)) {\n                    cloned.length = input.length;\n                    \"concat every filter forEach indexOf join lastIndexOf map pop push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift\"\n                    .split(\" \")\n                    .forEach(\n                      function (methodName) {\n                        isfn( Array.prototype[methodName] )\n                        && (\n                            cloned[methodName] =\n                            function () {\n                                return Array.prototype[methodName].apply(cloned, arguments);\n                            }\n                        );\n                      }\n                    );\n                }\n                return isemptyobj(cloned)\n                       ? (\n                          isObject(input)\n                          ? cloned\n                          : input\n                        )\n                       : cloned;\n            } else {\n                return input;\n            }\n        };\n    }\n));\n//\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e32", + "creator": "BLaZuRE", + "createdAt": 1374958294000, + "text": "Why would this answer be better than any of the other ones?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329bf082fcc3049e92e34", + "creator": "BLaZuRE", + "createdAt": 1374960103000, + "text": "Did you get this from any framework or library, or did you create this? I don't know, it just doesn't seem as readable to me.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90652", + "creator": "Jeremy", + "createdAt": 1376174708000, + "text": "

Structured Cloning

\n

2022 update: The structuredClone() global function is already available in Node 17, Deno 1.14, and most major browsers (see Can I Use).

\n

You can use the same structured clone mechanism that the HTML standard includes for sending data between realms.

\n
const clone = structuredClone(original);\n
\n

See the other answer for more details.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e3e", + "creator": "Beni Cherniavsky-Paskin", + "createdAt": 1389680066000, + "text": "+1 for giving an idea in what form it might become builtin, eventually—even if unusable right now.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90650", + "creator": "Alex Che", + "createdAt": 1367862867000, + "text": "

My favorite & elegant JS objects clone solution is

\n\n
function CloneObject() {}\nfunction cloneObject(o) {\n   CloneObject.prototype = o;\n   return new CloneObject();\n}\n
\n\n

Use cloneObject(object) to get a clone of JS object.

\n\n

Unlike many copy solutions this clone keeps prototype relationship in cloned object.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e41", + "creator": "d13", + "createdAt": 1389888796000, + "text": "This is prototypal inheritance, not cloning.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329c0082fcc3049e92e42", + "creator": "RobG", + "createdAt": 1400587773000, + "text": "@d13—for many, this is cloning. Copying properties is copying.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 15, + "commentItems": [ + { + "_id": "62f321c1082fcc3049e9055a", + "creator": "chiccodoro", + "createdAt": 1317048977000, + "text": "Definitely support @Niyaz! Shortlink: tinyurl.com/JSCopyObject", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e9055b", + "creator": "Lord Loh.", + "createdAt": 1359799755000, + "text": "For JSON, I use mObj=JSON.parse(JSON.stringify(jsonObject));", + "upvotes": 527, + "upvoterUsernames": [], + "downvotes": 242, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e9055c", + "creator": "froginvasion", + "createdAt": 1407511382000, + "text": "I really don't get why no one suggests Object.create(o), it does everything the author asks?", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e9055d", + "creator": "WynandB", + "createdAt": 1410149277000, + "text": "@froginvasion Probably because it's not supported in IE8 and under.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e9055e", + "creator": "BTC", + "createdAt": 1435334146000, + "text": "Object.create works great if you want to copy your object's prototype.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e9055f", + "creator": "Matt", + "createdAt": 1442770262000, + "text": "@froginvasion - because it doesn't work even for flat objects? (Chrome 45). You just get __proto__, not a clone...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90560", + "creator": "r3wt", + "createdAt": 1454013723000, + "text": "how about var newObj = (function(){ return oldObj; }());", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90561", + "creator": "user3275211", + "createdAt": 1455648885000, + "text": "@r3wt that will not work... Please post only after doing basic test of the solution..", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90562", + "creator": "EscapeNetscape", + "createdAt": 1479139437000, + "text": "here a benchmark of different solutions: jsben.ch/#/bWfk9", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90563", + "creator": "Mahi", + "createdAt": 1479991353000, + "text": "@RubenStolk why OP cannot store object into new variable ? like var copyobject=object ??", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90564", + "creator": "soundly_typed", + "createdAt": 1480363001000, + "text": "@Mahi That does not copy the object. It creates a new variable and makes it point to the same object.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90565", + "creator": "OverCoder", + "createdAt": 1486580241000, + "text": "{...original}", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90566", + "creator": "Muhammad Ali", + "createdAt": 1548619260000, + "text": "Recently answered it over here", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90567", + "creator": "SwiftNinjaPro", + "createdAt": 1577854536000, + "text": "This should create a copy: let y = {...x};", + "upvotes": 1858, + "upvoterUsernames": [], + "downvotes": 1858, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90568", + "creator": "Geetanshu Gulati", + "createdAt": 1579873726000, + "text": "you can use clone method of lodash, or use spread operator let x = {} let y = {some value} x = {...x, ...y}", + "upvotes": 447, + "upvoterUsernames": [], + "downvotes": 447, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2210110, + "uvac": 2210182 + } + }, + { + "_id": "62f321bb082fcc3049e8fed8", + "title": "JavaScript closure inside loops – simple practical example", + "title-lowercase": "javascript closure inside loops – simple practical example", + "creator": "nickf", + "createdAt": 1239775580000, + "status": "open", + "text": "

\r\n
\r\n
var funcs = [];\n// let's create 3 functions\nfor (var i = 0; i < 3; i++) {\n  // and store them in funcs\n  funcs[i] = function() {\n    // each should log its value.\n    console.log(\"My value: \" + i);\n  };\n}\nfor (var j = 0; j < 3; j++) {\n  // and now let's run each one to see\n  funcs[j]();\n}
\r\n
\r\n
\r\n

\n

It outputs this:

\n
\n

My value: 3
\nMy value: 3
\nMy value: 3

\n
\n

Whereas I'd like it to output:

\n
\n

My value: 0
\nMy value: 1
\nMy value: 2

\n
\n
\n

The same problem occurs when the delay in running the function is caused by using event listeners:

\n

\r\n
\r\n
var buttons = document.getElementsByTagName(\"button\");\n// let's create 3 functions\nfor (var i = 0; i < buttons.length; i++) {\n  // as event listeners\n  buttons[i].addEventListener(\"click\", function() {\n    // each should log its value.\n    console.log(\"My value: \" + i);\n  });\n}
\r\n
<button>0</button>\n<br />\n<button>1</button>\n<br />\n<button>2</button>
\r\n
\r\n
\r\n

\n

… or asynchronous code, e.g. using Promises:

\n

\r\n
\r\n
// Some async wait function\nconst wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));\n\nfor (var i = 0; i < 3; i++) {\n  // Log `i` as soon as each promise resolves.\n  wait(i * 100).then(() => console.log(i));\n}
\r\n
\r\n
\r\n

\n

It is also apparent in for in and for of loops:

\n

\r\n
\r\n
const arr = [1,2,3];\nconst fns = [];\n\nfor(var i in arr){\n  fns.push(() => console.log(`index: ${i}`));\n}\n\nfor(var v of arr){\n  fns.push(() => console.log(`value: ${v}`));\n}\n\nfor(var f of fns){\n  f();\n}
\r\n
\r\n
\r\n

\n

What’s the solution to this basic problem?

\n", + "upvotes": 4256, + "upvoterUsernames": [], + "downvotes": 1124, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 455521, + "answers": 41, + "answerItems": [ + { + "_id": "62f321c4082fcc3049e907fc", + "creator": "Bjorn", + "createdAt": 1239775850000, + "text": "

Try:

\n\n

\r\n
\r\n
var funcs = [];\r\n    \r\nfor (var i = 0; i < 3; i++) {\r\n    funcs[i] = (function(index) {\r\n        return function() {\r\n            console.log(\"My value: \" + index);\r\n        };\r\n    }(i));\r\n}\r\n\r\nfor (var j = 0; j < 3; j++) {\r\n    funcs[j]();\r\n}
\r\n
\r\n
\r\n

\n\n

Edit (2014):

\n\n

Personally I think @Aust's more recent answer about using .bind is the best way to do this kind of thing now. There's also lo-dash/underscore's _.partial when you don't need or want to mess with bind's thisArg.

\n", + "upvotes": 546, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e8082fcc3049e91f1f", + "creator": "aswzen", + "createdAt": 1522978338000, + "text": "any explanation about the }(i)); ?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f325e8082fcc3049e91f21", + "creator": "Jet Blue", + "createdAt": 1532642510000, + "text": "@aswzen I think it passes i as the argument index to the function.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e8082fcc3049e91f23", + "creator": "Abhishek Singh", + "createdAt": 1552663028000, + "text": "it is actually creating local variable index.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907fd", + "creator": "jottos", + "createdAt": 1239776309000, + "text": "

The reason your original example did not work is that all the closures you created in the loop referenced the same frame. In effect, having 3 methods on one object with only a single i variable. They all printed out the same value.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ff", + "creator": "Darren Clark", + "createdAt": 1239778123000, + "text": "

Another way of saying it is that the i in your function is bound at the time of executing the function, not the time of creating the function.

\n\n

When you create the closure, i is a reference to the variable defined in the outside scope, not a copy of it as it was when you created the closure. It will be evaluated at the time of execution.

\n\n

Most of the other answers provide ways to work around by creating another variable that won't change the value for you.

\n\n

Just thought I'd add an explanation for clarity. For a solution, personally, I'd go with Harto's since it is the most self-explanatory way of doing it from the answers here. Any of the code posted will work, but I'd opt for a closure factory over having to write a pile of comments to explain why I'm declaring a new variable(Freddy and 1800's) or have weird embedded closure syntax(apphacker).

\n", + "upvotes": 141, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90800", + "creator": "Boann", + "createdAt": 1344329135000, + "text": "

Here's another variation on the technique, similar to Bjorn's (apphacker), which lets you assign the variable value inside the function rather than passing it as a parameter, which might be clearer sometimes:

\n\n

\r\n
\r\n
var funcs = [];\r\nfor (var i = 0; i < 3; i++) {\r\n    funcs[i] = (function() {\r\n        var index = i;\r\n        return function() {\r\n            console.log(\"My value: \" + index);\r\n        }\r\n    })();\r\n}
\r\n
\r\n
\r\n

\n\n

Note that whatever technique you use, the index variable becomes a sort of static variable, bound to the returned copy of the inner function. I.e., changes to its value are preserved between calls. It can be very handy.

\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e8082fcc3049e91f27", + "creator": "Boann", + "createdAt": 1386045328000, + "text": "@midnite If you swapped var and return then the variable wouldn't be assigned before it returned the inner function.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90801", + "creator": "user1724763", + "createdAt": 1366451997000, + "text": "

This describes the common mistake with using closures in JavaScript.

\n\n

A function defines a new environment

\n\n

Consider:

\n\n
function makeCounter()\n{\n  var obj = {counter: 0};\n  return {\n    inc: function(){obj.counter ++;},\n    get: function(){return obj.counter;}\n  };\n}\n\ncounter1 = makeCounter();\ncounter2 = makeCounter();\n\ncounter1.inc();\n\nalert(counter1.get()); // returns 1\nalert(counter2.get()); // returns 0\n
\n\n

For each time makeCounter is invoked, {counter: 0} results in a new object being created. Also, a new copy of obj \nis created as well to reference the new object. Thus, counter1 and counter2 are independent of each other.

\n\n

Closures in loops

\n\n

Using a closure in a loop is tricky.

\n\n

Consider:

\n\n
var counters = [];\n\nfunction makeCounters(num)\n{\n  for (var i = 0; i < num; i++)\n  {\n    var obj = {counter: 0};\n    counters[i] = {\n      inc: function(){obj.counter++;},\n      get: function(){return obj.counter;}\n    }; \n  }\n}\n\nmakeCounters(2);\n\ncounters[0].inc();\n\nalert(counters[0].get()); // returns 1\nalert(counters[1].get()); // returns 1\n
\n\n

Notice that counters[0] and counters[1] are not independent. In fact, they operate on the same obj!

\n\n

This is because there is only one copy of obj shared across all iterations of the loop, perhaps for performance reasons.\nEven though {counter: 0} creates a new object in each iteration, the same copy of obj will just get updated with a\nreference to the newest object.

\n\n

Solution is to use another helper function:

\n\n
function makeHelper(obj)\n{\n  return {\n    inc: function(){obj.counter++;},\n    get: function(){return obj.counter;}\n  }; \n}\n\nfunction makeCounters(num)\n{\n  for (var i = 0; i < num; i++)\n  {\n    var obj = {counter: 0};\n    counters[i] = makeHelper(obj);\n  }\n}\n
\n\n

This works because local variables in the function scope directly, as well as function argument variables, are allocated \nnew copies upon entry.

\n", + "upvotes": 96, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907fe", + "creator": "eglasius", + "createdAt": 1239776708000, + "text": "

What you need to understand is the scope of the variables in javascript is based on the function. This is an important difference than say c# where you have block scope, and just copying the variable to one inside the for will work.

\n\n

Wrapping it in a function that evaluates returning the function like apphacker's answer will do the trick, as the variable now has the function scope.

\n\n

There is also a let keyword instead of var, that would allow using the block scope rule. In that case defining a variable inside the for would do the trick. That said, the let keyword isn't a practical solution because of compatibility.

\n\n

\r\n
\r\n
var funcs = {};\r\n\r\nfor (var i = 0; i < 3; i++) {\r\n  let index = i; //add this\r\n  funcs[i] = function() {\r\n    console.log(\"My value: \" + index); //change to the copy\r\n  };\r\n}\r\n\r\nfor (var j = 0; j < 3; j++) {\r\n  funcs[j]();\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90802", + "creator": "Ben McCormick", + "createdAt": 1369105488000, + "text": "

With ES6 now widely supported, the best answer to this question has changed. ES6 provides the let and const keywords for this exact circumstance. Instead of messing around with closures, we can just use let to set a loop scope variable like this:

\n\n

\r\n
\r\n
var funcs = [];\r\n\r\nfor (let i = 0; i < 3; i++) {          \r\n    funcs[i] = function() {            \r\n      console.log(\"My value: \" + i); \r\n    };\r\n}
\r\n
\r\n
\r\n

\n\n

val will then point to an object that is specific to that particular turn of the loop, and will return the correct value without the additional closure notation. This obviously significantly simplifies this problem.

\n\n

const is similar to let with the additional restriction that the variable name can't be rebound to a new reference after initial assignment.

\n\n

Browser support is now here for those targeting the latest versions of browsers. const/let are currently supported in the latest Firefox, Safari, Edge and Chrome. It also is supported in Node, and you can use it anywhere by taking advantage of build tools like Babel. You can see a working example here: http://jsfiddle.net/ben336/rbU4t/2/

\n\n

Docs here:

\n\n\n\n

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.

\n", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e8082fcc3049e91f2b", + "creator": "pixel 67", + "createdAt": 1521474960000, + "text": "Isn't this why we use babel to transpile our code so browsers that don't support ES6/7 can understand what's going on?", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90803", + "creator": "Kemal Dağ", + "createdAt": 1372170113000, + "text": "

The most simple solution would be,

\n\n

Instead of using:

\n\n
var funcs = [];\nfor(var i =0; i<3; i++){\n    funcs[i] = function(){\n        alert(i);\n    }\n}\n\nfor(var j =0; j<3; j++){\n    funcs[j]();\n}\n
\n\n

which alerts \"2\", for 3 times. This is because anonymous functions created in for loop, shares same closure, and in that closure, the value of i is the same. Use this to prevent shared closure:

\n\n
var funcs = [];\nfor(var new_i =0; new_i<3; new_i++){\n    (function(i){\n        funcs[i] = function(){\n            alert(i);\n        }\n    })(new_i);\n}\n\nfor(var j =0; j<3; j++){\n    funcs[j]();\n}\n
\n\n

The idea behind this is, encapsulating the entire body of the for loop with an IIFE (Immediately-Invoked Function Expression) and passing new_i as a parameter and capturing it as i. Since the anonymous function is executed immediately, the i value is different for each function defined inside the anonymous function.

\n\n

This solution seems to fit any such problem since it will require minimal changes to the original code suffering from this issue. In fact, this is by design, it should not be an issue at all!

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e8082fcc3049e91f2e", + "creator": "Kemal Dağ", + "createdAt": 1374841244000, + "text": "@DanMan Thanks. Self calling anonymous functions are very good way to deal javascript's lack of block level variable scope.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90804", + "creator": "yilmazburk", + "createdAt": 1379600424000, + "text": "

try this shorter one

\n\n\n\n


\n\n
for (var i = 0; i < 3; i++) {\n    createfunc(i)();\n}\n\nfunction createfunc(i) {\n    return function(){console.log(\"My value: \" + i);};\n}\n
\n\n

http://jsfiddle.net/7P6EN/

\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90808", + "creator": "Daryl", + "createdAt": 1399088577000, + "text": "

Here's a simple solution that uses forEach (works back to IE9):

\n\n

\r\n
\r\n
var funcs = [];\r\n[0,1,2].forEach(function(i) {          // let's create 3 functions\r\n    funcs[i] = function() {            // and store them in funcs\r\n        console.log(\"My value: \" + i); // each should log its value.\r\n    };\r\n})\r\nfor (var j = 0; j < 3; j++) {\r\n    funcs[j]();                        // and now let's run each one to see\r\n}
\r\n
\r\n
\r\n

\n\n

Prints:

\n\n
\n
My value: 0\nMy value: 1\nMy value: 2\n
\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90805", + "creator": "Aust", + "createdAt": 1381509716000, + "text": "

Another way that hasn't been mentioned yet is the use of Function.prototype.bind

\n\n

\r\n
\r\n
var funcs = {};\r\nfor (var i = 0; i < 3; i++) {\r\n  funcs[i] = function(x) {\r\n    console.log('My value: ' + x);\r\n  }.bind(this, i);\r\n}\r\nfor (var j = 0; j < 3; j++) {\r\n  funcs[j]();\r\n}
\r\n
\r\n
\r\n

\n\n

UPDATE

\n\n

As pointed out by @squint and @mekdev, you get better performance by creating the function outside the loop first and then binding the results within the loop.

\n\n

\r\n
\r\n
function log(x) {\r\n  console.log('My value: ' + x);\r\n}\r\n\r\nvar funcs = [];\r\n\r\nfor (var i = 0; i < 3; i++) {\r\n  funcs[i] = log.bind(this, i);\r\n}\r\n\r\nfor (var j = 0; j < 3; j++) {\r\n  funcs[j]();\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 432, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e9082fcc3049e91f32", + "creator": "Bjorn", + "createdAt": 1418015911000, + "text": "This is what I do these days too, I also like lo-dash/underscore's _.partial", + "upvotes": 2872, + "upvoterUsernames": [], + "downvotes": 2872, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f33", + "creator": "user2290820", + "createdAt": 1441973660000, + "text": "I think instead of wasting computation over two O(n) loops, just do for (var i = 0; i < 3; i++) { log.call(this, i); }", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f35", + "creator": "niry", + "createdAt": 1483854907000, + "text": ".bind() does what the accepted answer suggests PLUS fiddles with this.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90806", + "creator": "neurosnap", + "createdAt": 1381515823000, + "text": "

Using an Immediately-Invoked Function Expression, the simplest and most readable way to enclose an index variable:

\n\n

\r\n
\r\n
for (var i = 0; i < 3; i++) {\r\n\r\n    (function(index) {\r\n\r\n        console.log('iterator: ' + index);\r\n        //now you can also loop an ajax call here \r\n        //without losing track of the iterator value:   $.ajax({});\r\n    \r\n    })(i);\r\n\r\n}
\r\n
\r\n
\r\n

\n\n

This sends the iterator i into the anonymous function of which we define as index. This creates a closure, where the variable i gets saved for later use in any asynchronous functionality within the IIFE.

\n", + "upvotes": 390, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e9082fcc3049e91f38", + "creator": "Kyle Falconer", + "createdAt": 1389372353000, + "text": "For further code readability and to avoid confusion as to which i is what, I'd rename the function parameter to index.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f3a", + "creator": "Nico", + "createdAt": 1417353439000, + "text": "How would you use this technique to define the array funcs described in the original question?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f3c", + "creator": "JLRishe", + "createdAt": 1427835275000, + "text": "@Nico The same way as shown in the original question, except you would use index instead of i.", + "upvotes": 1100, + "upvoterUsernames": [], + "downvotes": 1100, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f3e", + "creator": "Timar Ivo Batis", + "createdAt": 1544716273000, + "text": "i was looking for the syntax of immediately invoked function and couldn't figure it out. +1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90807", + "creator": "Travis J", + "createdAt": 1394060604000, + "text": "

The main issue with the code shown by the OP is that i is never read until the second loop. To demonstrate, imagine seeing an error inside of the code

\n\n
funcs[i] = function() {            // and store them in funcs\n    throw new Error(\"test\");\n    console.log(\"My value: \" + i); // each should log its value.\n};\n
\n\n

The error actually does not occur until funcs[someIndex] is executed (). Using this same logic, it should be apparent that the value of i is also not collected until this point either. Once the original loop finishes, i++ brings i to the value of 3 which results in the condition i < 3 failing and the loop ending. At this point, i is 3 and so when funcs[someIndex]() is used, and i is evaluated, it is 3 - every time.

\n\n

To get past this, you must evaluate i as it is encountered. Note that this has already happened in the form of funcs[i] (where there are 3 unique indexes). There are several ways to capture this value. One is to pass it in as a parameter to a function which is shown in several ways already here.

\n\n

Another option is to construct a function object which will be able to close over the variable. That can be accomplished thusly

\n\n

jsFiddle Demo

\n\n
funcs[i] = new function() {   \n    var closedVariable = i;\n    return function(){\n        console.log(\"My value: \" + closedVariable); \n    };\n};\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9080a", + "creator": "Christian Landgren", + "createdAt": 1418163857000, + "text": "

I'm surprised no one yet has suggested using the forEach function to better avoid (re)using local variables. In fact, I'm not using for(var i ...) at all anymore for this reason.

\n\n
[0,2,3].forEach(function(i){ console.log('My value:', i); });\n// My value: 0\n// My value: 2\n// My value: 3\n
\n\n

// edited to use forEach instead of map.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e9082fcc3049e91f41", + "creator": "jherax", + "createdAt": 1445919263000, + "text": "This question is not about loop over an array", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f43", + "creator": "Christian Landgren", + "createdAt": 1447277103000, + "text": "Well, he wants to create an array of functions, this example shows how to do that without involving a global variable.", + "upvotes": 337, + "upvoterUsernames": [], + "downvotes": 337, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90809", + "creator": "wpding", + "createdAt": 1405348920000, + "text": "

After reading through various solutions, I'd like to add that the reason those solutions work is to rely on the concept of scope chain. It's the way JavaScript resolve a variable during execution.

\n\n\n\n

In the initial code:

\n\n
funcs = {};\nfor (var i = 0; i < 3; i++) {         \n  funcs[i] = function inner() {        // function inner's scope contains nothing\n    console.log(\"My value: \" + i);    \n  };\n}\nconsole.log(window.i)                  // test value 'i', print 3\n
\n\n

When funcs gets executed, the scope chain will be function inner -> global. Since the variable i cannot be found in function inner (neither declared using var nor passed as arguments), it continues to search, until the value of i is eventually found in the global scope which is window.i.

\n\n

By wrapping it in an outer function either explicitly define a helper function like harto did or use an anonymous function like Bjorn did:

\n\n
funcs = {};\nfunction outer(i) {              // function outer's scope contains 'i'\n  return function inner() {      // function inner, closure created\n   console.log(\"My value: \" + i);\n  };\n}\nfor (var i = 0; i < 3; i++) {\n  funcs[i] = outer(i);\n}\nconsole.log(window.i)          // print 3 still\n
\n\n

When funcs gets executed, now the scope chain will be function inner -> function outer. This time i can be found in the outer function's scope which is executed 3 times in the for loop, each time has value i bound correctly. It won't use the value of window.i when inner executed.

\n\n

More detail can be found here
\nIt includes the common mistake in creating closure in the loop as what we have here, as well as why we need closure and the performance consideration.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9080b", + "creator": "axelduch", + "createdAt": 1430951588000, + "text": "

This is a problem often encountered with asynchronous code, the variable i is mutable and at the time at which the function call is made the code using i will be executed and i will have mutated to its last value, thus meaning all functions created within the loop will create a closure and i will be equal to 3 (the upper bound + 1 of the for loop.

\n\n

A workaround to this, is to create a function that will hold the value of i for each iteration and force a copy i (as it is a primitive, think of it as a snapshot if it helps you).

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9080c", + "creator": "Rune FS", + "createdAt": 1434542562000, + "text": "

You could use a declarative module for lists of data such as query-js(*). In these situations I personally find a declarative approach less surprising

\n\n
var funcs = Query.range(0,3).each(function(i){\n     return  function() {\n        console.log(\"My value: \" + i);\n    };\n});\n
\n\n

You could then use your second loop and get the expected result or you could do

\n\n
funcs.iterate(function(f){ f(); });\n
\n\n

(*) I'm the author of query-js and therefor biased towards using it, so don't take my words as a recommendation for said library only for the declarative approach :)

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e9082fcc3049e91f47", + "creator": "Rune FS", + "createdAt": 1434651678000, + "text": "I would love an explanation of the down vote. The code solves the problem at hand. It would be valuable to know how to potentially improve the code", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9080e", + "creator": "pixel 67", + "createdAt": 1462448931000, + "text": "

And yet another solution: instead of creating another loop, just bind the this to the return function.

\n\n

\r\n
\r\n
var funcs = [];\r\n\r\nfunction createFunc(i) {\r\n  return function() {\r\n    console.log('My value: ' + i); //log value of i.\r\n  }.call(this);\r\n}\r\n\r\nfor (var i = 1; i <= 5; i++) {  //5 functions\r\n  funcs[i] = createFunc(i);     // call createFunc() i=5 times\r\n}
\r\n
\r\n
\r\n

\n\n

By binding this, solves the problem as well.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9080d", + "creator": "Rax Wunter", + "createdAt": 1450361696000, + "text": "

I prefer to use forEach function, which has its own closure with creating a pseudo range:

\n\n
var funcs = [];\n\nnew Array(3).fill(0).forEach(function (_, i) { // creating a range\n    funcs[i] = function() {            \n        // now i is safely incapsulated \n        console.log(\"My value: \" + i);\n    };\n});\n\nfor (var j = 0; j < 3; j++) {\n    funcs[j](); // 0, 1, 2\n}\n
\n\n

That looks uglier than ranges in other languages, but IMHO less monstrous than other solutions.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e9082fcc3049e91f49", + "creator": "Rax Wunter", + "createdAt": 1450362660000, + "text": "It's related exactly to the mentioned issue: how to iterate safely without closure problems", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f4b", + "creator": "Quentin", + "createdAt": 1450362670000, + "text": "Now it doesn't seem significantly different from the accepted answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f325e9082fcc3049e91f4c", + "creator": "Rax Wunter", + "createdAt": 1450363500000, + "text": "@Quentin I would recommend to investigate solution before minusing", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9080f", + "creator": "Ali Kahoot", + "createdAt": 1478245567000, + "text": "

First of all, understand what's wrong with this code:

\n\n
var funcs = [];\nfor (var i = 0; i < 3; i++) {          // let's create 3 functions\n    funcs[i] = function() {            // and store them in funcs\n        console.log(\"My value: \" + i); // each should log its value.\n    };\n}\nfor (var j = 0; j < 3; j++) {\n    funcs[j]();                        // and now let's run each one to see\n}\n
\n\n

Here when the funcs[] array is being initialized, i is being incremented, the funcs array is initialized and the size of func array becomes 3, so i = 3,. \nNow when the funcs[j]() is called, it is again using the variable i, which has already been incremented to 3.

\n\n

Now to solve this, we have many options. Below are two of them:

\n\n
    \n
  1. We can initialize i with let or initialize a new variable index with let and make it equal to i. So when the call is being made, index will be used and its scope will end after initialization. And for calling, index will be initialized again:

    \n\n
    var funcs = [];\nfor (var i = 0; i < 3; i++) {          \n    let index = i;\n    funcs[i] = function() {            \n        console.log(\"My value: \" + index); \n    };\n}\nfor (var j = 0; j < 3; j++) {\n    funcs[j]();                        \n}\n
  2. \n
  3. Other Option can be to introduce a tempFunc which returns the actual function:

    \n\n
    var funcs = [];\nfunction tempFunc(i){\n    return function(){\n        console.log(\"My value: \" + i);\n    };\n}\nfor (var i = 0; i < 3; i++) {  \n    funcs[i] = tempFunc(i);                                     \n}\nfor (var j = 0; j < 3; j++) {\n    funcs[j]();                        \n}\n
  4. \n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90810", + "creator": "Buksy", + "createdAt": 1478249909000, + "text": "

Your code doesn't work, because what it does is:

\n\n
Create variable `funcs` and assign it an empty array;  \nLoop from 0 up until it is less than 3 and assign it to variable `i`;\n    Push to variable `funcs` next function:  \n        // Only push (save), but don't execute\n        **Write to console current value of variable `i`;**\n\n// First loop has ended, i = 3;\n\nLoop from 0 up until it is less than 3 and assign it to variable `j`;\n    Call `j`-th function from variable `funcs`:  \n        **Write to console current value of variable `i`;**  \n        // Ask yourself NOW! What is the value of i?\n
\n\n

Now the question is, what is the value of variable i when the function is called? Because the first loop is created with the condition of i < 3, it stops immediately when the condition is false, so it is i = 3.

\n\n

You need to understand that, in time when your functions are created, none of their code is executed, it is only saved for later. And so when they are called later, the interpreter executes them and asks: \"What is the current value of i?\"

\n\n

So, your goal is to first save the value of i to function and only after that save the function to funcs. This could be done for example this way:

\n\n
var funcs = [];\nfor (var i = 0; i < 3; i++) {          // let's create 3 functions\n    funcs[i] = function(x) {            // and store them in funcs\n        console.log(\"My value: \" + x); // each should log its value.\n    }.bind(null, i);\n}\nfor (var j = 0; j < 3; j++) {\n    funcs[j]();                        // and now let's run each one to see\n}\n
\n\n

This way, each function will have it's own variable x and we set this x to the value of i in each iteration.

\n\n

This is only one of the multiple ways to solve this problem.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90811", + "creator": "Prithvi Uppalapati", + "createdAt": 1478517954000, + "text": "

With new features of ES6 block level scoping is managed:

\n\n
var funcs = [];\nfor (let i = 0; i < 3; i++) {          // let's create 3 functions\n    funcs[i] = function() {            // and store them in funcs\n        console.log(\"My value: \" + i); // each should log its value.\n    };\n}\nfor (let j = 0; j < 3; j++) {\n    funcs[j]();                        // and now let's run each one to see\n}\n
\n\n

The code in OP's question is replaced with let instead of var.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90812", + "creator": "Alexander Levakov", + "createdAt": 1478857397000, + "text": "

Let's take advantage of new Function. Thus i stops to be a variable of a closure and becomes just a part of the text:

\n\n
var funcs = [];\nfor (var i = 0; i < 3; i++) {\n    var functionBody = 'console.log(\"My value: ' + i + '\");';\n    funcs[i] = new Function(functionBody);\n}\n\nfor (var j = 0; j < 3; j++) {\n    funcs[j]();\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325ea082fcc3049e91f50", + "creator": "lovasoa", + "createdAt": 1478946730000, + "text": "This is slow, potentially insecure, and doesn't work everywhere.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90813", + "creator": "Costa Michailidis", + "createdAt": 1481132007000, + "text": "

JavaScript functions \"close over\" the scope they have access to upon declaration, and retain access to that scope even as variables in that scope change.

\n\n

\r\n
\r\n
var funcs = []\r\n\r\nfor (var i = 0; i < 3; i += 1) {\r\n  funcs[i] = function () {\r\n    console.log(i)\r\n  }\r\n}\r\n\r\nfor (var k = 0; k < 3; k += 1) {\r\n  funcs[k]()\r\n}
\r\n
\r\n
\r\n

\n\n

Each function in the array above closes over the global scope (global, simply because that happens to be the scope they're declared in).

\n\n

Later those functions are invoked logging the most current value of i in the global scope. That's the magic, and frustration, of closure.

\n\n

\"JavaScript Functions close over the scope they are declared in, and retain access to that scope even as variable values inside of that scope change.\"

\n\n

Using let instead of var solves this by creating a new scope each time the for loop runs, creating a separated scope for each function to close over. Various other techniques do the same thing with extra functions.

\n\n

\r\n
\r\n
var funcs = []\r\n\r\nfor (let i = 0; i < 3; i += 1) {\r\n  funcs[i] = function () {\r\n    console.log(i)\r\n  }\r\n}\r\n\r\nfor (var k = 0; k < 3; k += 1) {\r\n  funcs[k]()\r\n}
\r\n
\r\n
\r\n

\n\n

(let makes variables block scoped. Blocks are denoted by curly braces, but in the case of the for loop the initialization variable, i in our case, is considered to be declared in the braces.)

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90814", + "creator": "Vikash_Singh", + "createdAt": 1484906552000, + "text": "

Use closure structure, this would reduce your extra for loop. You can do it in a single for loop:

\n\n
var funcs = [];\nfor (var i = 0; i < 3; i++) {     \n  (funcs[i] = function() {         \n    console.log(\"My value: \" + i); \n  })(i);\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90816", + "creator": "jsbisht", + "createdAt": 1500286255000, + "text": "

COUNTER BEING A PRIMITIVE

\n\n

Let's define callback functions as follows:

\n\n
// ****************************\n// COUNTER BEING A PRIMITIVE\n// ****************************\nfunction test1() {\n    for (var i=0; i<2; i++) {\n        setTimeout(function() {\n            console.log(i);\n        });\n    }\n}\ntest1();\n// 2\n// 2\n
\n\n

After timeout completes it will print 2 for both. This is because the callback function accesses the value based on the lexical scope, where it was function was defined.

\n\n

To pass and preserve the value while callback was defined, we can create a closure, to preserve the value before the callback is invoked. This can be done as follows:

\n\n
function test2() {\n    function sendRequest(i) {\n        setTimeout(function() {\n            console.log(i);\n        });\n    }\n\n    for (var i = 0; i < 2; i++) {\n        sendRequest(i);\n    }\n}\ntest2();\n// 1\n// 2\n
\n\n

Now what's special about this is \"The primitives are passed by value and copied. Thus when the closure is defined, they keep the value from the previous loop.\"

\n\n

COUNTER BEING AN OBJECT

\n\n

Since closures have access to parent function variables via reference, this approach would differ from that for primitives.

\n\n
// ****************************\n// COUNTER BEING AN OBJECT\n// ****************************\nfunction test3() {\n    var index = { i: 0 };\n    for (index.i=0; index.i<2; index.i++) {\n        setTimeout(function() {\n            console.log('test3: ' + index.i);\n        });\n    }\n}\ntest3();\n// 2\n// 2\n
\n\n

So, even if a closure is created for the variable being passed as an object, the value of the loop index will not be preserved. This is to show that the values of an object are not copied whereas they are accessed via reference.

\n\n
function test4() {\n    var index = { i: 0 };\n    function sendRequest(index, i) {\n        setTimeout(function() {\n            console.log('index: ' + index);\n            console.log('i: ' + i);\n            console.log(index[i]);\n        });\n    }\n\n    for (index.i=0; index.i<2; index.i++) {\n        sendRequest(index, index.i);\n    }\n}\ntest4();\n// index: { i: 2}\n// 0\n// undefined\n\n// index: { i: 2}\n// 1\n// undefined\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90815", + "creator": "Pawel", + "createdAt": 1488294544000, + "text": "

Many solutions seem correct but they don't mention it's called Currying which is a functional programming design pattern for situations like here. 3-10 times faster than bind depending on the browser.

\n\n
var funcs = [];\nfor (var i = 0; i < 3; i++) {      // let's create 3 functions\n  funcs[i] = curryShowValue(i);\n}\nfor (var j = 0; j < 3; j++) {\n  funcs[j]();                      // and now let's run each one to see\n}\n\nfunction curryShowValue(i) {\n  return function showValue() {\n    console.log(\"My value: \" + i);\n  }\n}\n
\n\n

See the performance gain in different browsers.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90817", + "creator": "neatsu", + "createdAt": 1507884798000, + "text": "

Already many valid answers to this question. Not many using a functional approach though. Here is an alternative solution using the forEach method, which works well with callbacks and closures:

\n\n

let arr = [1,2,3];

\n\n

let myFunc = (val, index) => { \n console.log('val: '+val+'\\nindex: '+index);\n};

\n\n

arr.forEach(myFunc);

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90818", + "creator": "sidhuko", + "createdAt": 1515849477000, + "text": "

This question really shows the history of JavaScript! Now we can avoid block scoping with arrow functions and handle loops directly from DOM nodes using Object methods.

\n\n

\r\n
\r\n
const funcs = [1, 2, 3].map(i => () => console.log(i));\r\nfuncs.map(fn => fn())
\r\n
\r\n
\r\n

\n\n

\r\n
\r\n
const buttons = document.getElementsByTagName(\"button\");\r\nObject\r\n  .keys(buttons)\r\n  .map(i => buttons[i].addEventListener('click', () => console.log(i)));
\r\n
<button>0</button><br>\r\n<button>1</button><br>\r\n<button>2</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9081a", + "creator": "Eyal Segal", + "createdAt": 1521008354000, + "text": "

Let's say you don't use ES6;\nYou can use IIFEs:

\n
var funcs = [];\nfor (var i = 0; i < 13; i++) {      \n    funcs[i] = (function(x) {\n      console.log("My value: " + i)\n     })(i);\n   }\n
\n

But it will be different.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90819", + "creator": "Brooks DuBois", + "createdAt": 1519528521000, + "text": "

While this question is old and answered, I have yet another fairly interesting solution:

\n\n
var funcs = [];\n\nfor (var i = 0; i < 3; i++) {     \n  funcs[i] = function() {          \n    console.log(\"My value: \" + i); \n };\n}\n\nfor (var i = 0; i < 3; i++) {\n  funcs[i]();\n}\n
\n\n

The change is so small it's almost difficult to see what I did. I switched the second iterator from a j to an i. This somehow refreshes the state of i in time to give you the desired result. I did this by accident but it makes sense considering previous answers.

\n\n

I wrote this up to point out this small, yet very important difference. Hope that helps to clear up some confusion for other learners like me.

\n\n

Note: I am not sharing this because I think it's the right answer. This is a flakey solution that probably will break under certain circumstances. Actually, I'm quite amazed that it really works.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325eb082fcc3049e91f57", + "creator": "Brooks DuBois", + "createdAt": 1521161491000, + "text": "right, which makes sense considering the other answers about scoping, but still is sort of counterintuitive", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9081c", + "creator": "stemon", + "createdAt": 1527207513000, + "text": "

Why not simply call each function inside the first (and only) loop just after they were created, such as:

\n\n
 var funcs = [];\n    for (var i = 0; i < 3; i++) {\n    // let's create 3 functions\n    funcs[i] = function() {\n    // and store them in funcs\n    console.log(\"My value: \" + i); // each should log its value.\n    };\n    funcs[i]();// and now let's run each one to see\n    }\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325eb082fcc3049e91f59", + "creator": "nickf", + "createdAt": 1527758568000, + "text": "Because it was just an example of the problem.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e9081b", + "creator": "Mark Manning", + "createdAt": 1522268111000, + "text": "

Ok. I read through all of the answers. Even though there is a good explanation here - I just could not get this to work. So I went looking on the internet. The person at https://dzone.com/articles/why-does-javascript-loop-only-use-last-value had an answer which is not presented here. So I thought I'd post a short example. This made a lot more sense to me.

\n\n

The long and short of it is that the LET command is nice but is only now being used. HOWEVER, the LET command is really just a TRY-CATCH combo. This works all the way back to IE3 (I believe). Using the TRY-CATCH combo - life is simple and good. Probably why the EMCScript people decided to use it. It also does not need a setTimeout() function. So no time is lost. Basically, you need one TRY-CATCH combo per FOR loop. Here is an example:

\n\n
    for( var i in myArray ){\n       try{ throw i }\n       catch(ii){\n// Do whatever it is you want to do with ii\n          }\n       }\n
\n\n

If you have more than one FOR loop, you just put a TRY-CATCH combo for each one. Also, personally, I always use the double letter of whatever FOR variable I am using. So \"ii\" for \"i\" and so on. I am using this technique in a routine to send mouseover commands to a different routine.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9081e", + "creator": "user1559625", + "createdAt": 1533115925000, + "text": "

This proves how ugly javascript is with regard to how 'closure' and 'non-closure' works.

\n\n

In the case of:

\n\n
var funcs = [];\n\nfor (var i = 0; i < 3; i++) {      // let's create 3 functions\n  funcs[i] = function() {          // and store them in funcs\n    console.log(\"My value: \" + i); // each should log its value.\n  };\n}\n
\n\n

funcs[i] is a global function, and 'console.log(\"My value: \" + i);' is printing global variable i

\n\n

In the case of

\n\n
var funcs = [];\n\nfunction createfunc(i) {\n    return function() { console.log(\"My value: \" + i); };\n}\n\nfor (var i = 0; i < 3; i++) {\n    funcs[i] = createfunc(i);\n}\n
\n\n

because of this twisted closure design of javascript, 'console.log(\"My value: \" + i);' is printing the i from outer function 'createfunc(i)'

\n\n

all because javascript can not design something decent like the 'static' variable inside a function like what C programming language is doing!

\n", + "upvotes": 2558, + "upvoterUsernames": [], + "downvotes": 2558, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90824", + "creator": "CertainPerformance", + "createdAt": 1569101684000, + "text": "

If you're having this sort of problem with a while loop, rather than a for loop, for example:

\n

\r\n
\r\n
var i = 0;\nwhile (i < 5) {\n  setTimeout(function() {\n    console.log(i);\n  }, i * 1000);\n  i++;\n}
\r\n
\r\n
\r\n

\n

The technique to close over the current value is a bit different. Declare a block-scoped variable with const inside the while block, and assign the current i to it. Then, wherever the variable is being used asynchronously, replace i with the new block-scoped variable:

\n

\r\n
\r\n
var i = 0;\nwhile (i < 5) {\n  const thisIterationI = i;\n  setTimeout(function() {\n    console.log(thisIterationI);\n  }, i * 1000);\n  i++;\n}
\r\n
\r\n
\r\n

\n

For older browsers that don't support block-scoped variables, you can use an IIFE called with i:

\n

\r\n
\r\n
var i = 0;\nwhile (i < 5) {\n  (function(innerI) {\n    setTimeout(function() {\n      console.log(innerI);\n    }, innerI * 1000);\n  })(i);\n  i++;\n}
\r\n
\r\n
\r\n

\n

If the asynchronous action to be invoked happens to be setTimeout like the above, you can also call setTimeout with a third parameter to indicate the argument to call the passed function with:

\n

\r\n
\r\n
var i = 0;\nwhile (i < 5) {\n  setTimeout(\n    (thisIterationI) => { // Callback\n      console.log(thisIterationI);\n    },\n    i * 1000, // Delay\n    i // Gets passed to the callback; becomes thisIterationI\n  );\n  i++;\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32628082fcc3049e91f5d", + "creator": "Noman_1", + "createdAt": 1613124724000, + "text": "The IIFE is what I was looking for", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90823", + "creator": "Shivang Gupta", + "createdAt": 1556798267000, + "text": "

Till ES5, This problem can only be solved using closure.

\n\n

But now in ES6, we have block level scope variables. Changing var to let in first for loop will solve the problem.

\n\n

\r\n
\r\n
var funcs = [];\r\nfor (let i = 0; i < 3; i++) {      // let's create 3 functions\r\n  funcs[i] = function() {          // and store them in funcs\r\n    console.log(\"My value: \" + i); // each should log its value.\r\n  };\r\n}\r\nfor (var j = 0; j < 3; j++) {\r\n  funcs[j]();                      // and now let's run each one to see\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9081d", + "creator": "Ashish Yadav", + "createdAt": 1531468929000, + "text": "
var funcs = [];\nfor (var i = 0; i < 3; i++) {      // let's create 3 functions\n  funcs[i] = function(param) {          // and store them in funcs\n    console.log(\"My value: \" + param); // each should log its value.\n  };\n}\nfor (var j = 0; j < 3; j++) {\n  funcs[j](j);                      // and now let's run each one to see with j\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e9081f", + "creator": "Nouman Dilshad", + "createdAt": 1536152437000, + "text": "

Just change the var keyword to let.

\n\n

var is function scoped.

\n\n

let is block scoped.

\n\n

When you start you code the for loop will iterate and assign the value of i to 3, which will remain 3 throughout your code. I suggest you to read more about scopes in node (let,var,const and others)

\n\n
funcs = [];\nfor (let i = 0; i < 3; i++) {      // let's create 3 functions\n  funcs[i] =async function() {          // and store them in funcs\n    await console.log(\"My value: \" + i); // each should log its value.\n  };\n}\nfor (var j = 0; j < 3; j++) {\n  funcs[j]();                      // and now let's run each one to see\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90820", + "creator": "swogger", + "createdAt": 1554655519000, + "text": "
  asyncIterable = [1,2,3,4,5,6,7,8];\n\n  (async function() {\n       for await (let num of asyncIterable) {\n         console.log(num);\n       }\n    })();\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c1082fcc3049e92e55", + "creator": "swogger", + "createdAt": 1554764483000, + "text": "Thanks @FedericoGrandi, after two pages of examples I thought that will not be required.", + "upvotes": 1347, + "upvoterUsernames": [], + "downvotes": 1347, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e90821", + "creator": "B G Hari Prasad", + "createdAt": 1555324659000, + "text": "

Use let(blocked-scope) instead of var.

\n\n

\r\n
\r\n
var funcs = [];\r\nfor (let i = 0; i < 3; i++) {      \r\n  funcs[i] = function() {          \r\n    console.log(\"My value: \" + i); \r\n  };\r\n}\r\nfor (var j = 0; j < 3; j++) {\r\n  funcs[j]();                      \r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e90822", + "creator": "Murtaza Hussain", + "createdAt": 1555488622000, + "text": "

With the support of ES6, the best way to this is to use let and const keywords for this exact circumstance. So var variable get hoisted and with the end of loop the value of i gets updated for all the closures..., we can just use let to set a loop scope variable like this:

\n\n
var funcs = [];\nfor (let i = 0; i < 3; i++) {          \n    funcs[i] = function() {            \n      console.log(\"My value: \" + i); \n    };\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 7, + "commentItems": [ + { + "_id": "62f321c3082fcc3049e907a0", + "creator": "DanMan", + "createdAt": 1374837130000, + "text": "You sure you don't want funcs to be an array, if you're using numeric indices? Just a heads up.", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a1", + "creator": "Costa Michailidis", + "createdAt": 1481140113000, + "text": "Made a more detailed answer below: stackoverflow.com/a/41023816/1027004", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a2", + "creator": "Pawel", + "createdAt": 1488294602000, + "text": "The design pattern perfectly matching this scenario stackoverflow.com/a/42512295/696535", + "upvotes": 375, + "upvoterUsernames": [], + "downvotes": 375, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a3", + "creator": "MukulSharma", + "createdAt": 1497604825000, + "text": "why this does not work - funcs[i] = function abc () { return function() { console.log("My value: " + i); \t };", + "upvotes": 910, + "upvoterUsernames": [], + "downvotes": 910, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a4", + "creator": "S.D.", + "createdAt": 1591115422000, + "text": "Added an example of this issue in for in and for of loops.", + "upvotes": 273, + "upvoterUsernames": [], + "downvotes": 273, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a5", + "creator": "S.D.", + "createdAt": 1596015511000, + "text": "@3limin4t0r Corrected.", + "upvotes": 749, + "upvoterUsernames": [], + "downvotes": 749, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e907a6", + "creator": "BenKoshy", + "createdAt": 1599521627000, + "text": "This is why I hate javascript.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 459784, + "uvac": 459825 + } + }, + { + "_id": "62f321bb082fcc3049e8feb6", + "title": "What is the difference between "let" and "var"?", + "title-lowercase": "what is the difference between "let" and "var"?", + "creator": "TM.", + "createdAt": 1239998966000, + "status": "open", + "text": "

ECMAScript 6 introduced the let statement.

\n

I've heard that it's described as a local variable, but I'm still not quite sure how it behaves differently than the var keyword.

\n

What are the differences? When should let be used instead of var?

\n", + "upvotes": 7289, + "upvoterUsernames": [], + "downvotes": 1444, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2025023, + "answers": 30, + "answerItems": [ + { + "_id": "62f321be082fcc3049e9012c", + "creator": "Ben S", + "createdAt": 1239999107000, + "text": "

Here's an explanation of the let keyword with some examples.

\n\n
\n

let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function

\n
\n\n

This table on Wikipedia shows which browsers support Javascript 1.7.

\n\n

Note that only Mozilla and Chrome browsers support it. IE, Safari, and potentially others don't.

\n", + "upvotes": 190, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ee082fcc3049e912f0", + "creator": "Shapon Pal", + "createdAt": 1546924293000, + "text": "Now let support all latest browser today except Opera, Blackberry & QQ Browsers.", + "upvotes": 1868, + "upvoterUsernames": [], + "downvotes": 1868, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9012e", + "creator": "vlio20", + "createdAt": 1425638470000, + "text": "

Here is an example for the difference between the two (support just started for chrome):
\n\"enter

\n\n

As you can see the var j variable is still having a value outside of the for loop scope (Block Scope), but the let i variable is undefined outside of the for loop scope.

\n\n

\r\n
\r\n
\"use strict\";\r\nconsole.log(\"var:\");\r\nfor (var j = 0; j < 2; j++) {\r\n  console.log(j);\r\n}\r\n\r\nconsole.log(j);\r\n\r\nconsole.log(\"let:\");\r\nfor (let i = 0; i < 2; i++) {\r\n  console.log(i);\r\n}\r\n\r\nconsole.log(i);
\r\n
\r\n
\r\n

\n", + "upvotes": 99, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9012d", + "creator": "olliej", + "createdAt": 1240004302000, + "text": "

There are some subtle differences — let scoping behaves more like variable scoping does in more or less any other languages.

\n\n

e.g. It scopes to the enclosing block, They don't exist before they're declared, etc.

\n\n

However it's worth noting that let is only a part of newer Javascript implementations and has varying degrees of browser support.

\n", + "upvotes": 110, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90130", + "creator": "Lcf.vs", + "createdAt": 1433278757000, + "text": "

The accepted answer is missing a point:

\n\n
{\n  let a = 123;\n};\n\nconsole.log(a); // ReferenceError: a is not defined\n
\n", + "upvotes": 214, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ee082fcc3049e912f6", + "creator": "Dave Newton", + "createdAt": 1459459933000, + "text": "@stimpy77\t It explicitly states "let is scoped to the nearest enclosing block"; does every way that manifests need to be included?", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f322ee082fcc3049e912f8", + "creator": "Jon Davis", + "createdAt": 1459460313000, + "text": "there were a lot of examples and none of them properly demonstrated the matter .. I might've upvoted both the accepted answer and this one?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9012f", + "creator": "Gurpreet Singh", + "createdAt": 1432721783000, + "text": "

let can also be used to avoid problems with closures. It binds fresh value rather than keeping an old reference as shown in examples below.

\n\n

\r\n
\r\n
for(var i=1; i<6; i++) {\r\n  $(\"#div\" + i).click(function () { console.log(i); });\r\n}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n<p>Clicking on each number will log to console:</p> \r\n<div id=\"div1\">1</div>\r\n<div id=\"div2\">2</div>\r\n<div id=\"div3\">3</div>\r\n<div id=\"div4\">4</div>\r\n<div id=\"div5\">5</div>
\r\n
\r\n
\r\n

\n\n

Code above demonstrates a classic JavaScript closure problem. Reference to the i variable is being stored in the click handler closure, rather than the actual value of i.

\n\n

Every single click handler will refer to the same object because there’s only one counter object which holds 6 so you get six on each click.

\n\n

A general workaround is to wrap this in an anonymous function and pass i as an argument. Such issues can also be avoided now by using let instead var as shown in the code below.

\n\n

(Tested in Chrome and Firefox 50)

\n\n

\r\n
\r\n
for(let i=1; i<6; i++) {\r\n  $(\"#div\" + i).click(function () { console.log(i); });\r\n}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n<p>Clicking on each number will log to console:</p> \r\n<div id=\"div1\">1</div>\r\n<div id=\"div2\">2</div>\r\n<div id=\"div3\">3</div>\r\n<div id=\"div4\">4</div>\r\n<div id=\"div5\">5</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 1246, + "upvoterUsernames": [], + "downvotes": 461, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ee082fcc3049e912fb", + "creator": "Marie", + "createdAt": 1453991128000, + "text": "While reading the link in @Jim s comment I noticed a note that mentioned let working in Firefox 44.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322ee082fcc3049e912fd", + "creator": "KWallace", + "createdAt": 1571853362000, + "text": "Has something changed in Javascript since this answer was posted? Both the "var" and "let" version produce the same results.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90131", + "creator": "RDoc", + "createdAt": 1439253329000, + "text": "

It also appears that, at least in Visual Studio 2015, TypeScript 1.5, \"var\" allows multiple declarations of the same variable name in a block, and \"let\" doesn't.

\n\n

This won't generate a compile error:

\n\n
var x = 1;\nvar x = 2;\n
\n\n

This will:

\n\n
let x = 1;\nlet x = 2;\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90132", + "creator": "Abdennour TOUMI", + "createdAt": 1450322523000, + "text": "

May the following two functions show the difference:

\n\n
function varTest() {\n    var x = 31;\n    if (true) {\n        var x = 71;  // Same variable!\n        console.log(x);  // 71\n    }\n    console.log(x);  // 71\n}\n\nfunction letTest() {\n    let x = 31;\n    if (true) {\n        let x = 71;  // Different variable\n        console.log(x);  // 71\n    }\n    console.log(x);  // 31\n}\n
\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90133", + "creator": "zangw", + "createdAt": 1453043491000, + "text": "\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ef082fcc3049e91300", + "creator": "N-ate", + "createdAt": 1511300790000, + "text": "So let is hoisted, but unavailable? How is that different than 'not hoisted'?", + "upvotes": 1709, + "upvoterUsernames": [], + "downvotes": 1709, + "downvoterUsernames": [] + }, + { + "_id": "62f322ef082fcc3049e91302", + "creator": "N-ate", + "createdAt": 1511367537000, + "text": "Hopefully Brian or Bergi come back to answer this. Is the declaration of let hoisted, but not the assignment? Thanks!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90134", + "creator": "Gurucharan M K", + "createdAt": 1463658070000, + "text": "

ECMAScript 6 added one more keyword to declare variables other the \"const\" other than \"let\".

\n\n

The primary goal of introduction of \"let\" and \"const\" over \"var\" is to have block scoping instead of traditional lexical scoping.\nThis article explains very briefly difference between \"var\" and \"let\" and it also covers the discussion on \"const\".

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90135", + "creator": "swaraj patil", + "createdAt": 1467361355000, + "text": "

Now I think there is better scoping of variables to a block of statements using let:

\n\n
function printnums()\n{\n    // i is not accessible here\n    for(let i = 0; i <10; i+=)\n    {\n       console.log(i);\n    }\n    // i is not accessible here\n\n    // j is accessible here\n    for(var j = 0; j <10; j++)\n    {\n       console.log(j);\n    }\n    // j is accessible here\n}\n
\n\n

I think people will start using let here after so that they will have similar scoping in JavaScript like other languages, Java, C#, etc.

\n\n

People with not a clear understanding about scoping in JavaScript used to make the mistake earlier.

\n\n

Hoisting is not supported using let.

\n\n

With this approach errors present in JavaScript are getting removed.

\n\n

Refer to ES6 In Depth: let and const to understand it better.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90137", + "creator": "zloctb", + "createdAt": 1469122936000, + "text": "

Some hacks with let:

\n\n

1.

\n\n
    let statistics = [16, 170, 10];\n    let [age, height, grade] = statistics;\n\n    console.log(height)\n
\n\n

2.

\n\n
    let x = 120,\n    y = 12;\n    [x, y] = [y, x];\n    console.log(`x: ${x} y: ${y}`);\n
\n\n

3.

\n\n
    let node = {\n                   type: \"Identifier\",\n                   name: \"foo\"\n               };\n\n    let { type, name, value } = node;\n\n    console.log(type);      // \"Identifier\"\n    console.log(name);      // \"foo\"\n    console.log(value);     // undefined\n\n    let node = {\n        type: \"Identifier\"\n    };\n\n    let { type: localType, name: localName = \"bar\" } = node;\n\n    console.log(localType);     // \"Identifier\"\n    console.log(localName);     // \"bar\"\n
\n\n

Getter and setter with let:

\n\n
let jar = {\n    numberOfCookies: 10,\n    get cookies() {\n        return this.numberOfCookies;\n    },\n    set cookies(value) {\n        this.numberOfCookies = value;\n    }\n};\n\nconsole.log(jar.cookies)\njar.cookies = 7;\n\nconsole.log(jar.cookies)\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ef082fcc3049e91305", + "creator": "Rehan Haider", + "createdAt": 1547031428000, + "text": "In example 3 you are re-declaring node which cause exception. These all examples also work perfectly with var too.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322ef082fcc3049e91307", + "creator": "TylerH", + "createdAt": 1618339850000, + "text": "This doesn't answer the question; it could benefit from an explanation as to what each block of code is doing.", + "upvotes": 359, + "upvoterUsernames": [], + "downvotes": 359, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90136", + "creator": "Dmitry", + "createdAt": 1467937271000, + "text": "

let is interesting, because it allows us to do something like this:

\n\n
(() => {\n    var count = 0;\n\n    for (let i = 0; i < 2; ++i) {\n        for (let i = 0; i < 2; ++i) {\n            for (let i = 0; i < 2; ++i) {\n                console.log(count++);\n            }\n        }\n    }\n})();\n
\n\n

Which results in counting [0, 7].

\n\n

Whereas

\n\n
(() => {\n    var count = 0;\n\n    for (var i = 0; i < 2; ++i) {\n        for (var i = 0; i < 2; ++i) {\n            for (var i = 0; i < 2; ++i) {\n                console.log(count++);\n            }\n        }\n    }\n})();\n
\n\n

Only counts [0, 1].

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ef082fcc3049e9130a", + "creator": "Bekim Bacaj", + "createdAt": 1633375671000, + "text": "yes, it adds much more confusion than necessary and where there should be none.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90139", + "creator": "anandharshan", + "createdAt": 1482831895000, + "text": "

This article clearly defines the difference between var, let and const

\n\n
\n

const is a signal that the identifier won’t be reassigned.

\n \n

let, is a signal that the variable may be reassigned, such as a\n counter in a loop, or a value swap in an algorithm. It also signals\n that the variable will be used only in the block it’s defined in,\n which is not always the entire containing function.

\n \n

var is now the weakest signal available when you define a variable\n in JavaScript. The variable may or may not be reassigned, and the\n variable may or may not be used for an entire function, or just for\n the purpose of a block or loop.

\n
\n\n

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90138", + "creator": "Daniel Sokolowski", + "createdAt": 1476421270000, + "text": "

If I read the specs right then let thankfully can also be leveraged to avoid self invoking functions used to simulate private only members - a popular design pattern that decreases code readability, complicates debugging, that adds no real code protection or other benefit - except maybe satisfying someone's desire for semantics, so stop using it. /rant

\n\n
var SomeConstructor;\n\n{\n    let privateScope = {};\n\n    SomeConstructor = function SomeConstructor () {\n        this.someProperty = \"foo\";\n        privateScope.hiddenProperty = \"bar\";\n    }\n\n    SomeConstructor.prototype.showPublic = function () {\n        console.log(this.someProperty); // foo\n    }\n\n    SomeConstructor.prototype.showPrivate = function () {\n        console.log(privateScope.hiddenProperty); // bar\n    }\n\n}\n\nvar myInstance = new SomeConstructor();\n\nmyInstance.showPublic();\nmyInstance.showPrivate();\n\nconsole.log(privateScope.hiddenProperty); // error\n
\n\n

See 'Emulating private interfaces'

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ef082fcc3049e9130e", + "creator": "Robert Siemer", + "createdAt": 1583067149000, + "text": "And why do you set hiddenProperty in the constructor? There is only one hiddenProperty for all instances in your “class”.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9013a", + "creator": "Alireza", + "createdAt": 1490193586000, + "text": "

The main difference is the scope difference, while let can be only available inside the scope it's declared, like in for loop, var can be accessed outside the loop for example. From the documentation in MDN (examples also from MDN):

\n\n
\n

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

\n \n

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

\n
\n\n
function varTest() {\n  var x = 1;\n  if (true) {\n    var x = 2;  // same variable!\n    console.log(x);  // 2\n  }\n  console.log(x);  // 2\n}\n\nfunction letTest() {\n  let x = 1;\n  if (true) {\n    let x = 2;  // different variable\n    console.log(x);  // 2\n  }\n  console.log(x);  // 1\n}`\n
\n\n
\n

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

\n
\n\n
var x = 'global';\nlet y = 'global';\nconsole.log(this.x); // \"global\"\nconsole.log(this.y); // undefined\n
\n\n
\n

When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.

\n
\n\n
var a = 1;\nvar b = 2;\n\nif (a === 1) {\n  var a = 11; // the scope is global\n  let b = 22; // the scope is inside the if-block\n\n  console.log(a);  // 11\n  console.log(b);  // 22\n} \n\nconsole.log(a); // 11\nconsole.log(b); // 2\n
\n\n

Also don't forget it's ECMA6 feature, so it's not fully supported yet, so it's better always transpiles it to ECMA5 using Babel etc... for more info about visit babel website

\n", + "upvotes": 121, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9013b", + "creator": "Moslem Shahsavan", + "createdAt": 1509194536000, + "text": "

var is global scope (hoist-able) variable.

\n\n

let and const is block scope.

\n\n
\n

test.js

\n
\n\n

\r\n
\r\n
{\r\n    let l = 'let';\r\n    const c = 'const';\r\n    var v = 'var';\r\n    v2 = 'var 2';\r\n}\r\n\r\nconsole.log(v, this.v);\r\nconsole.log(v2, this.v2);\r\nconsole.log(l); // ReferenceError: l is not defined\r\nconsole.log(c); // ReferenceError: c is not defined
\r\n
\r\n
\r\n

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9013c", + "creator": "Vipul Jain", + "createdAt": 1513507651000, + "text": "

let is a part of es6. These functions will explain the difference in easy way.

\n\n
function varTest() {\n  var x = 1;\n  if (true) {\n    var x = 2;  // same variable!\n    console.log(x);  // 2\n  }\n  console.log(x);  // 2\n}\n\nfunction letTest() {\n  let x = 1;\n  if (true) {\n    let x = 2;  // different variable\n    console.log(x);  // 2\n  }\n  console.log(x);  // 1\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9013e", + "creator": "Ankur Soni", + "createdAt": 1526994756000, + "text": "

When Using let

\n\n

The let keyword attaches the variable declaration to the scope of whatever block (commonly a { .. } pair) it's contained in. In other words,let implicitly hijacks any block's scope for its variable declaration.

\n\n

let variables cannot be accessed in the window object because they cannot be globally accessed.

\n\n
function a(){\n    { // this is the Max Scope for let variable\n        let x = 12;\n    }\n    console.log(x);\n}\na(); // Uncaught ReferenceError: x is not defined\n
\n\n

When Using var

\n\n

var and variables in ES5 has scopes in functions meaning the variables are valid within the function and not outside the function itself.

\n\n

var variables can be accessed in the window object because they cannot be globally accessed.

\n\n
function a(){ // this is the Max Scope for var variable\n    { \n        var x = 12;\n    }\n    console.log(x);\n}\na(); // 12\n
\n\n

If you want to know more continue reading below

\n\n

one of the most famous interview questions on scope also can suffice the exact use of let and var as below;

\n\n

When using let

\n\n
for (let i = 0; i < 10 ; i++) {\n    setTimeout(\n        function a() {\n            console.log(i); //print 0 to 9, that is literally AWW!!!\n        }, \n        100 * i);\n}\n
\n\n

This is because when using let, for every loop iteration the variable is scoped and has its own copy.

\n\n

When using var

\n\n
for (var i = 0; i < 10 ; i++) {\n    setTimeout(\n        function a() {\n            console.log(i); //print 10 times 10\n        }, \n        100 * i);\n}\n
\n\n

This is because when using var, for every loop iteration the variable is scoped and has shared copy.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9013d", + "creator": "Nurlan", + "createdAt": 1526628461000, + "text": "

Check this link in\nMDN

\n\n
let x = 1;\n\nif (x === 1) {\nlet x = 2;\n\nconsole.log(x);\n// expected output: 2\n}\n\nconsole.log(x);\n// expected output: 1\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f0082fcc3049e91314", + "creator": "pushkin", + "createdAt": 1527796030000, + "text": "It would additionally help to explain in words what let is doing, though that would make it a duplicate of another answer here", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9013f", + "creator": "N Randhawa", + "createdAt": 1533637557000, + "text": "

As mentioned above:

\n\n
\n

The difference is scoping. var is scoped to the nearest function\n block and let is scoped to the nearest enclosing block, which\n can be smaller than a function block. Both are global if outside any\n block.Lets see an example:

\n
\n\n

Example1:

\n\n

In my both examples I have a function myfunc. myfunc contains a variable myvar equals to 10. \nIn my first example I check if myvar equals to 10 (myvar==10) . If yes, I agian declare a variable myvar (now I have two myvar variables)using var keyword and assign it a new value (20). In next line I print its value on my console. After the conditional block I again print the value of myvar on my console. If you look at the output of myfunc, myvar has value equals to 20.

\n\n

\"let

\n\n

Example2:\nIn my second example instead of using var keyword in my conditional block I declare myvar using let keyword . Now when I call myfunc I get two different outputs: myvar=20 and myvar=10.

\n\n

So the difference is very simple i.e its scope.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90140", + "creator": "Daniel Viglione", + "createdAt": 1550074042000, + "text": "

I want to link these keywords to the Execution Context, because the Execution Context is important in all of this. The Execution Context has two phases: a Creation Phase and Execution Phase. In addition, each Execution Context has a Variable Environment and Outer Environment (its Lexical Environment).

\n\n

During the Creation Phase of an Execution Context, var, let and const will still store its variable in memory with an undefined value in the Variable Environment of the given Execution Context. The difference is in the Execution Phase. If you use reference a variable defined with var before it is assigned a value, it will just be undefined. No exception will be raised.

\n\n

However, you cannot reference the variable declared with let or const until it is declared. If you try to use it before it is declared, then an exception will be raised during the Execution Phase of the Execution Context. Now the variable will still be in memory, courtesy of the Creation Phase of the Execution Context, but the Engine will not allow you to use it:

\n\n
function a(){\n    b;\n    let b;\n}\na();\n> Uncaught ReferenceError: b is not defined\n
\n\n

With a variable defined with var, if the Engine cannot find the variable in the current Execution Context's Variable Environment, then it will go up the scope chain (the Outer Environment) and check the Outer Environment's Variable Environment for the variable. If it cannot find it there, it will continue searching the Scope Chain. This is not the case with let and const.

\n\n

The second feature of let is it introduces block scope. Blocks are defined by curly braces. Examples include function blocks, if blocks, for blocks, etc. When you declare a variable with let inside of a block, the variable is only available inside of the block. In fact, each time the block is run, such as within a for loop, it will create a new variable in memory.

\n\n

ES6 also introduces the const keyword for declaring variables. const is also block scoped. The difference between let and const is that const variables need to be declared using an initializer, or it will generate an error.

\n\n

And, finally, when it comes to the Execution Context, variables defined with var will be attached to the 'this' object. In the global Execution Context, that will be the window object in browsers. This is not the case for let or const.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90141", + "creator": "Mile Mijatović", + "createdAt": 1550337465000, + "text": "

\r\n
\r\n
const name = 'Max';\nlet age = 33;\nvar hasHobbies = true;\n\nname = 'Maximilian';\nage = 34;\nhasHobbies = false;\n\nconst summarizeUser = (userName, userAge, userHasHobby) => {\n    return (\n    'Name is ' +\n    userName +\n    ', age is ' +\n    userAge +\n    ' and the user has hobbies: ' +\n    userHasHobby\n    );\n}\n\nconsole.log(summarizeUser(name, age, hasHobbies));
\r\n
\r\n
\r\n

\n

As you can see from running the code above, when you try to change the const variable, you will get an error:

\n
\n

Attempting to override 'name' which is a constant.

\n
\n

or

\n
\n

TypeError: Invalid assignment to const 'name'.

\n
\n

but take a look at the let variable.

\n

First we declare let age = 33, and later assign some other value age = 34;, which is OK; we don't have any errors when we try to change let variable

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f0082fcc3049e91319", + "creator": "TylerH", + "createdAt": 1618339897000, + "text": "Code should be written out in SO posts, not shared via a screenshot.", + "upvotes": 91, + "upvoterUsernames": [], + "downvotes": 91, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90142", + "creator": "Lucian", + "createdAt": 1552323165000, + "text": "

As I am currently trying to get an in depth understanding of JavaScript I will share my brief research which contains some of the great pieces already discussed plus some other details in a different perspective.

\n\n

Understanding the difference between var and let can be easier if we understand the difference between function and block scope.

\n\n

Let's consider the following cases:

\n\n
(function timer() {\n    for(var i = 0; i <= 5; i++) {\n        setTimeout(function notime() { console.log(i); }, i * 1000);\n    }\n})();\n\n\n   Stack            VariableEnvironment //one VariablEnvironment for timer();\n                                       // when the timer is out - the value will be the same value for each call\n5. [setTimeout, i]  [i=5] \n4. [setTimeout, i]  \n3. [setTimeout, i]\n2. [setTimeout, i]\n1. [setTimeout, i]\n0. [setTimeout, i]\n\n####################    \n\n(function timer() {\n    for (let i = 0; i <= 5; i++) {\n        setTimeout(function notime() { console.log(i); }, i * 1000);\n    }\n})();\n\n   Stack           LexicalEnvironment - each iteration has a new lexical environment\n5. [setTimeout, i]  [i=5]       \n                      LexicalEnvironment \n4. [setTimeout, i]    [i=4]     \n                        LexicalEnvironment \n3. [setTimeout, i]      [i=3]       \n                         LexicalEnvironment \n2. [setTimeout, i]       [i=2]\n                           LexicalEnvironment \n1. [setTimeout, i]         [i=1]\n                             LexicalEnvironment \n0. [setTimeout, i]           [i=0]\n
\n\n

when timer() gets called an ExecutionContext is created which will contain both the VariableEnvironment and all the LexicalEnvironments corresponding to each iteration.

\n\n

And a simpler example

\n\n

Function Scope

\n\n
function test() {\n    for(var z = 0; z < 69; z++) {\n        //todo\n    }\n    //z is visible outside the loop\n}\n
\n\n

Block Scope

\n\n
function test() {\n    for(let z = 0; z < 69; z++) {\n        //todo\n    }\n    //z is not defined :(\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90143", + "creator": "daCoda", + "createdAt": 1555548572000, + "text": "

let vs var. It's all about scope.

\n\n

var variables are global and can be accessed basically everywhere, while let variables are not global and only exist until a closing parenthesis kills them.

\n\n

See my example below, and note how the lion (let) variable acts differently in the two console.logs; it becomes out of scope in the 2nd console.log.

\n\n
var cat = \"cat\";\nlet dog = \"dog\";\n\nvar animals = () => {\n    var giraffe = \"giraffe\";\n    let lion = \"lion\";\n\n    console.log(cat);  //will print 'cat'.\n    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).\n\n    console.log(giraffe); //will print 'giraffe'.\n    console.log(lion); //will print 'lion', as lion is within scope.\n}\n\nconsole.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).\nconsole.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90144", + "creator": "Rafael Herscovici", + "createdAt": 1556418073000, + "text": "

I think the terms and most of the examples are a bit overwhelming,\nThe main issue i had personally with the difference is understanding what a \"Block\" is.\nAt some point i realized, a block would be any curly brackets except for IF statement.\nan opening bracket { of a function or loop will define a new block, anything defined with let within it, will not be available after the closing bracket } of the same thing (function or loop);\nWith that in mind, it was easier to understand:

\n\n

\r\n
\r\n
let msg = \"Hello World\";\r\n\r\nfunction doWork() { // msg will be available since it was defined above this opening bracket!\r\n  let friends = 0;\r\n  console.log(msg);\r\n\r\n  // with VAR though:\r\n  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!\r\n  console.log(iCount2);\r\n  \r\n    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined\r\n  console.log(iCount1);\r\n  \r\n} // friends will no be available after this closing bracket!\r\ndoWork();\r\nconsole.log(friends);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90146", + "creator": "Srikrushna", + "createdAt": 1580038761000, + "text": "

ES6 introduced two new keyword(let and const) alternate to var.

\n

When you need a block level deceleration you can go with let and const instead of var.

\n

The below table summarize the difference between var, let and const

\n

\"enter

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90145", + "creator": "Piklu Dey", + "createdAt": 1567855515000, + "text": "

The below shows how 'let' and 'var' are different in the scope:

\n\n
let gfoo = 123;\nif (true) {\n    let gfoo = 456;\n}\nconsole.log(gfoo); // 123\n\nvar hfoo = 123;\nif (true) {\n    var hfoo = 456;\n}\nconsole.log(hfoo); // 456\n
\n\n

The gfoo, defined by let initially is in the global scope, and when we declare gfoo again inside the if clause its scope changed and when a new value is assigned to the variable inside that scope it does not affect the global scope.

\n\n

Whereas hfoo, defined by var is initially in the global scope, but again when we declare it inside the if clause, it considers the global scope hfoo, although var has been used again to declare it. And when we re-assign its value we see that the global scope hfoo is also affected. This is the primary difference.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90148", + "creator": "Sarvar N", + "createdAt": 1603646121000, + "text": "

I just came across one use case that I had to use var over let to introduce new variable. Here's a case:

\n

I want to create a new variable with dynamic variable names.

\n
let variableName = 'a';\neval("let " + variableName + '= 10;');\nconsole.log(a);   // this doesn't work\n
\n
var variableName = 'a';\neval("var " + variableName + '= 10;');\nconsole.log(a);   // this works\n
\n

The above code doesn't work because eval introduces a new block of code. The declaration using var will declare a variable outside of this block of code since var declares a variable in the function scope.

\n

let, on the other hand, declares a variable in a block scope. So, a variable will only be visible in eval block.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322f1082fcc3049e9131f", + "creator": "Bekim Bacaj", + "createdAt": 1633369594000, + "text": "In fact, that's because the re-declaration of a JavaScript let proposition is not allowed.", + "upvotes": 1192, + "upvoterUsernames": [], + "downvotes": 1192, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90147", + "creator": "Hasan Sefa Ozalp", + "createdAt": 1589216688000, + "text": "

In most basic terms,

\n\n
for (let i = 0; i < 5; i++) {\n  // i accessible ✔️\n}\n// i not accessible ❌\n
\n\n
\n\n
for (var i = 0; i < 5; i++) {\n  // i accessible ✔️\n}\n// i accessible ✔️\n
\n\n
\n\n

⚡️ Sandbox to play around ↓

\n\n

\"Edit

\n", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90149", + "creator": "Taib Islam Dipu", + "createdAt": 1608044505000, + "text": "

Before 2015, using the var keyword was the only way to declare a JavaScript variable.

\n

After ES6 (a JavaScript version), it allows 2 new keywords let & const.

\n

let = can be re-assigned
\nconst = cannot be re-assigned ( const which comes from constant, short-form 'const' )

\n

Example:

\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2032312, + "uvac": 2032342 + } + }, + { + "_id": "62f321bb082fcc3049e8fec6", + "title": "What is the !! (not not) operator in JavaScript?", + "title-lowercase": "what is the !! (not not) operator in javascript?", + "creator": "Hexagon Theory", + "createdAt": 1240560838000, + "status": "open", + "text": "

I saw some code that seems to use an operator I don't recognize, in the form of two exclamation points, like so: !!. Can someone please tell me what this operator does?

\n\n

The context in which I saw this was,

\n\n
this.vertical = vertical !== undefined ? !!vertical : this.vertical;\n
\n", + "upvotes": 4996, + "upvoterUsernames": [], + "downvotes": 1099, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 815823, + "answers": 36, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e904c0", + "creator": "Steve Harrison", + "createdAt": 1240561219000, + "text": "

It seems that the !! operator results in a double negation.

\n
var foo = "Hello, World!";\n\n!foo // Result: false\n!!foo // Result: true\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904be", + "creator": "stevehipwell", + "createdAt": 1240561087000, + "text": "

It converts Object to boolean. If it was falsey (e.g., 0, null, undefined, etc.), it would be false, otherwise, true.

\n
!object  // Inverted Boolean\n!!object // Noninverted Boolean, so true Boolean representation\n
\n

So !! is not an operator; it's just the ! operator twice.

\n

It may be simpler to do:

\n
Boolean(object) // Boolean\n
\n

Real World Example "Test IE version":

\n
const isIE8 = !! navigator.userAgent.match(/MSIE 8.0/);\nconsole.log(isIE8); // Returns true or false\n
\n

If you ⇒

\n
console.log(navigator.userAgent.match(/MSIE 8.0/));\n// Returns either an Array or null\n
\n

But if you ⇒

\n
console.log(!!navigator.userAgent.match(/MSIE 8.0/));\n// Returns either true or false\n
\n", + "upvotes": 6290, + "upvoterUsernames": [], + "downvotes": 2818, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3246e082fcc3049e918da", + "creator": "Micah Snyder", + "createdAt": 1240597647000, + "text": "An easy way to describe it is: Boolean(5) === !!5; Same casting, fewer characters.", + "upvotes": 227, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918dc", + "creator": "thetoolman", + "createdAt": 1342410802000, + "text": "This is used to convert truthy values to boolean true, and falsy values too boolean false.", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918de", + "creator": "Neurotransmitter", + "createdAt": 1480359184000, + "text": "All browsers which support ! support !!. Because it is not an operator, it is a pair of operators.", + "upvotes": 321, + "upvoterUsernames": [], + "downvotes": 321, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918e0", + "creator": "Diego Ponciano", + "createdAt": 1631142594000, + "text": "Is it a good practice to negate !!! (3 times) an object to get the boolean value?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918e2", + "creator": "Abel", + "createdAt": 1637132455000, + "text": "@DiegoPonciano No, !!!value would negate !!value, which in the end is the same as !value.", + "upvotes": 5855, + "upvoterUsernames": [], + "downvotes": 5855, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918e4", + "creator": "Dale Harris", + "createdAt": 1648710876000, + "text": "@awe Yes... you're correct. That was my mistake.", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904bf", + "creator": "Greg", + "createdAt": 1240561129000, + "text": "

It's just the logical NOT operator, twice. It's used to convert something to Boolean, e.g.:

\n
true === !!10\n\nfalse === !!0\n
\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c1", + "creator": "Darren Clark", + "createdAt": 1240562019000, + "text": "

I suspect this is a leftover from C++ where people override the ! operator, but not the bool operator.

\n

So to get a negative (or positive) answer in that case, you would first need to use the ! operator to get a Boolean, but if you wanted to check the positive case you would use !!.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c2", + "creator": "Crescent Fresh", + "createdAt": 1252603613000, + "text": "

!! converts the value to the right of it to its equivalent Boolean value. (Think poor man's way of "type-casting".) Its intent is usually to convey to the reader that the code does not care what value is in the variable, but what its "truth" value is.

\n", + "upvotes": 221, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246e082fcc3049e918e8", + "creator": "Daniel A. White", + "createdAt": 1252603736000, + "text": "Or in the case of a boolean value on the right, it does nothing.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3246e082fcc3049e918ea", + "creator": "CodyBugstein", + "createdAt": 1462577820000, + "text": "But what is the point? If I do if(0){... Javascript already knows this is false. Why is it better to say if(!!0){...?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904c3", + "creator": "Paul McMillan", + "createdAt": 1252603667000, + "text": "

It converts the suffix to a Boolean value.

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c4", + "creator": "Tom Ritter", + "createdAt": 1252603716000, + "text": "

It's a horribly obscure way to do a type conversion.

\n

! means NOT. So !true is false, and !false is true. !0 is true, and !1 is false.

\n

So you're converting a value to a Boolean, inverting it, and then inverting it again.

\n
// Maximum Obscurity:\nval.enabled = !!userId;\n\n// Partial Obscurity:\nval.enabled = (userId != 0) ? true : false;\n\n// And finally, much easier to understand:\nval.enabled = (userId != 0);\n\n// Or just\nval.enabled = Boolean(userId);\n
\n

Note: the latter two expressions aren't exactly equivalent to the first expression when it comes to some edge case (when userId is [], for example), due to the way the != operator works and what values are considered truthy.

\n", + "upvotes": 1925, + "upvoterUsernames": [], + "downvotes": 948, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3246f082fcc3049e918ee", + "creator": "cllpse", + "createdAt": 1252604309000, + "text": "!!false = false. !!true = true", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [] + }, + { + "_id": "62f3246f082fcc3049e918f0", + "creator": "Maik Lowrey", + "createdAt": 1617913985000, + "text": "@cllpse You mean !!false === false and !!true === true ? ;-)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3246f082fcc3049e918f2", + "creator": "Sam Johnson", + "createdAt": 1628836968000, + "text": "It's worth noting though that !! is portable to virtually any C-style language", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3246f082fcc3049e918f4", + "creator": "Unmitigated", + "createdAt": 1659729296000, + "text": "@slim false != 0 is actually false.", + "upvotes": 3029, + "upvoterUsernames": [], + "downvotes": 3029, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904c6", + "creator": "Annika Backstrom", + "createdAt": 1252603758000, + "text": "

! is "Boolean not", which essentially typecasts the value of "enable" to its boolean opposite. The second ! flips this value. So, !!enable means "not not enable," giving you the value of enable as a Boolean.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904cd", + "creator": "Navin Rauniyar", + "createdAt": 1397017763000, + "text": "

The !! construct is a simple way of turning any JavaScript expression into\nits Boolean equivalent.

\n\n

For example: !!\"he shot me down\" === true and !!0 === false.

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ac082fcc3049e918f8", + "creator": "ruffin", + "createdAt": 1430329102000, + "text": "Very close to the important distinction. Key is that 0 === false is false and !!0 === false is true.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904ce", + "creator": "Warren Davis", + "createdAt": 1398344749000, + "text": "

There are tons of great answers here, but if you've read down this far, this helped me to 'get it'. Open the console in Chrome (etc.), and start typing:

\n
!(!(1))\n!(!(0))\n!(!('truthy')) \n!(!(null))\n!(!(''))\n!(!(undefined))\n!(!(new Object())\n!(!({}))\nwoo = 'hoo'\n!(!(woo))\n...etc., etc., until the light goes on ;)\n
\n

Naturally, these are all the same as merely typing !!someThing, but the added parentheses might help make it more understandable.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d0", + "creator": "JeevanReddy Avanaganti", + "createdAt": 1425457535000, + "text": "

Here is a piece of code from AngularJS:

\n
var requestAnimationFrame = $window.requestAnimationFrame ||\n                            $window.webkitRequestAnimationFrame ||\n                            $window.mozRequestAnimationFrame;\n\nvar rafSupported = !!requestAnimationFrame;\n
\n

Their intention is to set rafSupported to true or false based on the availability of function in requestAnimationFrame.

\n

It can be achieved by checking in the following way in general:

\n
if(typeof requestAnimationFrame === 'function')\n    rafSupported =true;\nelse\n    rafSupported =false;\n
\n

The short way could be using !!

\n
rafSupported = !!requestAnimationFrame;\n
\n

So if requestAnimationFrame was assigned a function then !requestAnimationFrame would be false and one more ! of it would be true.

\n

If requestAnimationFrame was assigned undefined then !requestAnimationFrame would be true and one more ! of it would be false.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904cf", + "creator": "user3698272", + "createdAt": 1403926513000, + "text": "
a = 1;\nalert(!a) // -> false : a is not not defined\nalert(!!a) // -> true : a is not not defined\n
\n\n

For !a, it checks whether a is NOT defined, while !!a checks if the variable is defined.

\n\n

!!a is the same as !(!a). If a is defined, a is true, !a is false, and !!a is true.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d2", + "creator": "Greg Gum", + "createdAt": 1449233449000, + "text": "

!!x is shorthand for Boolean(x).

\n

The first bang forces the JavaScript engine to run Boolean(x), but it also has the side effect of inverting the value. So the second bang undoes the side effect.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d1", + "creator": "GibboK", + "createdAt": 1435735752000, + "text": "

Some operators in JavaScript perform implicit type conversions, and are sometimes used for type conversion.

\n

The unary ! operator converts its operand to a Boolean and negates it.

\n

This fact leads to the following idiom that you can see in your source code:

\n
!!x // Same as Boolean(x). Note double exclamation mark\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d3", + "creator": "GreQ", + "createdAt": 1464171514000, + "text": "

I think worth mentioning is that a condition combined with logical AND/OR will not return a Boolean value, but the last success or first fail in case of && and the first success or last fail in case of || of the condition chain.

\n
res = (1 && 2); // res is 2\nres = (true && alert) // res is function alert()\nres = ('foo' || alert) // res is 'foo'\n
\n

In order to cast the condition to a true Boolean literal we can use the double negation:

\n
res = !!(1 && 2); // res is true\nres = !!(true && alert) // res is true\nres = !!('foo' || alert) // res is true\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d4", + "creator": "Abhay Dixit", + "createdAt": 1468578301000, + "text": "

Use the logical not operator two times.

\n

It means !true = false and !!true = true.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d6", + "creator": "Wouter Vanherck", + "createdAt": 1490771721000, + "text": "

After seeing all these great answers, I would like to add another reason for using !!. Currently I'm working in Angular 2-4 (TypeScript) and I want to return a Boolean as false when my user is not authenticated. If he isn't authenticated, the token-string would be null or "". I can do this by using the next block of code:

\n
public isAuthenticated(): boolean {\n   return !!this.getToken();\n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d5", + "creator": "Ryan Taylor", + "createdAt": 1476910836000, + "text": "

I just wanted to add that

\n
if(variableThing){\n  // do something\n}\n
\n

is the same as

\n
if(!!variableThing){\n  // do something\n}\n
\n

But this can be an issue when something is undefined.

\n
// a === undefined, b is an empty object (eg. b.asdf === undefined)\nvar a, b = {};\n\n// Both of these give error a.foo is not defined etc.\n// you'd see the same behavior for !!a.foo and !!b.foo.bar\n\na.foo \nb.foo.bar\n\n// This works -- these return undefined\n\na && a.foo\nb.foo && b.foo.bar\nb && b.foo && b.foo.bar\n
\n

The trick here is the chain of &&s will return the first falsey value it finds -- and this can be fed to an if statement etc. So if b.foo is undefined, it will return undefined and skip the b.foo.bar statement, and we get no error.

\n

The above return undefined but if you have an empty string, false, null, 0, undefined those values will return and soon as we encounter them in the chain -- [] and {} are both "truthy" and we will continue down the so-called "&& chain" to the next value to the right.

\n

P.S. Another way of doing the above (b && b.foo) is (b || {}).foo. Those are equivalent, because if b is undefined then b || {} will be {}, and you'll be accessing a value in an empty object (no error) instead of trying to access a value within "undefined" (causes an error).

\n

So, (b || {}).foo is the same as b && b.foo and ((b || {}).foo || {}).bar is the same as b && b.foo && b.foo.bar.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d8", + "creator": "efkan", + "createdAt": 1499756630000, + "text": "

It returns the Boolean value of a variable.

\n

Instead, the Boolean class can be used.

\n

(Please read the code descriptions.)

\n
var X = "test"; // The X value is "test" as a String value\nvar booleanX = !!X // booleanX is `true` as a Boolean value because non-empty strings evaluates as `true` in Boolean\nvar whatIsXValueInBoolean = Boolean(X) // whatIsXValueInBoolean is `true` again\nconsole.log(Boolean(X) === !!X) // Writes `true`\n
\n

Namely, Boolean(X) = !!X in use.

\n

Please check code snippet out below

\n

\r\n
\r\n
let a = 0\nconsole.log(\"a: \", a) // Writes a value in its kind\nconsole.log(\"!a: \", !a) // Writes '0 is NOT true in Boolean' value as Boolean - so that's true. In Boolean, 0 means false and 1 means true.\nconsole.log(\"!!a: \", !!a) // Writes 0 value in Boolean. 0 means false.\nconsole.log(\"Boolean(a): \", Boolean(a)) // Equals `!!a`\nconsole.log(\"\\n\") // Newline\n\na = 1\nconsole.log(\"a: \", a)\nconsole.log(\"!a: \", !a)\nconsole.log(\"!!a: \", !!a) // Writes 1 value in Boolean\nconsole.log(\"\\n\") // Newline\n\na = \"\"\nconsole.log(\"a: \", a)\nconsole.log(\"!a: \", !a) // Writes '\"\" is NOT true in Boolean' value as Boolean - so that's true. In Boolean, empty strings, null and undefined values mean false and if there is a string it means true.\nconsole.log(\"!!a: \", !!a) // Writes \"\" value in Boolean\nconsole.log(\"\\n\") // Newline\n\na = \"test\"\nconsole.log(\"a: \", a) // Writes a value in its kind\nconsole.log(\"!a: \", !a)\nconsole.log(\"!!a: \", !!a) // Writes \"test\" value in Boolean\n\nconsole.log(\"Boolean(a) === !!a: \", Boolean(a) === !!a) // writes true
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d7", + "creator": "Alireza", + "createdAt": 1498277783000, + "text": "

!! is using the NOT operation twice together. ! converts the value to a Boolean and reverses it, so using it twice, showing the Boolean (false or true) of that value. Here is a simple example to see how !! works:

\n

At first, the place you have:

\n
var zero = 0;\n
\n

Then you do !0. It will be converted to Boolean and be evaluated to true, because 0 is falsy, so you get the reversed value and converted to Boolean, so it gets evaluated to true.

\n
!zero; //true\n
\n

But we don't want the reversed Boolean version of the value, so we can reverse it again to get our result! That's why we use another !.

\n

Basically, !! makes us sure the value we get is Boolean, not falsy, truthy, string, etc...

\n

So it's like using the Boolean function in JavaScript, but an easier and shorter way to convert a value to Boolean:

\n
var zero = 0;\n!!zero; //false\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904d9", + "creator": "Khuong", + "createdAt": 1532490772000, + "text": "

It forces all things to Boolean.

\n

For example:

\n
console.log(undefined); // -> undefined\nconsole.log(!undefined); // -> true\nconsole.log(!!undefined); // -> false\n\nconsole.log('abc'); // -> abc\nconsole.log(!'abc'); // -> false\nconsole.log(!!'abc'); // -> true\n\nconsole.log(0 === false); // -> undefined\nconsole.log(!0 === false); // -> false\nconsole.log(!!0 === false); // -> true\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904da", + "creator": "مهدی عابدی برنامه نویس و مشاور", + "createdAt": 1536986241000, + "text": "

Sometimes it is necessary to check whether we have a value in the function or not, and the amount itself is not important to us, but whether or not it matters.

\n

For example, we want to check if the user has a major or not and we have a function just like:

\n
hasMajor() {return this.major} // It returns "(users major is) Science"\n
\n

But the answer is not important to us. We just want to check if it has a major or not and we need a Boolean value (true or false). How do we get it?

\n

Just like this:

\n
hasMajor() { return !(!this.major)}\n
\n

Or as the same

\n
hasMajor() {return !!this.major)}\n
\n

If this.major has a value then !this.major returns false, but because the value has exits and we need to return true, we use ! twice to return the correct answer, !(!this.major).

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904dc", + "creator": "Lakmal", + "createdAt": 1572379009000, + "text": "

To cast your JavaScript variables to Boolean,

\n
var firstname = "test";\n// Type of firstname is string\n\nvar firstNameNotEmpty = !!firstname;\n// Type of firstNameNotEmpty is Boolean\n
\n

JavaScript false for "", 0, undefined, and null.

\n

JavaScript is true for number other than zero, not empty strings, {}, [] and new Date() so,

\n
!!("test") /* Is true */\n!!("") /* Is false */\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904db", + "creator": "KWallace", + "createdAt": 1552243406000, + "text": "

This question has been answered quite thoroughly, but I'd like to add an answer that I hope is as simplified as possible, making the meaning of !! as simple to grasp as can be.

\n

Because JavaScript has what are called "truthy" and "falsy" values, there are expressions that when evaluated in other expressions will result in a true or false condition, even though the value or expression being examined is not actually true or false.

\n

For instance:

\n
if (document.getElementById('myElement')) {\n    // Code block\n}\n
\n

If that element does in fact exist, the expression will evaluate as true, and the code block will be executed.

\n

However:

\n
if (document.getElementById('myElement') == true) {\n    // Code block\n}\n
\n

...will not result in a true condition, and the code block will not be executed, even if the element does exist.

\n

Why? Because document.getElementById() is a "truthy" expression that will evaluate as true in this if() statement, but it is not an actual Boolean value of true.

\n

The double "not" in this case is quite simple. It is simply two nots back to back.

\n

The first one simply "inverts" the truthy or falsy value, resulting in an actual Boolean type, and then the second one "inverts" it back again to its original state, but now in an actual Boolean value. That way you have consistency:

\n
if (!!document.getElementById('myElement')) {}\n
\n

and

\n
if (!!document.getElementById('myElement') == true) {}\n
\n

will both return true, as expected.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904de", + "creator": "Lucky Brain", + "createdAt": 1611237169000, + "text": "

It is important to remember the evaluations to true and false in JavaScript:

\n\n

Applying the "logical not" operator (!) evaluates the operand, converting it to boolean and then negating it. Applying it twice will negate the negation, effectively converting the value to boolean. Not applying the operator will just be a regular assignment of the exact value. Examples:

\n
var value = 23; // number\nvar valueAsNegatedBoolean = !value; // boolean falsy (because 23 is truthy)\nvar valueAsBoolean = !!value; // boolean truthy\nvar copyOfValue = value; // number 23\n\nvar value2 = 0;\nvar value2AsNegatedBoolean = !value2; // boolean truthy (because 0 is falsy)\nvar value2AsBoolean = !!value2; // boolean falsy\nvar copyOfValue2 = value2; // number 0\n
\n\n

\r\n
\r\n
if (value) {\n  value2 = true;\n} else {\n  value2 = false;\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ad082fcc3049e91907", + "creator": "Andreas", + "createdAt": 1611250596000, + "text": "How does this add anything new or useful to the other answers?", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904dd", + "creator": "Mile Mijatović", + "createdAt": 1593595107000, + "text": "

\r\n
\r\n
const foo = 'bar';\nconsole.log(!!foo); // Boolean: true
\r\n
\r\n
\r\n

\n

! negates (inverts) a value and always returns/ produces a Boolean. So !'bar' would yield false (because 'bar' is truthy => negated + Boolean = false). With the additional ! operator, the value is negated again, so false becomes true.

\n", + "upvotes": 147, + "upvoterUsernames": [], + "downvotes": 147, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904e0", + "creator": "Umesh Bhutada", + "createdAt": 1650526740000, + "text": "

!! is simply the NOT operator twice. The net effect is just converting anything to ensure a Boolean data type.\nFor example.

\n

!!undefined is false
\n!!0 is false
\n!!null is false
\n!!anyobject is true
\n!!true is true
\n!!false is false
\n!0 is true
\n!1 is false

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904df", + "creator": "nguyenhoavuong", + "createdAt": 1636689362000, + "text": "

!! is not an operator. It's just the ! operator twice.

\n

But with JavaScript, apply !! for converting Object to Boolean is redundant and verbose in the most cases because:

\n
\n

Any object of which the value is not undefined or null, including a\nBoolean object whose value is false, evaluates to true when passed to\na conditional statement

\n
\n

Example: if ({}) { console.log("{} is true")} // logs: "{} is true"

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904e1", + "creator": "Umesh Bhutada", + "createdAt": 1655327156000, + "text": "

Just to check if exist

\n
if(!!isComplianceOnHold){\n//write code here is not undefined\n//if isComplianceOnHold is undefined or null will not enter in net is false\n// if isComplianceOnHold is not null or even boolean net result is true and enter inside if block\n}\n
\n
\n

Any object of which the value is not undefined or null, including a Boolean object whose value is false, evaluates to true when passed to a conditional statement

\n
\n", + "upvotes": 1093, + "upvoterUsernames": [], + "downvotes": 1093, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c5", + "creator": "Bill the Lizard", + "createdAt": 1252603726000, + "text": "

It's a double not operation. The first ! converts the value to Boolean and inverts its logical value. The second ! inverts the logical value back.

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c8", + "creator": "Justin Johnson", + "createdAt": 1252617549000, + "text": "

It's not a single operator; it's two. It's equivalent to the following and is a quick way to cast a value to Boolean.

\n
val.enabled = !(!enable);\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904c7", + "creator": "Christoph", + "createdAt": 1252617316000, + "text": "

!!foo applies the unary not operator twice and is used to cast to a Boolean type similar to the use of unary plus +foo to cast to a number and concatenating an empty string ''+foo to cast to a string.

\n

Instead of these hacks, you can also use the constructor functions corresponding to the primitive types (without using new) to explicitly cast values, i.e.,

\n
Boolean(foo) === !!foo\nNumber(foo)  === +foo\nString(foo)  === ''+foo\n
\n", + "upvotes": 137, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329be082fcc3049e92e15", + "creator": "Seamus", + "createdAt": 1286456010000, + "text": "But then you can run into issues with instanceof. new Boolean(1) instanceof Object -> true !!1 instanceof Object -> false", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e17", + "creator": "Christoph", + "createdAt": 1286531164000, + "text": "no, you can't: notice that the constructor functions are called without new - as explicitly mentioned in my answer", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e18", + "creator": "placeybordeaux", + "createdAt": 1451432762000, + "text": "@DiegoDD why would you choose !!+x vs x !== "0"?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904c9", + "creator": "Sergey Ilinsky", + "createdAt": 1291321274000, + "text": "

It is double Boolean negation. It is often used to check if a value is not undefined.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e904cc", + "creator": "Salman A", + "createdAt": 1337072778000, + "text": "

!!expr (two ! operators followed by an expression) returns a Boolean value (true or false) depending on the truthiness of the expression. It makes more sense when used on non-boolean types. Consider these examples, especially the 3rd example and onward:

\n
          !!false === false\n           !!true === true\n\n              !!0 === false\n!!parseInt("foo") === false // NaN is falsy\n              !!1 === true\n             !!-1 === true  // -1 is truthy\n          !!(1/0) === true  // Infinity is truthy\n\n             !!"" === false // empty string is falsy\n          !!"foo" === true  // non-empty string is truthy\n        !!"false" === true  // ...even if it contains a falsy value\n\n     !!window.foo === false // undefined value is falsy\n      !!undefined === false // undefined primitive is falsy\n           !!null === false // null is falsy\n\n             !!{} === true  // an (empty) object is truthy\n             !![] === true  // an (empty) array is truthy; PHP programmers beware!\n
\n", + "upvotes": 659, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329be082fcc3049e92e1b", + "creator": "Camilo Martin", + "createdAt": 1355817951000, + "text": "Worth noting: !!new Boolean(false) // true", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e1c", + "creator": "Camilo Martin", + "createdAt": 1355817997000, + "text": "...But also !!Boolean(false) // false", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e1d", + "creator": "Salman A", + "createdAt": 1355818521000, + "text": "new Boolean(false) is an object and an object is truthy even if it contains a falsy value!", + "upvotes": 148, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e1e", + "creator": "Yashwin Munsadwala", + "createdAt": 1623864474000, + "text": "Definite suggestion to add !!undefined //false to this great answer!", + "upvotes": 577, + "upvoterUsernames": [], + "downvotes": 577, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904cb", + "creator": "rob_james", + "createdAt": 1335270125000, + "text": "

This is a really handy way to check for undefined, \"undefined\", null, \"null\", \"\"

\n\n
if (!!var1 && !!var2 && !!var3 && !!var4 ){\n   //... some code here\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329be082fcc3049e92e20", + "creator": "rob_james", + "createdAt": 1351444957000, + "text": "Why? I still need to know var1 through 4 have values in them.", + "upvotes": 3172, + "upvoterUsernames": [], + "downvotes": 3172, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e22", + "creator": "nalply", + "createdAt": 1351495893000, + "text": "Because the && operators already "convert" its operators to boolean.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e904ca", + "creator": "Prakash", + "createdAt": 1300881225000, + "text": "

It simulates the behavior of the Boolean() casting function.\nThe first NOT returns a Boolean value no matter what operand it is given. The second NOT negates that Boolean value and so gives the true Boolean value of a variable. The end result is the same as using the Boolean() function on a value.

\n", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 8, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e90449", + "creator": "Gus", + "createdAt": 1329330902000, + "text": "Remember it by "bang, bang you're boolean"", + "upvotes": 1339, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044a", + "creator": "Vivek", + "createdAt": 1405495281000, + "text": "!! is not an operator. It's just the ! operator twice.", + "upvotes": 199, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044b", + "creator": "user2808054", + "createdAt": 1444728232000, + "text": "Put simply: !!vertical gives you a boolean value as to whether 'vertical' is defined or non-false.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044c", + "creator": "phuclv", + "createdAt": 1493458834000, + "text": "What does !!(x) mean in C (esp. the Linux kernel)?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044d", + "creator": "bytefish", + "createdAt": 1544771818000, + "text": "Simplely explain: !!value === Boolean(value)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044e", + "creator": "BillMux", + "createdAt": 1639134899000, + "text": "I like to think of it as an "is truthy" check.", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e9044f", + "creator": "Jone Polvora", + "createdAt": 1648321342000, + "text": "it will just force the variable to have a boolean value. Its useful for assertions and checks.", + "upvotes": 1164, + "upvoterUsernames": [], + "downvotes": 1164, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e90450", + "creator": "Aidin53", + "createdAt": 1657083925000, + "text": "!!!!!!!!!!!!!!!!!!false === false", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 820827, + "uvac": 820863 + } + }, + { + "_id": "62f321bb082fcc3049e8ff02", + "title": "$(document).ready equivalent without jQuery", + "title-lowercase": "$(document).ready equivalent without jquery", + "creator": "FlySwat", + "createdAt": 1240955471000, + "status": "open", + "text": "

I have a script that uses $(document).ready, but it doesn't use anything else from jQuery. I'd like to lighten it up by removing the jQuery dependency.

\n\n

How can I implement my own $(document).ready functionality without using jQuery? I know that using window.onload will not be the same, as window.onload fires after all images, frames, etc. have been loaded.

\n", + "upvotes": 2811, + "upvoterUsernames": [], + "downvotes": 489, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1250118, + "answers": 36, + "answerItems": [ + { + "_id": "62f321cd082fcc3049e90ef4", + "creator": "Chad Grant", + "createdAt": 1240955993000, + "text": "

There is a standards based replacement,DOMContentLoaded that is supported by over 99% of browsers, though not IE8:

\n
document.addEventListener("DOMContentLoaded", function(event) { \n  //do work\n});\n
\n

jQuery's native function is much more complicated than just window.onload, as depicted below.

\n
function bindReady(){\n    if ( readyBound ) return;\n    readyBound = true;\n\n    // Mozilla, Opera and webkit nightlies currently support this event\n    if ( document.addEventListener ) {\n        // Use the handy event callback\n        document.addEventListener( "DOMContentLoaded", function(){\n            document.removeEventListener( "DOMContentLoaded", arguments.callee, false );\n            jQuery.ready();\n        }, false );\n\n    // If IE event model is used\n    } else if ( document.attachEvent ) {\n        // ensure firing before onload,\n        // maybe late but safe also for iframes\n        document.attachEvent("onreadystatechange", function(){\n            if ( document.readyState === "complete" ) {\n                document.detachEvent( "onreadystatechange", arguments.callee );\n                jQuery.ready();\n            }\n        });\n\n        // If IE and not an iframe\n        // continually check to see if the document is ready\n        if ( document.documentElement.doScroll && window == window.top ) (function(){\n            if ( jQuery.isReady ) return;\n\n            try {\n                // If IE is used, use the trick by Diego Perini\n                // http://javascript.nwbox.com/IEContentLoaded/\n                document.documentElement.doScroll("left");\n            } catch( error ) {\n                setTimeout( arguments.callee, 0 );\n                return;\n            }\n\n            // and execute any waiting functions\n            jQuery.ready();\n        })();\n    }\n\n    // A fallback to window.onload, that will always work\n    jQuery.event.add( window, "load", jQuery.ready );\n}\n
\n", + "upvotes": 3044, + "upvoterUsernames": [], + "downvotes": 1293, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32924082fcc3049e92acf", + "creator": "Jared Insel", + "createdAt": 1479315819000, + "text": "DOMContentLoaded will not work if script is loaded afterwards. JQuery document ready executes always.", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ef5", + "creator": "tnyfst", + "createdAt": 1240956124000, + "text": "

The ready function in jQuery does a number of things. Frankly, I don't see that point of replacing it unless you have amazingly small output from your website. jQuery is a pretty tiny library, and it handles all sorts of cross-browser things you'll need later.

\n\n

Anyway, there's little point in posting it here, just open up jQuery and look at the bindReady method.

\n\n

It starts by calling either document.addEventListener(\"DOMContentLoaded\") or document.attachEvent('onreadystatechange') depending on the event model, and goes on from there.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef6", + "creator": "rob", + "createdAt": 1260204409000, + "text": "

Place your <script>/*JavaScript code*/</script> right before the closing </body> tag.

\n\n

Admittedly, this might not suit everyone's purposes since it requires changing the HTML file rather than just doing something in the JavaScript file a la document.ready, but still...

\n", + "upvotes": 152, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef9", + "creator": "Jakob Sternberg", + "createdAt": 1344104022000, + "text": "

Poor man's solution:

\n\n
var checkLoad = function() {   \n    document.readyState !== \"complete\" ? setTimeout(checkLoad, 11) : alert(\"loaded!\");   \n};  \n\ncheckLoad();  \n
\n\n

View Fiddle

\n\n

Added this one, a bit better I guess, own scope, and non recursive

\n\n
(function(){\n    var tId = setInterval(function() {\n        if (document.readyState == \"complete\") onComplete()\n    }, 11);\n    function onComplete(){\n        clearInterval(tId);    \n        alert(\"loaded!\");    \n    };\n})()\n
\n\n

View Fiddle

\n", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef7", + "creator": "James", + "createdAt": 1285118978000, + "text": "

I was recently using this for a mobile site. This is John Resig's simplified version from \"Pro JavaScript Techniques\". It depends on addEvent.

\n\n
var ready = ( function () {\n  function ready( f ) {\n    if( ready.done ) return f();\n\n    if( ready.timer ) {\n      ready.ready.push(f);\n    } else {\n      addEvent( window, \"load\", isDOMReady );\n      ready.ready = [ f ];\n      ready.timer = setInterval(isDOMReady, 13);\n    }\n  };\n\n  function isDOMReady() {\n    if( ready.done ) return false;\n\n    if( document && document.getElementsByTagName && document.getElementById && document.body ) {\n      clearInterval( ready.timer );\n      ready.timer = null;\n      for( var i = 0; i < ready.ready.length; i++ ) {\n        ready.ready[i]();\n      }\n      ready.ready = null;\n      ready.done = true;\n    }\n  }\n\n  return ready;\n})();\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef8", + "creator": "Ben", + "createdAt": 1317208421000, + "text": "

It is worth looking in Rock Solid addEvent() and http://www.braksator.com/how-to-make-your-own-jquery.

\n\n

Here is the code in case the site goes down

\n\n
function addEvent(obj, type, fn) {\n    if (obj.addEventListener) {\n        obj.addEventListener(type, fn, false);\n        EventCache.add(obj, type, fn);\n    }\n    else if (obj.attachEvent) {\n        obj[\"e\"+type+fn] = fn;\n        obj[type+fn] = function() { obj[\"e\"+type+fn]( window.event ); }\n        obj.attachEvent( \"on\"+type, obj[type+fn] );\n        EventCache.add(obj, type, fn);\n    }\n    else {\n        obj[\"on\"+type] = obj[\"e\"+type+fn];\n    }\n}\n\nvar EventCache = function(){\n    var listEvents = [];\n    return {\n        listEvents : listEvents,\n        add : function(node, sEventName, fHandler){\n            listEvents.push(arguments);\n        },\n        flush : function(){\n            var i, item;\n            for(i = listEvents.length - 1; i >= 0; i = i - 1){\n                item = listEvents[i];\n                if(item[0].removeEventListener){\n                    item[0].removeEventListener(item[1], item[2], item[3]);\n                };\n                if(item[1].substring(0, 2) != \"on\"){\n                    item[1] = \"on\" + item[1];\n                };\n                if(item[0].detachEvent){\n                    item[0].detachEvent(item[1], item[2]);\n                };\n                item[0][item[1]] = null;\n            };\n        }\n    };\n}();\n\n// Usage\naddEvent(window, 'unload', EventCache.flush);\naddEvent(window, 'load', function(){alert(\"I'm ready\");});\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32925082fcc3049e92b15", + "creator": "Peter Mortensen", + "createdAt": 1461496960000, + "text": "The second link is broken.", + "upvotes": 457, + "upvoterUsernames": [], + "downvotes": 457, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90efa", + "creator": "mike", + "createdAt": 1345188835000, + "text": "

How about this solution?

\n\n
// other onload attached earlier\nwindow.onload=function() {\n   alert('test');\n};\n\ntmpPreviousFunction=window.onload ? window.onload : null;\n\n// our onload function\nwindow.onload=function() {\n   alert('another message');\n\n   // execute previous one\n   if (tmpPreviousFunction) tmpPreviousFunction();\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32925082fcc3049e92b16", + "creator": "Zaffy", + "createdAt": 1357727798000, + "text": "You could use addEventListener on window with "load". Listeners are executed one after one and dont need manually chaining.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32925082fcc3049e92b18", + "creator": "Teoman shipahi", + "createdAt": 1392851298000, + "text": "this will override rest of the window.onload events on the page and would cause issues. it should add event on top of existing one.", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90efb", + "creator": "Miere", + "createdAt": 1346864218000, + "text": "

The jQuery answer was pretty useful to me. With a little refactory it fitted my needs well.\nI hope it helps anybody else.

\n\n
function onReady ( callback ){\n    var addListener = document.addEventListener || document.attachEvent,\n        removeListener =  document.removeEventListener || document.detachEvent\n        eventName = document.addEventListener ? \"DOMContentLoaded\" : \"onreadystatechange\"\n\n    addListener.call(document, eventName, function(){\n        removeListener( eventName, arguments.callee, false )\n        callback()\n    }, false )\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90efd", + "creator": "Jhankar Mahbub", + "createdAt": 1379025184000, + "text": "

Three options:

\n\n
    \n
  1. If script is the last tag of the body, the DOM would be ready before script tag executes
  2. \n
  3. When the DOM is ready, \"readyState\" will change to \"complete\"
  4. \n
  5. Put everything under 'DOMContentLoaded' event listener
  6. \n
\n\n

onreadystatechange

\n\n
  document.onreadystatechange = function () {\n     if (document.readyState == \"complete\") {\n     // document is ready. Do your stuff here\n   }\n }\n
\n\n

Source: MDN

\n\n

DOMContentLoaded

\n\n
document.addEventListener('DOMContentLoaded', function() {\n   console.log('document is ready. I can sleep now');\n});\n
\n\n

Concerned about stone age browsers:\nGo to the jQuery source code and use the ready function. In that case you are not parsing+executing the whole library you're are doing only a very small part of it.

\n", + "upvotes": 412, + "upvoterUsernames": [], + "downvotes": 193, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90efc", + "creator": "davefrassoni", + "createdAt": 1361620286000, + "text": "

Just add this to the bottom of your HTML page...

\n\n
<script>\n    Your_Function();\n</script>\n
\n\n

Because, HTML documents are parsed by top-bottom.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90efe", + "creator": "Whome", + "createdAt": 1380791573000, + "text": "

This was a good https://stackoverflow.com/a/11810957/185565 poor man's solution. One comment considered a counter to bail out in case of emergency. This is my modification.

\n\n
function doTheMagic(counter) {\n  alert(\"It worked on \" + counter);\n}\n\n// wait for document ready then call handler function\nvar checkLoad = function(counter) {\n  counter++;\n  if (document.readyState != \"complete\" && counter<1000) {\n    var fn = function() { checkLoad(counter); };\n    setTimeout(fn,10);\n  } else doTheMagic(counter);\n};\ncheckLoad(0);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eff", + "creator": "puchu", + "createdAt": 1382907347000, + "text": "

If you want to support Internet Explorer 7+ (no quirks, compatibility and other pain), last Chrome, last Safari, last Firefox and no iframes - this will be enough:

\n\n
is_loaded = false\ncallbacks = []\n\nloaded = ->\n  is_loaded = true\n  for i in [0...callbacks.length]\n    callbacks[i].call document\n  callbacks = []\n\ncontent_loaded = ->\n  document.removeEventListener \"DOMContentLoaded\", content_loaded, true\n  loaded()\n\nstate_changed = ->\n  if document.readyState is \"complete\"\n    document.detachEvent \"onreadystatechange\", state_changed\n    loaded()\n\nif !!document.addEventListener\n  document.addEventListener \"DOMContentLoaded\", content_loaded, true\nelse\n  document.attachEvent \"onreadystatechange\", state_changed\n\ndom_ready = (callback) ->\n  if is_loaded\n    callback.call document\n  else\n    callbacks.push callback\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32925082fcc3049e92b1f", + "creator": "Richard J. Ross III", + "createdAt": 1390223259000, + "text": "That most definitely isn't javascript.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32925082fcc3049e92b21", + "creator": "Adrian", + "createdAt": 1390325536000, + "text": "Looks like someone writes CoffeeScript.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f01", + "creator": "Dustin Davis", + "createdAt": 1387826072000, + "text": "

I use this:

\n\n
document.addEventListener(\"DOMContentLoaded\", function(event) { \n    //Do work\n});\n
\n\n

Note: This probably only works with newer browsers, especially these: http://caniuse.com/#feat=domcontentloaded

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f00", + "creator": "Pawel", + "createdAt": 1384811931000, + "text": "

Cross-browser (old browsers too) and a simple solution:

\n\n
var docLoaded = setInterval(function () {\n    if(document.readyState !== \"complete\") return;\n    clearInterval(docLoaded);\n\n    /*\n        Your code goes here i.e. init()\n    */\n}, 30);\n
\n\n

Showing alert in jsfiddle

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f02", + "creator": "Matt Pileggi", + "createdAt": 1393003019000, + "text": "

If you are loading jQuery near the bottom of BODY, but are having trouble with code that writes out jQuery(<func>) or jQuery(document).ready(<func>), check out jqShim on Github.

\n\n

Rather than recreate its own document ready function, it simply holds onto the functions until jQuery is available then proceeds with jQuery as expected. The point of moving jQuery to the bottom of body is to speed up page load, and you can still accomplish it by inlining the jqShim.min.js in the head of your template.

\n\n

I ended up writing this code to make moving all the scripts in WordPress to the footer, and just this shim code now sits directly in the header.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f03", + "creator": "Forestrf", + "createdAt": 1401207776000, + "text": "

Edit of the edit of @duskwuff to support Internet Explorer 8 too. The difference is a new call to the function test of the regex and the setTimeout with an anonymous function.

\n\n

Also, I set the timeout to 99.

\n\n
function ready(f){/in/.test(document.readyState)?setTimeout(function(){ready(f);},99):f();}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f04", + "creator": "malko", + "createdAt": 1406639284000, + "text": "

We found a quick-and-dirty cross browser implementation of ours that may do the trick for most simple cases with a minimal implementation:

\n\n
window.onReady = function onReady(fn){\n    document.body ? fn() : setTimeout(function(){ onReady(fn);},50);\n};\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32926082fcc3049e92b27", + "creator": "Nabi K.A.Z.", + "createdAt": 1457563835000, + "text": "what's doc.body !?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f05", + "creator": "Diego Perini", + "createdAt": 1407236669000, + "text": "

The setTimeout/setInterval solutions presented here will only work in specific circumstances.

\n\n

The problem shows up especially in older Internet Explorer versions up to 8.

\n\n

The variables affecting the success of these setTimeout/setInterval solutions are:

\n\n
1) dynamic or static HTML\n2) cached or non cached requests\n3) size of the complete HTML document\n4) chunked or non chunked transfer encoding\n
\n\n

the original (native Javascript) code solving this specific issue is here:

\n\n
https://github.com/dperini/ContentLoaded\nhttp://javascript.nwbox.com/ContentLoaded (test)\n
\n\n

this is the code from which the jQuery team have built their implementation.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f07", + "creator": "Max Heiber", + "createdAt": 1419699162000, + "text": "

This cross-browser code will call a function once the DOM is ready:

\n\n
var domReady=function(func){\n    var scriptText='('+func+')();';\n    var scriptElement=document.createElement('script');\n    scriptElement.innerText=scriptText;\n    document.body.appendChild(scriptElement);\n};\n
\n\n

Here's how it works:

\n\n
    \n
  1. The first line of domReady calls the toString method of the function to get a string representation of the function you pass in and wraps it in an expression that immediately calls the function.
  2. \n
  3. The rest of domReady creates a script element with the expression and appends it to the body of the document.
  4. \n
  5. The browser runs script tags appended to body after the DOM is ready.
  6. \n
\n\n

For example, if you do this: domReady(function(){alert();});, the following will appended to the body element:

\n\n
 <script>(function (){alert();})();</script>\n
\n\n

Note that this works only for user-defined functions. The following won't work: domReady(alert);

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f06", + "creator": "Dan", + "createdAt": 1415346318000, + "text": "

Really, if you care about Internet Explorer 9+ only, this code would be enough to replace jQuery.ready:

\n\n
    document.addEventListener(\"DOMContentLoaded\", callback);\n
\n\n
\n\n

If you worry about Internet Explorer 6 and some really strange and rare browsers, this will work:

\n\n
domReady: function (callback) {\n    // Mozilla, Opera and WebKit\n    if (document.addEventListener) {\n        document.addEventListener(\"DOMContentLoaded\", callback, false);\n        // If Internet Explorer, the event model is used\n    } else if (document.attachEvent) {\n        document.attachEvent(\"onreadystatechange\", function() {\n            if (document.readyState === \"complete\" ) {\n                callback();\n            }\n        });\n        // A fallback to window.onload, that will always work\n    } else {\n        var oldOnload = window.onload;\n        window.onload = function () {\n            oldOnload && oldOnload();\n            callback();\n        }\n    }\n},\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f08", + "creator": "chugadie", + "createdAt": 1424096141000, + "text": "

This question was asked quite a long time ago. For anyone just seeing this question, there is now a site called \"you might not need jquery\" which breaks down - by level of IE support required - all the functionality of jquery and provides some alternative, smaller libraries.

\n\n

IE8 document ready script according to you might not need jquery

\n\n
function ready(fn) {\n    if (document.readyState != 'loading')\n        fn();\n    else if (document.addEventListener)\n        document.addEventListener('DOMContentLoaded', fn);\n    else\n        document.attachEvent('onreadystatechange', function() {\n            if (document.readyState != 'loading')\n                fn();\n        });\n}\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32926082fcc3049e92b2b", + "creator": "Luke", + "createdAt": 1444771869000, + "text": "I wonder why the 'onreadystatechange' is necessary rather than document.attachEvent('onload', fn);", + "upvotes": 497, + "upvoterUsernames": [], + "downvotes": 497, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f09", + "creator": "Antara Roy", + "createdAt": 1432275616000, + "text": "

Here is the smallest code snippet to test DOM ready which works across all browsers (even IE 8):

\n\n
r(function(){\n    alert('DOM Ready!');\n});\nfunction r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}\n
\n\n

See this answer.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0b", + "creator": "Joaquinglezsantos", + "createdAt": 1454512832000, + "text": "

For IE9+:

\n\n
function ready(fn) {\n  if (document.readyState != 'loading'){\n    fn();\n  } else {\n    document.addEventListener('DOMContentLoaded', fn);\n  }\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0a", + "creator": "Manan Sheth", + "createdAt": 1452616538000, + "text": "

In short, instead of the $(document).ready() used in jQuery, we can use a JavaScript method:

\n\n
<script>\n    document.addEventListener(\"DOMContentLoaded\", function_name, false);\n    function function_name(){\n        statements;\n    }\n</script>\n
\n\n

Thus, when the page is ready i.e. DOMContentLoaded only then the function function_name() will be invoked.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0d", + "creator": "Olemak", + "createdAt": 1467899203000, + "text": "

Here's what I use, it's fast and covers all bases I think; works for everything except IE<9.

\n\n
(() => { function fn() {\n    // \"On document ready\" commands:\n    console.log(document.readyState);\n};  \n  if (document.readyState != 'loading') {fn()}\n  else {document.addEventListener('DOMContentLoaded', fn)}\n})();\n
\n\n

This seems to catch all cases:

\n\n\n\n

The DOMContentLoaded event is available in IE9 and everything else, so I personally think it's OK to use this. Rewrite the arrow function declaration to a regular anonymous function if you're not transpiling your code from ES2015 to ES5.

\n\n

If you want to wait until all assets are loaded, all images displayed etc then use window.onload instead.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0c", + "creator": "Vatsal", + "createdAt": 1456511907000, + "text": "

It's always good to use JavaScript equivalents as compared to jQuery. One reason is one fewer library to depend on and they are much faster than the jQuery equivalents.

\n\n

One fantastic reference for jQuery equivalents is http://youmightnotneedjquery.com/.

\n\n

As far as your question is concerned, I took the below code from the above link :)\nOnly caveat is it only works with Internet Explorer 9 and later.

\n\n
function ready(fn) {\n    if (document.readyState != 'loading') {\n        fn();\n    }\n    else {\n        document.addEventListener('DOMContentLoaded', fn);\n    }\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0e", + "creator": "user4617883", + "createdAt": 1508651986000, + "text": "

If you don't have to support very old browsers, here is a way to do it even when your external script is loaded with async attribute:

\n\n
HTMLDocument.prototype.ready = new Promise(function(resolve) {\n   if(document.readyState != \"loading\")\n      resolve();\n   else\n      document.addEventListener(\"DOMContentLoaded\", function() {\n         resolve();\n      });\n});\n\ndocument.ready.then(function() {\n   console.log(\"document.ready\");\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f0f", + "creator": "Dexygen", + "createdAt": 1508895779000, + "text": "

I simply use:

\n\n
setTimeout(function(){\n    //reference/manipulate DOM here\n});\n
\n\n

And unlike document.addEventListener(\"DOMContentLoaded\" //etc as in the very top answer, it works as far back as IE9 -- http://caniuse.com/#search=DOMContentLoaded only indicates as recently as IE11.

\n\n

Interestingly I stumbled upon this setTimeout solution in 2009: Is checking for the readiness of the DOM overkill?, which probably could have been worded slightly better, as I meant \"is it overkill to use various frameworks' more complicated approaches to check for the readiness of the DOM\".

\n\n

My best explanation for why this technique works is that, when the script with such a setTimeout has been reached, the DOM is in the middle of being parsed, so execution of the code within the setTimeout gets deferred until that operation is finished.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f10", + "creator": "user8903269", + "createdAt": 1527437484000, + "text": "

Try this:

\n\n
function ready(callback){\n    if(typeof callback === \"function\"){\n        document.addEventListener(\"DOMContentLoaded\", callback);\n        window.addEventListener(\"load\", callback);\n    }else{\n        throw new Error(\"Sorry, I can not run this!\");\n    }\n}\nready(function(){\n    console.log(\"It worked!\");\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32927082fcc3049e92b30", + "creator": "Andrew", + "createdAt": 1585765454000, + "text": "Lol you're gonna run the callback twice", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f11", + "creator": "Dustin Poissant", + "createdAt": 1543354842000, + "text": "
(function(f){\n  if(document.readyState != \"loading\") f();\n  else document.addEventListener(\"DOMContentLoaded\", f);\n})(function(){\n  console.log(\"The Document is ready\");\n});\n
\n", + "upvotes": 416, + "upvoterUsernames": [], + "downvotes": 416, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32927082fcc3049e92b31", + "creator": "dwjohnston", + "createdAt": 1543366614000, + "text": "What does this add that the other answers do not?", + "upvotes": 929, + "upvoterUsernames": [], + "downvotes": 929, + "downvoterUsernames": [] + }, + { + "_id": "62f32927082fcc3049e92b33", + "creator": "Dustin Poissant", + "createdAt": 1543420612000, + "text": "It also works even after the DOM has already loaded (like jQuery.ready does), which most of these answers fail to do.", + "upvotes": 188, + "upvoterUsernames": [], + "downvotes": 188, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90f12", + "creator": "Null", + "createdAt": 1555065161000, + "text": "

Most vanilla JS Ready functions do NOT consider the scenario where the DOMContentLoaded handler is set after the document is already loaded - Which means the function will never run. This can happen if you look for DOMContentLoaded within an async external script (<script async src=\"file.js\"></script>).

\n\n

The code below checks for DOMContentLoaded only if the document's readyState isn't already interactive or complete.

\n\n
var DOMReady = function(callback) {\n  document.readyState === \"interactive\" || document.readyState === \"complete\" ? callback() : document.addEventListener(\"DOMContentLoaded\", callback());\n};\nDOMReady(function() {\n  //DOM ready!\n});\n
\n\n

If you want to support IE aswell:

\n\n
var DOMReady = function(callback) {\n    if (document.readyState === \"interactive\" || document.readyState === \"complete\") {\n        callback();\n    } else if (document.addEventListener) {\n        document.addEventListener('DOMContentLoaded', callback());\n    } else if (document.attachEvent) {\n        document.attachEvent('onreadystatechange', function() {\n            if (document.readyState != 'loading') {\n                callback();\n            }\n        });\n    }\n};\n\nDOMReady(function() {\n  // DOM ready!\n});\n
\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f13", + "creator": "Shivam Sharma", + "createdAt": 1569825717000, + "text": "

Most minimal and 100% working

\n\n

I have picked the answer from PlainJS and it's working fine for me. It extends DOMContentLoaded so that it can be accepted at all the browsers.

\n\n
\n\n

This function is the equivalent of jQuery's $(document).ready() method:

\n\n
document.addEventListener('DOMContentLoaded', function(){\n    // do something\n});\n
\n\n

However, in contrast to jQuery, this code will only run properly in modern browsers (IE > 8) and it won't in case the document is already rendered at the time this script gets inserted (e.g. via Ajax). Therefore, we need to extend this a little bit:

\n\n
function run() {\n    // do something\n}\n\n// in case the document is already rendered\nif (document.readyState!='loading') run();\n// modern browsers\nelse if (document.addEventListener) \ndocument.addEventListener('DOMContentLoaded', run);\n// IE <= 8\nelse document.attachEvent('onreadystatechange', function(){\n    if (document.readyState=='complete') run();\n});\n
\n\n

This covers basically all possibilities and is a viable replacement for the jQuery helper.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f14", + "creator": "Mikser", + "createdAt": 1579818611000, + "text": "

It is year 2020 and <script> tag has defer attribute.

\n\n

for example:

\n\n
<script src=\"demo_defer.js\" defer></script>\n
\n\n

it specifies that the script is executed when the page has finished parsing.

\n\n

https://www.w3schools.com/tags/att_script_defer.asp

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f15", + "creator": "ceving", + "createdAt": 1605981433000, + "text": "

Nowadays you should use modules. Put your code into the default function of a module and import the function into a script element.

\n

client.js:

\n
export default function ()\n{\n  alert ("test");\n}\n
\n

index.html:

\n
<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset="UTF-8">\n    <title>test</title>\n  </head>\n  <body>\n    <script type="module">\n      import main from './client.js';\n      main ();\n    </script>\n  </body>\n</html>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f17", + "creator": "Chamara Indrajith", + "createdAt": 1641010732000, + "text": "

Simplest way using pure JavaScript. Without jQuery:

\n
document.addEventListener("DOMContentLoaded", function(event) {\n   // Your code to run since DOM is loaded and ready\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90f16", + "creator": "Greg", + "createdAt": 1639687169000, + "text": "

2022 version

\n

In 2022, all you need to do is put the defer attribute on your script, and load it in the head!

\n

Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer

\n
<!doctype html>\n<html>\n<head>\n  <script src="/script.js" defer></script>\n</head>\n<body>\n\n <p>In 2022, all you need to do is put the defer attribute on your script, and load it in the head!</p>\n\n</body>\n</html>\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32928082fcc3049e92b38", + "creator": "Chamara Indrajith", + "createdAt": 1641010807000, + "text": "Could you please share a reference for this?", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b39", + "creator": "Greg", + "createdAt": 1641306309000, + "text": "@ChamaraIndrajith sure - added link to MDN", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32928082fcc3049e92b3a", + "creator": "DexieTheSheep", + "createdAt": 1652462393000, + "text": "By far the best answer, but keep in mind that it makes you download the script in parallel with the page (doesn't matter for most people though)", + "upvotes": 1365, + "upvoterUsernames": [], + "downvotes": 1365, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321cd082fcc3049e90ece", + "creator": "Joel Mueller", + "createdAt": 1244057974000, + "text": "...and also definitely not the same functionality.", + "upvotes": 433, + "upvoterUsernames": [], + "downvotes": 132, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1252930, + "uvac": 1252966 + } + }, + { + "_id": "62f321bb082fcc3049e8fed3", + "title": "Creating multiline strings in JavaScript", + "title-lowercase": "creating multiline strings in javascript", + "creator": "Newy", + "createdAt": 1241057515000, + "status": "open", + "text": "

I have the following code in Ruby. I want to convert this code into JavaScript. What is the equivalent code in JS?

\n
text = <<"HERE"\nThis\nIs\nA\nMultiline\nString\nHERE\n
\n", + "upvotes": 5801, + "upvoterUsernames": [], + "downvotes": 2568, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2105139, + "answers": 43, + "answerItems": [ + { + "_id": "62f321c3082fcc3049e90706", + "creator": "alex", + "createdAt": 1241057629000, + "text": "

You can do this...

\n\n
var string = 'This is\\n' +\n'a multiline\\n' + \n'string';\n
\n", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d0a", + "creator": "Paul Sweatte", + "createdAt": 1358564087000, + "text": "e4x.js would be the good future-proof solution", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 248, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90707", + "creator": "Anonymous", + "createdAt": 1241057720000, + "text": "

Update:

\n\n

ECMAScript 6 (ES6) introduces a new type of literal, namely template literals. They have many features, variable interpolation among others, but most importantly for this question, they can be multiline.

\n\n

A template literal is delimited by backticks:

\n\n
var html = `\n  <div>\n    <span>Some HTML here</span>\n  </div>\n`;\n
\n\n

(Note: I'm not advocating to use HTML in strings)

\n\n

Browser support is OK, but you can use transpilers to be more compatible.

\n\n
\n\n

Original ES5 answer:

\n\n

Javascript doesn't have a here-document syntax. You can escape the literal newline, however, which comes close:

\n\n
\"foo \\\nbar\"\n
\n", + "upvotes": 7926, + "upvoterUsernames": [], + "downvotes": 3714, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d0c", + "creator": "staticsan", + "createdAt": 1241058178000, + "text": "Be warned: some browsers will insert newlines at the continuance, some will not.", + "upvotes": 539, + "upvoterUsernames": [], + "downvotes": 268, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d0e", + "creator": "jcollum", + "createdAt": 1303077538000, + "text": "Visual Studio 2010 seems to be confused by this syntax as well.", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d0f", + "creator": "Juan Mendes", + "createdAt": 1409945888000, + "text": "@some It's not a multiline string, it's a one line string split over multiple lines of code that make up a single statement", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d10", + "creator": "Zane", + "createdAt": 1414329922000, + "text": "This solution is not wrong - but it's obviously not the best. So sad it has such a high count here. Scroll down to read better solutions.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d12", + "creator": "IcedDante", + "createdAt": 1469636519000, + "text": "AFAIK, IE still does not support it", + "upvotes": 762, + "upvoterUsernames": [], + "downvotes": 762, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d14", + "creator": "user1493948", + "createdAt": 1495625297000, + "text": "Thank you so much to the person who provided this answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d16", + "creator": "Tom Andraszek", + "createdAt": 1495689518000, + "text": ""Browser support is OK"... not supported by IE11 - not OK", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d18", + "creator": "Rishabh Agrahari", + "createdAt": 1505409737000, + "text": "I have tested template literal on safari 7.1 Mavericks, I doesn't work.", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 312, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d1a", + "creator": "César León", + "createdAt": 1514566549000, + "text": "It Fails for IE", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d1c", + "creator": "Iharob Al Asimi", + "createdAt": 1541348275000, + "text": "This is the kind of answer that makes me LOVE SO.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d1e", + "creator": "RayLoveless", + "createdAt": 1572547170000, + "text": "WARNING: Not supported by IE. :(", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d20", + "creator": "Clonkex", + "createdAt": 1646347564000, + "text": "@sotn He probably meant "ok" as in "acceptable but not great".", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90708", + "creator": "KooiInc", + "createdAt": 1241076149000, + "text": "

the pattern text = <<\"HERE\" This Is A Multiline String HERE is not available in js (I remember using it much in my good old Perl days).

\n\n

To keep oversight with complex or long multiline strings I sometimes use an array pattern:

\n\n
var myString = \n   ['<div id=\"someId\">',\n    'some content<br />',\n    '<a href=\"#someRef\">someRefTxt</a>',\n    '</div>'\n   ].join('\\n');\n
\n\n

or the pattern anonymous already showed (escape newline), which can be an ugly block in your code:

\n\n
    var myString = \n       '<div id=\"someId\"> \\\nsome content<br /> \\\n<a href=\"#someRef\">someRefTxt</a> \\\n</div>';\n
\n\n

Here's another weird but working 'trick'1:

\n\n
var myString = (function () {/*\n   <div id=\"someId\">\n     some content<br />\n     <a href=\"#someRef\">someRefTxt</a>\n    </div>        \n*/}).toString().match(/[^]*\\/\\*([^]*)\\*\\/\\}$/)[1];\n
\n\n

external edit: jsfiddle

\n\n

ES20xx supports spanning strings over multiple lines using template strings:

\n\n
let str = `This is a text\n    with multiple lines.\n    Escapes are interpreted,\n    \\n is a newline.`;\nlet str = String.raw`This is a text\n    with multiple lines.\n    Escapes are not interpreted,\n    \\n is not a newline.`;\n
\n\n

1 Note: this will be lost after minifying/obfuscating your code

\n", + "upvotes": 1068, + "upvoterUsernames": [], + "downvotes": 338, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d22", + "creator": "BMiner", + "createdAt": 1310906352000, + "text": "Please don't use the array pattern. It will be slower than plain-old string concatenation in most cases.", + "upvotes": 69, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d24", + "creator": "KooiInc", + "createdAt": 1319019009000, + "text": "@RoyTinker: feel free to cook up a jsperf.com-test yourself, or fiddle around with my test ;)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d26", + "creator": "RobKohr", + "createdAt": 1321961191000, + "text": "This is my favorite way (it looks nice), and fast enough that I am willing to take a minor speed hit for code readability.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d28", + "creator": "Lodewijk", + "createdAt": 1326066949000, + "text": "You could make a little function to do fast joins: function fastjoin(array){ out = ''; for(i in array){ out += i; } return out; }", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d2a", + "creator": "Lodewijk", + "createdAt": 1326070720000, + "text": "That's actually quite a bunch slower than those join's. Plus the join's are kind of the preferable way to do it. Joins it is.", + "upvotes": 336, + "upvoterUsernames": [], + "downvotes": 336, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d2b", + "creator": "Rimian", + "createdAt": 1336970528000, + "text": "I like joins. Write the code that writes the code. Much better than concatenating many times. It's redundancy.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d2c", + "creator": "Pavel", + "createdAt": 1337580374000, + "text": "+1 for an elegant alternative that not only works the same way in all browsers, but is also future-proof.", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d2e", + "creator": "Cody", + "createdAt": 1357802957000, + "text": "Must encase with 'singles' and NOT "doubles" for me in Notepad++", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d2f", + "creator": "Michael Mior", + "createdAt": 1373551461000, + "text": "I personally just concatenate strings and let the performance issues get sorted out when the JS is minified and the strings are combined.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d31", + "creator": "user2418182", + "createdAt": 1395761241000, + "text": "@BMiner: 1) "Premature optimization is the root of all evil" - Donald Knuth, and 2) 'readability' is in the eye of the beholder", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d33", + "creator": "nevelis", + "createdAt": 1431033453000, + "text": "Tested on Win 8.1 in Firefox & Chrome, join is 33% slower than concatenate.", + "upvotes": 3338, + "upvoterUsernames": [], + "downvotes": 3338, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d35", + "creator": "Florian Wendelborn", + "createdAt": 1432733333000, + "text": "Tested on Win 8.1 in Chrome and Firefox, join is 99% slower for me. (1,997,886,948 vs 18,679,979 respectively 2,161,096,056 vs 2,145,283)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d37", + "creator": "Wolfie Inu", + "createdAt": 1445934930000, + "text": "Unless this is deep inside a million nested loops and running on a potato, efficiency is probably irrelevant next to readability. +1 for the array", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d39", + "creator": "AturSams", + "createdAt": 1492000265000, + "text": "Why would string concatenation be much faster than join? Is there some sort of magic in join's implementation?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d3b", + "creator": "KooiInc", + "createdAt": 1492002370000, + "text": "@zehelvion not anymore. This is a very old issue (2009), browsers are much better nowadays", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 655, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d3c", + "creator": "Pacerier", + "createdAt": 1499585829000, + "text": "@KooiInc, What about concatenation within the multiline string?", + "upvotes": 992, + "upvoterUsernames": [], + "downvotes": 992, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d3d", + "creator": "KooiInc", + "createdAt": 1535093869000, + "text": "@Seblor why not? The sub header was not informative.", + "upvotes": 2990, + "upvoterUsernames": [], + "downvotes": 2990, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d3e", + "creator": "Seblor", + "createdAt": 1535094855000, + "text": "AFAIK, Template strings (or template literals) were not supported until ES2015 (ES6), so saying that any ES20XX version do seems wrong.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d40", + "creator": "Seblor", + "createdAt": 1535096525000, + "text": "Oh I get my confusion there. The ECMAScript versions started beeing named "ES20XX" after ES6, I forgot about that.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90709", + "creator": "stillatmycomputer", + "createdAt": 1290530789000, + "text": "

This works in IE, Safari, Chrome and Firefox:

\n\n
<script type=\"text/javascript\" src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js\"></script>\n<div class=\"crazy_idea\" thorn_in_my_side='<table  border=\"0\">\n                        <tr>\n                            <td ><span class=\"mlayouttablecellsdynamic\">PACKAGE price $65.00</span></td>\n                        </tr>\n                    </table>'></div>\n<script type=\"text/javascript\">\n    alert($(\".crazy_idea\").attr(\"thorn_in_my_side\"));\n</script>\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d45", + "creator": "Sk8erPeter", + "createdAt": 1330048504000, + "text": "Just think about it. Do you think it's valid? Don't you think it can cause display problems?", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d46", + "creator": "dotancohen", + "createdAt": 1330482763000, + "text": "Why the downvotes? This is a creative answer, if not very practical!", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d48", + "creator": "DCShannon", + "createdAt": 1426290524000, + "text": "I hope no one ever uses this answer in practice, but it's a neat idea", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d49", + "creator": "borracciaBlu", + "createdAt": 1608265558000, + "text": "Edge case when you have ' within the html. in that case you may have to use html entities '.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070a", + "creator": "Jordão", + "createdAt": 1302113795000, + "text": "

You can have multiline strings in pure JavaScript.

\n

This method is based on the serialization of functions, which is defined to be implementation-dependent. It does work in the most browsers (see below), but there's no guarantee that it will still work in the future, so do not rely on it.

\n

Using the following function:

\n
function hereDoc(f) {\n  return f.toString().\n      replace(/^[^\\/]+\\/\\*!?/, '').\n      replace(/\\*\\/[^\\/]+$/, '');\n}\n
\n

You can have here-documents like this:

\n
var tennysonQuote = hereDoc(function() {/*!\n  Theirs not to make reply,\n  Theirs not to reason why,\n  Theirs but to do and die\n*/});\n
\n

The method has successfully been tested in the following browsers (not mentioned = not tested):

\n\n

Be careful with your minifier, though. It tends to remove comments. For the YUI compressor, a comment starting with /*! (like the one I used) will be preserved.

\n

I think a real solution would be to use CoffeeScript.

\n

ES6 UPDATE: You could use backtick instead of creating a function with a comment and running toString on the comment. The regex would need to be updated to only strip spaces. You could also have a string prototype method for doing this:

\n
let foo = `\n  bar loves cake\n  baz loves beer\n  beer loves people\n`.removeIndentation()\n
\n

Someone should write this .removeIndentation string method... ;)

\n", + "upvotes": 427, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d4c", + "creator": "fforw", + "createdAt": 1308325749000, + "text": "What!? creating and decompiling a Function to hack a multiline comment into being a multiline string? Now that's ugly.", + "upvotes": 529, + "upvoterUsernames": [], + "downvotes": 252, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d4e", + "creator": "Jordão", + "createdAt": 1324772198000, + "text": "@BrockAdams: thanks for the information; this is exactly why this approach is an unreliable hack.", + "upvotes": 1869, + "upvoterUsernames": [], + "downvotes": 1869, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d50", + "creator": "Jason", + "createdAt": 1342156993000, + "text": "Extremely handy. I'm using it for (Jasmine) unit tests, but avoiding it for production code.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d52", + "creator": "mplungjan", + "createdAt": 1361094853000, + "text": "My Heredoc works in my Fx and Chrome on Mac: DEMO", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d53", + "creator": "Korjavin Ivan", + "createdAt": 1397666538000, + "text": "Its was failed when I have </script> literal inside the comments.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d55", + "creator": "Eugene Mala", + "createdAt": 1445698590000, + "text": "Syntax become ugly when you need to pass variables inside long multi-line text. Or you have to replace strings several times.", + "upvotes": 200, + "upvoterUsernames": [], + "downvotes": 200, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d56", + "creator": "Prakash GPz", + "createdAt": 1480156898000, + "text": "Even simpler hereDoc(() => {/*! Theirs not to make reply, Theirs not to reason why, Theirs but to do and die */});", + "upvotes": 1242, + "upvoterUsernames": [], + "downvotes": 1242, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d58", + "creator": "Pacerier", + "createdAt": 1497722390000, + "text": "@fforw, It's pretty neat actually, if only its implementation independent.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d59", + "creator": "Jordão", + "createdAt": 1508110275000, + "text": "@JamesWilkins: indeed you don't. In fact, you don't even should use this solution in the first place", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d5a", + "creator": "James Wilkins", + "createdAt": 1508121428000, + "text": "I know, just making the answer a bit more complete for new people finding it. ;)", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d5c", + "creator": "foobored", + "createdAt": 1587526150000, + "text": "@Jordão On the matter of the .removeIndentation() method how about .replace(/^\\s+(\\S.*)/gm, "$1")", + "upvotes": 1716, + "upvoterUsernames": [], + "downvotes": 1716, + "downvoterUsernames": [] + }, + { + "_id": "62f32596082fcc3049e91d5e", + "creator": "Bergi", + "createdAt": 1659297561000, + "text": "How does one escape the */ comment delimiter if it were to occur inside of the string?", + "upvotes": 688, + "upvoterUsernames": [], + "downvotes": 688, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070b", + "creator": "Tyler", + "createdAt": 1305897033000, + "text": "

to sum up, I have tried 2 approaches listed here in user javascript programming (Opera 11.01):

\n\n\n\n

So I recommend the working approach for Opera user JS users. Unlike what the author was saying:

\n\n
\n

It doesn't work on firefox or opera; only on IE, chrome and safari.

\n
\n\n

It DOES work in Opera 11. At least in user JS scripts. Too bad I can't comment on individual answers or upvote the answer, I'd do it immediately. If possible, someone with higher privileges please do it for me.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d5f", + "creator": "Tyler", + "createdAt": 1346380906000, + "text": "Thanks to everyone who actually upvoted this answer: I have now enough privileges to post normal comments! So thanks again.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070c", + "creator": "Devin Rhode", + "createdAt": 1307327445000, + "text": "

ES6 Update:

\n\n

As the first answer mentions, with ES6/Babel, you can now create multi-line strings simply by using backticks:

\n\n
const htmlString = `Say hello to \nmulti-line\nstrings!`;\n
\n\n

Interpolating variables is a popular new feature that comes with back-tick delimited strings:

\n\n
const htmlString = `${user.name} liked your post about strings`;\n
\n\n

This just transpiles down to concatenation:

\n\n
user.name + ' liked your post about strings'\n
\n\n

Original ES5 answer:

\n\n
\n

Google's JavaScript style guide recommends to use string concatenation instead of escaping newlines:

\n \n

Do not do this:

\n\n
var myString = 'A rather long string of English text, an error message \\\n                actually that just keeps going and going -- an error \\\n                message to make the Energizer bunny blush (right through \\\n                those Schwarzenegger shades)! Where was I? Oh yes, \\\n                you\\'ve got an error and all the extraneous whitespace is \\\n                just gravy.  Have a nice day.';\n
\n \n

The whitespace at the beginning of each line can't be safely stripped at compile time; whitespace after the slash will result in tricky errors; and while most script engines support this, it is not part of ECMAScript.

\n \n

Use string concatenation instead:

\n\n
var myString = 'A rather long string of English text, an error message ' +\n               'actually that just keeps going and going -- an error ' +\n               'message to make the Energizer bunny blush (right through ' +\n               'those Schwarzenegger shades)! Where was I? Oh yes, ' +\n               'you\\'ve got an error and all the extraneous whitespace is ' +\n               'just gravy.  Have a nice day.';\n
\n
\n", + "upvotes": 1703, + "upvoterUsernames": [], + "downvotes": 209, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d61", + "creator": "Inuart", + "createdAt": 1353373748000, + "text": "this doesn't work for me in canary chrome for windows even after enabling Experimental JavaScript", + "upvotes": 7460, + "upvoterUsernames": [], + "downvotes": 7460, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070d", + "creator": "semente", + "createdAt": 1323806950000, + "text": "

I like this syntax and indendation:

\n\n
string = 'my long string...\\n'\n       + 'continue here\\n'\n       + 'and here.';\n
\n\n

(but actually can't be considered as multiline string)

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d64", + "creator": "moliad", + "createdAt": 1386862697000, + "text": "putting the + at the beginning allows one to comment out that line without having to edit other lines when its the first/last line of the sequence.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d66", + "creator": "Daniel Sokolowski", + "createdAt": 1399477249000, + "text": "I prefer the + at the front too as visually I do not need to scan to the end of the line to know the next one is a continuation.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070e", + "creator": "Peter V. Mørch", + "createdAt": 1325620311000, + "text": "

I'm surprised I didn't see this, because it works everywhere I've tested it and is very useful for e.g. templates:

\n\n
<script type=\"bogus\" id=\"multi\">\n    My\n    multiline\n    string\n</script>\n<script>\n    alert($('#multi').html());\n</script>\n
\n\n

Does anybody know of an environment where there is HTML but it doesn't work?

\n", + "upvotes": 178, + "upvoterUsernames": [], + "downvotes": 84, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d68", + "creator": "Lodewijk", + "createdAt": 1326071558000, + "text": "Anywhere you don't want to put your strings into seperate and distant script elements.", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d69", + "creator": "Peter V. Mørch", + "createdAt": 1328259811000, + "text": "A valid objection! It isn't perfect. But for templates, that separation is not only ok, but perhaps even encouraged.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d6b", + "creator": "CpILL", + "createdAt": 1337676885000, + "text": "actually, this is HTML not Javascript :-/", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d6d", + "creator": "Davi Fiamenghi", + "createdAt": 1375220471000, + "text": "however, the task of obtaining a multiline string in javascript can be done this way", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d6f", + "creator": "Juan Mendes", + "createdAt": 1375603485000, + "text": "@Peter, you can also make an AJAX request for a text file with newlines in it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d70", + "creator": "hostingutilities.com", + "createdAt": 1529515683000, + "text": "But what happens when someone is using a JavaScript minifier?", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d72", + "creator": "Peter V. Mørch", + "createdAt": 1529818342000, + "text": "I don't think e.g. webpack will ever look at <script> tags embedded in HTML.", + "upvotes": 1152, + "upvoterUsernames": [], + "downvotes": 1152, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d73", + "creator": "Ryan Marshall", + "createdAt": 1543457736000, + "text": "Love this solution. Very clean.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9070f", + "creator": "Tom Beech", + "createdAt": 1345213553000, + "text": "

I solved this by outputting a div, making it hidden, and calling the div id by jQuery when I needed it.

\n\n

e.g.

\n\n
<div id=\"UniqueID\" style=\"display:none;\">\n     Strings\n     On\n     Multiple\n     Lines\n     Here\n</div>\n
\n\n

Then when I need to get the string, I just use the following jQuery:

\n\n
$('#UniqueID').html();\n
\n\n

Which returns my text on multiple lines. If I call

\n\n
alert($('#UniqueID').html());\n
\n\n

I get:

\n\n

\"enter

\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d75", + "creator": "beginner_", + "createdAt": 1375790813000, + "text": "This was the only method that actually worked for me to create a multi-line javascript string variable from a Java String.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d77", + "creator": "Venkat", + "createdAt": 1379953461000, + "text": "this gets the work done for me, although it doesnot solve multiline string values assignment to javascript variables", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d79", + "creator": "Dan Dascalescu", + "createdAt": 1390552777000, + "text": "What if the string is HTML?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d7a", + "creator": "mplungjan", + "createdAt": 1390555692000, + "text": "$('#UniqueID').content()", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d7b", + "creator": "Gavin", + "createdAt": 1500308475000, + "text": "Just be warned that search engines do still index content in hidden DIVs.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d7d", + "creator": "Pacerier", + "createdAt": 1502057993000, + "text": "@Gavin, Are u sure about that? Especially that we have already set it to display:none?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90710", + "creator": "jpfreire", + "createdAt": 1345746645000, + "text": "

Using script tags:

\n\n\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d7f", + "creator": "xdhmoore", + "createdAt": 1420819027000, + "text": "I think this strategy is clean & far underused. jsrender uses this.", + "upvotes": 1741, + "upvoterUsernames": [], + "downvotes": 1741, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d81", + "creator": "David Nouls", + "createdAt": 1437036787000, + "text": "I'm using this with innerText iso innerHTML, But how do I make sure that the whitespaces are preserved ?", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90712", + "creator": "Aditya Hajare", + "createdAt": 1359375646000, + "text": "

I think this workaround should work in IE, Chrome, Firefox, Safari, Opera -

\n\n

Using jQuery :

\n\n
<xmp id=\"unique_id\" style=\"display:none;\">\n  Some plain text\n  Both type of quotes :  \" ' \" And  ' \" '\n  JS Code : alert(\"Hello World\");\n  HTML Code : <div class=\"some_class\"></div>\n</xmp>\n<script>\n   alert($('#unique_id').html());\n</script>\n
\n\n

Using Pure Javascript :

\n\n
<xmp id=\"unique_id\" style=\"display:none;\">\n  Some plain text\n  Both type of quotes :  \" ' \" And  ' \" '\n  JS Code : alert(\"Hello World\");\n  HTML Code : <div class=\"some_class\"></div>\n</xmp>\n<script>\n   alert(document.getElementById('unique_id').innerHTML);\n</script>\n
\n\n

Cheers!!

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90711", + "creator": "Anmol Saraf", + "createdAt": 1353676200000, + "text": "

Just tried the Anonymous answer and found there's a little trick here, it doesn't work if there's a space after backslash \\
\nSo the following solution doesn't work -

\n\n
var x = { test:'<?xml version=\"1.0\"?>\\ <-- One space here\n            <?mso-application progid=\"Excel.Sheet\"?>' \n};\n
\n\n

But when space is removed it works -

\n\n
var x = { test:'<?xml version=\"1.0\"?>\\<-- No space here now\n          <?mso-application progid=\"Excel.Sheet\"?>' \n};\n\nalert(x.test);​\n
\n\n

Hope it helps !!

\n", + "upvotes": 2686, + "upvoterUsernames": [], + "downvotes": 2686, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d85", + "creator": "Sejanus", + "createdAt": 1355474871000, + "text": "well obviously if you have a space after a backslash, backslash escapes the space. It is supposed to escape linebreak, not space.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90714", + "creator": "BearCode", + "createdAt": 1361238410000, + "text": "

It's not extremely elegant but it's clean enough for me:

\n\n
var myString = \"First line\" + \"\\n\";\nvar myString = myString + \"Second line\" + \"\\n\";\nvar myString = myString + \"Third line\" + \"\\n\";\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d88", + "creator": "Colin", + "createdAt": 1361940358000, + "text": "You should use var myString += "Second line \\n"; instead. MUCH cleaner.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d8a", + "creator": "Michael Mior", + "createdAt": 1373551490000, + "text": "Actually, you should use myString += "Second line \\n"; The var shouldn't be repeated.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d8b", + "creator": "e-info128", + "createdAt": 1375458218000, + "text": "use + only for concatenate strings. Not need declare all buffer for each line. Its horrorific.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90713", + "creator": "mplungjan", + "createdAt": 1361095006000, + "text": "

Downvoters: This code is supplied for information only.

\n\n

This has been tested in Fx 19 and Chrome 24 on Mac

\n\n

DEMO

\n\n

\r\n
\r\n
var new_comment; /*<<<EOF \r\n    <li class=\"photobooth-comment\">\r\n       <span class=\"username\">\r\n          <a href=\"#\">You</a>:\r\n       </span>\r\n       <span class=\"comment-text\">\r\n          $text\r\n       </span> \r\n       @<span class=\"comment-time\">\r\n          2d\r\n       </span> ago\r\n    </li>\r\nEOF*/\r\n// note the script tag here is hardcoded as the FIRST tag \r\nnew_comment=document.currentScript.innerHTML.split(\"EOF\")[1]; \r\ndocument.querySelector(\"ul\").innerHTML=new_comment.replace('$text','This is a dynamically created text');
\r\n
<ul></ul>
\r\n
\r\n
\r\n

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d8e", + "creator": "mplungjan", + "createdAt": 1432721406000, + "text": "currentScript ? In what browsers? My answer is more than 2 years old :)", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d8f", + "creator": "mplungjan", + "createdAt": 1432745195000, + "text": "Undefined "you" in chrome for osx", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d90", + "creator": "Steve Bennett", + "createdAt": 1439770770000, + "text": "Still shows undefined throughout the text for me - Chrome/OSX.", + "upvotes": 352, + "upvoterUsernames": [], + "downvotes": 352, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d92", + "creator": "mplungjan", + "createdAt": 1439782202000, + "text": "I will look when I get to an iMac end of this week", + "upvotes": 216, + "upvoterUsernames": [], + "downvotes": 216, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90716", + "creator": "Luke", + "createdAt": 1363899915000, + "text": "

I came up with this very jimmy rigged method of a multi lined string. Since converting a function into a string also returns any comments inside the function you can use the comments as your string using a multilined comment /**/. You just have to trim off the ends and you have your string.

\n\n
var myString = function(){/*\n    This is some\n    awesome multi-lined\n    string using a comment \n    inside a function \n    returned as a string.\n    Enjoy the jimmy rigged code.\n*/}.toString().slice(14,-3)\n\nalert(myString)\n
\n", + "upvotes": 235, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d93", + "creator": "jondavidjohn", + "createdAt": 1382468833000, + "text": "Also beware of minifiers that strip comments... :D", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d94", + "creator": "schmijos", + "createdAt": 1413556967000, + "text": "@KevinCox you could use trim().", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d96", + "creator": "schmijos", + "createdAt": 1413806211000, + "text": "@KevinCox Thats true. Since I'm using coffeescript, it ends up in a combination: ###).toString().slice(13,-1).trim().slice(2,-2)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d97", + "creator": "Alex Bitek", + "createdAt": 1465564310000, + "text": "Why are 14 and -3 the parameters passed to slice?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d99", + "creator": "Sebastian Simon", + "createdAt": 1471306468000, + "text": "@MnemonicFlow Just copy-paste the code from the answer up to .toString() in your browser console and see what it returns.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d9a", + "creator": "Danilo M. Oliveira", + "createdAt": 1539628793000, + "text": "This is why we can't have nice things.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d9c", + "creator": "Luke", + "createdAt": 1540509942000, + "text": "You can do some weird stuff in javascript land. Though in all honesty, you should never use this.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32597082fcc3049e91d9d", + "creator": "Bergi", + "createdAt": 1659297599000, + "text": "How does one escape the */ comment delimiter if it were to occur inside of the string?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90715", + "creator": "Iain MacKay", + "createdAt": 1362649026000, + "text": "

This is one fairly economical approach, at least in terms of the source code:

\n\n
function s() {\n    var args = [],index;\n    for (index = 0; index< arguments.length; index++) {\n        args.push (arguments [index]);\n    }\n    return args.join (\"\\n\");\n}\nconsole.log (s (\n    \"This is the first line\",\n    \"and this is the second\",\n    \"finally a third\"\n));\n\nfunction s() {return arguments.join (\"\\n\")} \n
\n\n

would be nicer of course if the \"arguments\" property were a proper array.

\n\n

A second version might use \"\" to do the join for cases when you want to control the line breaks in a very long string.

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 101, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91d9e", + "creator": "Jan", + "createdAt": 1442121900000, + "text": "this works: function s() { return Array.prototype.join.call(arguments, '\\n'); }", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90717", + "creator": "e-info128", + "createdAt": 1375459490000, + "text": "

I program this way:

\n\n
sys = {\n    layout: null,\n    makeLayout: function (obCLS) {\n        this.layout = $('<div />').addClass('editor').appendTo($(obCLS)).append(\n\n            /* Cargador */\n            /* @this.layout.find('> div:nth-of-child(1)'); */\n            '<div>' +\n            '   <p>Seleccione la imagen que desea procesar.</p>' +\n            '   <input type=\"button\" value=\"Seleccionar\" class=\"btn btn-xlarge btn-success\" />' +\n            '   <span></span>' +\n            '</div>' +\n\n            /* Cargador - Progreso */\n            /* @this.layout.find('> div:nth-of-child(2)'); */\n            '<div>' +\n            '   <div>' +\n            '       <div></div>' +\n            '       <div>' +\n            '           <div></div>' +\n            '       </div>' +\n            '   </div>' +\n            '</div>' +\n\n            /* Editor */\n            /* @this.layout.find('> div:nth-of-child(3)');\n             * @sidebar = this.layout.find('> div:nth-of-child(3) > div > div > div:nth-of-type(1)');\n             * @body    = this.layout.find('> div:nth-of-child(3) > div > div > div:nth-of-type(2) > div'); */\n            '<div>' +\n            '   <div>' +\n            '       <div>' +\n            '           <div></div>' +\n            '           <div>' +\n            '               <div></div>' +\n            '           </div>' +\n            '       </div>' +\n            '   </div>' +\n            '</div>'\n        );\n    }\n}\n\nsys.makeLayout('#div');\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32597082fcc3049e91da1", + "creator": "Barry Chapman", + "createdAt": 1392741225000, + "text": "that is horrendous, theres a reason people do NOT program that way", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90718", + "creator": "AdrianCooney", + "createdAt": 1377623118000, + "text": "

I think I discovered another way to do it inline without any invasive syntax on every line. Use Javascript's ability to convert a function to string and create a multiline comment with the /**/ syntax then remove the \"function() {/*\\n\" and \"\\n*/}\".

\n\n
var multiline = function(string) { return string.toString().replace(/(^[^\\n]*\\n)|(\\n\\*\\/\\})/g, \"\"); };\n\nconsole.log(multiline(function() {/*\nHello world!\nI'm a multiline string!\n\nTada!\n*/}));\n
\n\n

The only pitfall I can see in this is the syntax highlighting.

\n\n

EDIT: Had I scrolled down a little more, I would have seen this answer doing exactly the same thing: https://stackoverflow.com/a/5571069/916553

\n", + "upvotes": 2302, + "upvoterUsernames": [], + "downvotes": 2302, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90719", + "creator": "KTys", + "createdAt": 1381712299000, + "text": "

My version of array-based join for string concat:

\n\n
var c = []; //c stands for content\nc.push(\"<div id='thisDiv' style='left:10px'></div>\");\nc.push(\"<div onclick='showDo(\\'something\\');'></div>\");\n$(body).append(c.join('\\n'));\n
\n\n

This has worked well for me, especially as I often insert values into the html constructed this way. But it has lots of limitations. Indentation would be nice. Not having to deal with nested quotation marks would be really nice, and just the bulkyness of it bothers me.

\n\n

Is the .push() to add to the array taking up a lot of time? See this related answer:

\n\n

(Is there a reason JavaScript developers don't use Array.push()?)

\n\n

After looking at these (opposing) test runs, it looks like .push() is fine for string arrays which will not likely grow over 100 items - I will avoid it in favor of indexed adds for larger arrays.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9071a", + "creator": "AgelessEssence", + "createdAt": 1384407992000, + "text": "

i found a more elegant solution, maybe a little non-topic related because it uses PHP, but im sure it will be useful and cuteness* for some of yours...

\n\n

this javascript code should stay inside script tags

\n\n
var html=<?php echo json_encode(\"\n\n        <div class=container>\n            <div class=area1>\n                xxx\n            </div>\n            <div class=area2>\n                \".$someVar.\"\n            </div>\n        </div>\n\n\"); ?>\n
\n\n

in your output html you will see something like

\n\n
var html=\"\\r\\n\\r\\n\\t\\t\\t<div class=container>\\r\\n\\t\\t\\t\\t<div class=area1>\\r\\n\\t\\t\\t\\t\\txxx\\r\\n\\t\\t\\t\\t<\\/div>\\r\\n\\t\\t\\t\\t<div class=area2>\\r\\n\\t\\t\\t\\t\\t44\\r\\n\\t\\t\\t\\t<\\/div>\\r\\n\\t\\t\\t<\\/div>\\r\\n\\r\\n\\t\\t\";  \n
\n\n

 

\n\n
\n\n

and et voilà!, it gives you code readability in your file.

\n\n

pD: this sample uses json_encode() PHP function, but certainly there are function equivalents for ASP, Ruby and JSP langs.

\n\n

pD: however, this solution have his limitations too, one of them is you cannot use javascript variables inside the encapsulated code.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9071b", + "creator": "pocheptsov", + "createdAt": 1386783058000, + "text": "

My extension to https://stackoverflow.com/a/15558082/80404.\nIt expects comment in a form /*! any multiline comment */ where symbol ! is used to prevent removing by minification (at least for YUI compressor)

\n\n
Function.prototype.extractComment = function() {\n    var startComment = \"/*!\";\n    var endComment = \"*/\";\n    var str = this.toString();\n\n    var start = str.indexOf(startComment);\n    var end = str.lastIndexOf(endComment);\n\n    return str.slice(start + startComment.length, -(str.length - end));\n};\n
\n\n

Example:

\n\n
var tmpl = function() { /*!\n <div class=\"navbar-collapse collapse\">\n    <ul class=\"nav navbar-nav\">\n    </ul>\n </div>\n*/}.extractComment();\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9071c", + "creator": "Mr. Alien", + "createdAt": 1390051134000, + "text": "

You can use += to concatenate your string, seems like no one answered that, which will be readable, and also neat... something like this

\n\n
var hello = 'hello' +\n            'world' +\n            'blah';\n
\n\n

can be also written as

\n\n
var hello = 'hello';\n    hello += ' world';\n    hello += ' blah';\n\nconsole.log(hello);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9071e", + "creator": "mightyiam", + "createdAt": 1398425697000, + "text": "

There's this library that makes it beautiful:

\n\n

https://github.com/sindresorhus/multiline

\n\n

Before

\n\n
var str = '' +\n'<!doctype html>' +\n'<html>' +\n'   <body>' +\n'       <h1>❤ unicorns</h1>' +\n'   </body>' +\n'</html>' +\n'';\n
\n\n

After

\n\n
var str = multiline(function(){/*\n<!doctype html>\n<html>\n    <body>\n        <h1>❤ unicorns</h1>\n    </body>\n</html>\n*/});\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32598082fcc3049e91da8", + "creator": "Huei Tan", + "createdAt": 1399279959000, + "text": "This support in nodejs, using in browser must becareful.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32598082fcc3049e91daa", + "creator": "mikemaccana", + "createdAt": 1405278872000, + "text": "@HueiTan Docs state it also works in the browser. Which makes sense - it's just Function.prototype.String().", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32598082fcc3049e91dab", + "creator": "mikemaccana", + "createdAt": 1405335136000, + "text": "@HueiTanYep I read that part. But Function.prototype.toString() is pretty stable and well known.", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9071d", + "creator": "mikemaccana", + "createdAt": 1392030818000, + "text": "

Updated for 2015: it's six years later now: most people use a module loader, and the main module systems each have ways of loading templates. It's not inline, but the most common type of multiline string are templates, and templates should generally be kept out of JS anyway.

\n\n

require.js: 'require text'.

\n\n

Using require.js 'text' plugin, with a multiline template in template.html

\n\n
var template = require('text!template.html')\n
\n\n

NPM/browserify: the 'brfs' module

\n\n

Browserify uses a 'brfs' module to load text files. This will actually build your template into your bundled HTML.

\n\n
var fs = require(\"fs\");\nvar template = fs.readFileSync(template.html', 'utf8');\n
\n\n

Easy.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9071f", + "creator": "Vignesh Subramanian", + "createdAt": 1401096881000, + "text": "

There are multiple ways to achieve this

\n\n

1. Slash concatenation

\n\n
  var MultiLine=  '1\\\n    2\\\n    3\\\n    4\\\n    5\\\n    6\\\n    7\\\n    8\\\n    9';\n
\n\n

2. regular concatenation

\n\n
var MultiLine = '1'\n+'2'\n+'3'\n+'4'\n+'5';\n
\n\n

3. Array Join concatenation

\n\n
var MultiLine = [\n'1',\n'2',\n'3',\n'4',\n'5'\n].join('');\n
\n\n

Performance wise, Slash concatenation (first one) is the fastest.

\n\n

Refer this test case for more details regarding the performance

\n\n

Update:

\n\n

With the ES2015, we can take advantage of its Template strings feature. With it, we just need to use back-ticks for creating multi line strings

\n\n

Example:

\n\n
 `<h1>{{title}}</h1>\n  <h2>{{hero.name}} details!</h2>\n  <div><label>id: </label>{{hero.id}}</div>\n  <div><label>name: </label>{{hero.name}}</div>\n  `\n
\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32598082fcc3049e91dad", + "creator": "RandomInsano", + "createdAt": 1407003753000, + "text": "I think it's that you've just regurgitated what has already on the page for five years, but in a cleaner way.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32598082fcc3049e91daf", + "creator": "f.khantsis", + "createdAt": 1494373173000, + "text": "won't slash concatenation also include the whitespace in beginning of lines?", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90720", + "creator": "Charles Brandt", + "createdAt": 1410220949000, + "text": "

If you happen to be running in Node only, you could use the fs module to read in the multi-line string from a file:

\n\n
var diagram;\nvar fs = require('fs');\nfs.readFile( __dirname + '/diagram.txt', function (err, data) {\n  if (err) {\n    throw err; \n  }\n  diagram = data.toString();\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90721", + "creator": "seo", + "createdAt": 1430245860000, + "text": "

If you're willing to use the escaped newlines, they can be used nicely. It looks like a document with a page border.

\n\n

\"enter

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32598082fcc3049e91db3", + "creator": "tomByrer", + "createdAt": 1449404997000, + "text": "Wouldn't this add extraneous blank spaces?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32598082fcc3049e91db5", + "creator": "seo", + "createdAt": 1449442970000, + "text": "@tomByrer Yes, good observation. It's only good for strings which you don't care about white space, e.g. HTML.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90722", + "creator": "Stefan Steiger", + "createdAt": 1442849013000, + "text": "

You can use TypeScript (JavaScript SuperSet), it supports multiline strings, and transpiles back down to pure JavaScript without overhead:

\n\n
var templates = {\n    myString: `this is\na multiline\nstring` \n}\n\nalert(templates.myString);\n
\n\n

If you'd want to accomplish the same with plain JavaScript:

\n\n
var templates = \n{\n myString: function(){/*\n    This is some\n    awesome multi-lined\n    string using a comment \n    inside a function \n    returned as a string.\n    Enjoy the jimmy rigged code.\n*/}.toString().slice(14,-3)\n\n}\nalert(templates.myString)\n
\n\n

Note that the iPad/Safari does not support 'functionName.toString()'

\n\n

If you have a lot of legacy code, you can also use the plain JavaScript variant in TypeScript (for cleanup purposes):

\n\n
interface externTemplates\n{\n    myString:string;\n}\n\ndeclare var templates:externTemplates;\n\nalert(templates.myString)\n
\n\n

and you can use the multiline-string object from the plain JavaScript variant, where you put the templates into another file (which you can merge in the bundle).

\n\n

You can try TypeScript at
\nhttp://www.typescriptlang.org/Playground

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90724", + "creator": "earl3s", + "createdAt": 1459190606000, + "text": "

ES6 allows you to use a backtick to specify a string on multiple lines. It's called a Template Literal. Like this:

\n\n
var multilineString = `One line of text\n    second line of text\n    third line of text\n    fourth line of text`;\n
\n\n

Using the backtick works in NodeJS, and it's supported by Chrome, Firefox, Edge, Safari, and Opera.

\n\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90723", + "creator": "Lonnie Best", + "createdAt": 1446645573000, + "text": "

The equivalent in javascript is:

\n\n
var text = `\nThis\nIs\nA\nMultiline\nString\n`;\n
\n\n

Here's the specification. See browser support at the bottom of this page. Here are some examples too.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90725", + "creator": "Prakash GPz", + "createdAt": 1468437931000, + "text": "

Also do note that, when extending string over multiple lines using forward backslash at end of each line, any extra characters (mostly spaces, tabs and comments added by mistake) after forward backslash will cause unexpected character error, which i took an hour to find out

\n\n
var string = \"line1\\  // comment, space or tabs here raise error\nline2\";\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90726", + "creator": "Sonevol", + "createdAt": 1502665040000, + "text": "

You have to use the concatenation operator '+'.

\n\n
<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Document</title>\n</head>\n<body>\n    <p id=\"demo\"></p>\n    <script>\n        var str = \"This \"\n                + \"\\n<br>is \"\n                + \"\\n<br>multiline \"\n                + \"\\n<br>string.\";\n        document.getElementById(\"demo\").innerHTML = str;\n     </script>\n</body>\n</html>\n
\n\n

By using \\n your source code will look like -

\n\n
\nThis \n <br>is\n <br>multiline\n <br>string.\n
\n\n

By using <br> your browser output will look like -

\n\n
\nThis\nis\nmultiline\nstring.\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90728", + "creator": "jenil christo", + "createdAt": 1531502751000, + "text": "

The ES6 way of doing it would be by using template literals:

\n\n
const str = `This \n\nis \n\na\n\nmultiline text`; \n\nconsole.log(str);\n
\n\n

More reference here

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90727", + "creator": "Pragmatiq", + "createdAt": 1507764527000, + "text": "

Please for the love of the internet use string concatenation and opt not to use ES6 solutions for this. ES6 is NOT supported all across the board, much like CSS3 and certain browsers being slow to adapt to the CSS3 movement. Use plain ol' JavaScript, your end users will thank you.

\n\n

Example:

\n\n

var str = \"This world is neither flat nor round. \"+\n \"Once was lost will be found\";

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32599082fcc3049e91dba", + "creator": "user151496", + "createdAt": 1520209100000, + "text": "while i agree with your point, i wouldn't call javascript "good" ol", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32599082fcc3049e91dbc", + "creator": "Ken Ingram", + "createdAt": 1647321134000, + "text": "How does this admonition stand up in 2022?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90729", + "creator": "Willem van der Veen", + "createdAt": 1536490817000, + "text": "

Easiest way to make multiline strings in Javascrips is with the use of backticks ( `` ). This allows you to create multiline strings in which you can insert variables with ${variableName}.

\n\n

Example:

\n\n

\r\n
\r\n
let name = 'Willem'; \r\nlet age = 26;\r\n\r\nlet multilineString = `\r\nmy name is: ${name}\r\n\r\nmy age is: ${age}\r\n`;\r\n\r\nconsole.log(multilineString);
\r\n
\r\n
\r\n

\n\n

compatibility :

\n\n\n\n

Check exact compatibility in Mozilla docs here

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32599082fcc3049e91dbe", + "creator": "cmpreshn", + "createdAt": 1538105869000, + "text": "Is this now compatible with all recent browsers? Or are there some browsers which still do not support this syntax?", + "upvotes": 887, + "upvoterUsernames": [], + "downvotes": 887, + "downvoterUsernames": [] + }, + { + "_id": "62f32599082fcc3049e91dc0", + "creator": "Willem van der Veen", + "createdAt": 1538429313000, + "text": "Sorry for my extreme late comment, edited the answer added compatibility info ;)", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9072a", + "creator": "Sapphire_Brick", + "createdAt": 1564005705000, + "text": "

The Rule is: when inside a string, use \\n wherever you want a new line; you do not have to put a space before or after the \\n, JavaScript's interpreter is smart enough to know how long the unprintable character representation is.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9072b", + "creator": "Kamil Kiełczewski", + "createdAt": 1591821955000, + "text": "

Exact

\n

Ruby produce: "This\\nIs\\nA\\nMultiline\\nString\\n" - below JS produce exact same string

\n

\r\n
\r\n
text = `This\nIs\nA\nMultiline\nString\n`\n\n// TEST\nconsole.log(JSON.stringify(text));\nconsole.log(text);
\r\n
\r\n
\r\n

\n

This is improvement to Lonnie Best answer because new-line characters in his answer are not exactly the same positions as in ruby output

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32599082fcc3049e91dc1", + "creator": "FlatLander", + "createdAt": 1594552859000, + "text": "text is string why json.stringify?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9072c", + "creator": "Pedro Andrade", + "createdAt": 1594243021000, + "text": "

You can use tagged templates to make sure you get the desired output.

\n

For example:

\n
// Merging multiple whitespaces and trimming the output\n\nconst t = (strings) => { return strings.map((s) => s.replace(/\\s+/g, ' ')).join("").trim() }\nconsole.log(t`\n  This\n  Is\n  A\n  Multiline\n  String\n`);\n// Output: 'This Is A Multiline String'\n\n// Similar but keeping whitespaces:\n\nconst tW = (strings) => { return strings.map((s) => s.replace(/\\s+/g, '\\n')).join("").trim() }\nconsole.log(tW`\n  This\n  Is\n  A\n  Multiline\n  String\n`);\n// Output: 'This\\nIs\\nA\\nMultiline\\nString'\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9072e", + "creator": "Niv", + "createdAt": 1636134393000, + "text": "

Found a lot of over engineered answers here.\nThe two best answers in my opinion were:

\n

1:

\n
 let str = `Multiline string.\n            foo.\n            bar.`\n
\n

which eventually logs:

\n
Multiline string.\n           foo.\n           bar.  \n
\n

2:

\n
let str = `Multiline string.\nfoo.\nbar.`\n
\n

That logs it correctly but it's ugly in the script file if str is nested inside functions / objects etc...:

\n
Multiline string.\nfoo.\nbar.\n
\n

My really simple answer with regex which logs the str correctly:

\n
let str = `Multiline string.\n           foo.\n           bar.`.replace(/\\n +/g, '\\n');\n
\n

Please note that it is not the perfect solution but it works if you are sure that after the new line (\\n) at least one space will come (+ means at least one occurrence). It also will work with * (zero or more).

\n

You can be more explicit and use {n,} which means at least n occurrences.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32599082fcc3049e91dc4", + "creator": "Kaz", + "createdAt": 1642792685000, + "text": "Why wouldn't you just [ "line", "line2", "line3" ].join("\\n").", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9072d", + "creator": "NimaDoustdar", + "createdAt": 1634988525000, + "text": "

JavaScript never had a true good way to handle multiline strings, until 2015 when ES6 was introduced, along with template literals.

\n

Template literals are strings delimited by backticks(``), instead of the normal single('')/doubl("")e quote delimiter.

\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9072f", + "creator": "Abhishek Goel", + "createdAt": 1642681156000, + "text": "

Multiline string with variables

\n
var x = 1\nstring = string + `<label class="container">\n                       <p>${x}</p>\n                   </label>`;\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90730", + "creator": "ankit bhusal", + "createdAt": 1646241961000, + "text": "

A simple way to print multiline strings in JavaScript is by using template literals(template strings) denoted by backticks (` `). you can also use variables inside a template string-like (` name is ${value} `)

\n

You can also

\n

\r\n
\r\n
const value = `multiline`\nconst text = `This is a\n${value}\nstring in js`;\nconsole.log(text);
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2110940, + "uvac": 2110983 + } + }, + { + "_id": "62f321bb082fcc3049e8fee5", + "title": "How do I modify the URL without reloading the page?", + "title-lowercase": "how do i modify the url without reloading the page?", + "creator": "Robin Rodricks", + "createdAt": 1241520845000, + "status": "open", + "text": "

Is there a way I can modify the URL of the current page without reloading the page?

\n

I would like to access the portion before the # hash if possible.

\n

I only need to change the portion after the domain, so it's not like I'm violating cross-domain policies.

\n
 window.location.href = "www.mysite.com/page2.php";  // this reloads\n
\n", + "upvotes": 5585, + "upvoterUsernames": [], + "downvotes": 2741, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1834691, + "answers": 20, + "answerItems": [ + { + "_id": "62f321c6082fcc3049e909ec", + "creator": "Gumbo", + "createdAt": 1241521133000, + "text": "

Any changes of the loction (either window.location or document.location) will cause a request on that new URL, if you’re not just changing the URL fragment. If you change the URL, you change the URL.

\n\n

Use server-side URL rewrite techniques like Apache’s mod_rewrite if you don’t like the URLs you are currently using.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e92206", + "creator": "Robin Rodricks", + "createdAt": 1241521773000, + "text": "Can I use "location.pathname"??", + "upvotes": 3119, + "upvoterUsernames": [], + "downvotes": 3119, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e92208", + "creator": "Gumbo", + "createdAt": 1241524047000, + "text": "No, changing that attribute will cause a request too.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909eb", + "creator": "Robin Day", + "createdAt": 1241521025000, + "text": "

NOTE: If you are working with an HTML5 browser then you should ignore this answer. This is now possible as can be seen in the other answers.

\n\n

There is no way to modify the URL in the browser without reloading the page. The URL represents what the last loaded page was. If you change it (document.location) then it will reload the page.

\n\n

One obvious reason being, you write a site on www.mysite.com that looks like a bank login page. Then you change the browser URL bar to say www.mybank.com. The user will be totally unaware that they are really looking at www.mysite.com.

\n", + "upvotes": 209, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e92209", + "creator": "Eugene", + "createdAt": 1626711499000, + "text": "remove it or add html4, because currently it displayed in google search as quick answer", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909ee", + "creator": "Nate", + "createdAt": 1260745031000, + "text": "

You can add anchor tags. I use this on my site so that I can track with Google Analytics what people are visiting on the page.

\n\n

I just add an anchor tag and then the part of the page I want to track:

\n\n
var trackCode = \"/#\" + urlencode($(\"myDiv\").text());\nwindow.location.href = \"http://www.piano-chords.net\" + trackCode;\npageTracker._trackPageview(trackCode);\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f0", + "creator": "Vivart", + "createdAt": 1282053575000, + "text": "

HTML5 introduced the history.pushState() and history.replaceState() methods, which allow you to add and modify history entries, respectively.

\n
window.history.pushState('page2', 'Title', '/page2.php');\n
\n

Read more about this from here

\n", + "upvotes": 1159, + "upvoterUsernames": [], + "downvotes": 383, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e9220a", + "creator": "Spectric", + "createdAt": 1617384405000, + "text": "IMO the easiest and cleanest solution.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e9220c", + "creator": "Rodrigo", + "createdAt": 1652111069000, + "text": "What happens if I use null as the first parameter of pushState()?", + "upvotes": 2213, + "upvoterUsernames": [], + "downvotes": 2213, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909ed", + "creator": "Steve", + "createdAt": 1242252884000, + "text": "
parent.location.hash = \"hello\";\n
\n", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e9220e", + "creator": "Robin Rodricks", + "createdAt": 1242325560000, + "text": "I want to change the URL, not just the hash -- #mydata", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e9220f", + "creator": "noisy", + "createdAt": 1374044823000, + "text": "@Jarvis: what is the difference?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e92211", + "creator": "MLK.DEV", + "createdAt": 1415311632000, + "text": "@noisy Server side tracking cannot see hashes unless sent to the tracking service explicitly.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e92213", + "creator": "catbadger", + "createdAt": 1485957443000, + "text": "this is not useful if you're using an mvc framework that routes on hash, for example backbone.", + "upvotes": 378, + "upvoterUsernames": [], + "downvotes": 378, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e92215", + "creator": "Math Coder 101", + "createdAt": 1627059422000, + "text": "This only partially answers the question. We want the url to change, not the hash.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909ef", + "creator": "David Murdoch", + "createdAt": 1280331031000, + "text": "

This can now be done in Chrome, Safari, Firefox 4+, and Internet Explorer 10pp4+!

\n\n

See this question's answer for more information:\nUpdating address bar with new URL without hash or reloading the page

\n\n

Example:

\n\n
 function processAjaxData(response, urlPath){\n     document.getElementById(\"content\").innerHTML = response.html;\n     document.title = response.pageTitle;\n     window.history.pushState({\"html\":response.html,\"pageTitle\":response.pageTitle},\"\", urlPath);\n }\n
\n\n

You can then use window.onpopstate to detect the back/forward button navigation:

\n\n
window.onpopstate = function(e){\n    if(e.state){\n        document.getElementById(\"content\").innerHTML = e.state.html;\n        document.title = e.state.pageTitle;\n    }\n};\n
\n\n
\n\n

For a more in-depth look at manipulating browser history, see this MDN article.

\n", + "upvotes": 4150, + "upvoterUsernames": [], + "downvotes": 1822, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e92217", + "creator": "Edison Pebojot", + "createdAt": 1621402969000, + "text": "Can I asks what is the value of response.html? Is that a string of HTML?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e92219", + "creator": "DeeStarks", + "createdAt": 1622842356000, + "text": "What data should be passed to "response"?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326cc082fcc3049e9221b", + "creator": "sshanzel", + "createdAt": 1655803807000, + "text": "Is there any workaround for changing it with a different domain? This works only when the target URL is under the same domain.", + "upvotes": 2983, + "upvoterUsernames": [], + "downvotes": 2983, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909f1", + "creator": "Jeremy Warne", + "createdAt": 1303213439000, + "text": "

If what you're trying to do is allow users to bookmark/share pages, and you don't need it to be exactly the right URL, and you're not using hash anchors for anything else, then you can do this in two parts; you use the location. hash discussed above, and then implement a check on the home page, to look for a URL with a hash anchor in it, and redirect you to the subsequent result.

\n

For instance:

\n
    \n
  1. User is on www.site.com/section/page/4

    \n
  2. \n
  3. User does some action which changes the URL to www.site.com/#/section/page/6 (with the hash). Say you've loaded the correct content for page 6 into the page, so apart from the hash the user is not too disturbed.

    \n
  4. \n
  5. User passes this URL on to someone else, or bookmarks it

    \n
  6. \n
  7. Someone else, or the same user at a later date, goes to www.site.com/#/section/page/6

    \n
  8. \n
  9. Code on www.site.com/ redirects the user to www.site.com/section/page/6, using something like this:

    \n
  10. \n
\n
if (window.location.hash.length > 0){ \n   window.location = window.location.hash.substring(1);\n}\n
\n

Hope that makes sense! It's a useful approach for some situations.

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f2", + "creator": "George Filippakos", + "createdAt": 1353321151000, + "text": "

You can also use HTML5 replaceState if you want to change the url but don't want to add the entry to the browser history:

\n\n
if (window.history.replaceState) {\n   //prevents browser from storing history with each change:\n   window.history.replaceState(statedata, title, url);\n}\n
\n\n

This would 'break' the back button functionality. This may be required in some instances such as an image gallery (where you want the back button to return back to the gallery index page instead of moving back through each and every image you viewed) whilst giving each image its own unique url.

\n", + "upvotes": 318, + "upvoterUsernames": [], + "downvotes": 128, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f4", + "creator": "Erenor Paz", + "createdAt": 1378389324000, + "text": "

As pointed out by Thomas Stjernegaard Jeppesen, you could use History.js to modify URL parameters whilst the user navigates through your Ajax links and apps.

\n\n

Almost an year has passed since that answer, and History.js grew and became more stable and cross-browser. Now it can be used to manage history states in HTML5-compliant as well as in many HTML4-only browsers. In this demo You can see an example of how it works (as well as being able to try its functionalities and limits.

\n\n

Should you need any help in how to use and implement this library, i suggest you to take a look at the source code of the demo page: you will see it's very easy to do.

\n\n

Finally, for a comprehensive explanation of what can be the issues about using hashes (and hashbangs), check out this link by Benjamin Lupton.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f3", + "creator": "Thomas Stjernegaard Jeppesen", + "createdAt": 1353496142000, + "text": "

The HTML5 replaceState is the answer, as already mentioned by Vivart and geo1701. However it is not supported in all browsers/versions.\nHistory.js wraps HTML5 state features and provides additional support for HTML4 browsers.

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f6", + "creator": "Haimei", + "createdAt": 1433960735000, + "text": "

Here is my solution (newUrl is your new URL which you want to replace with the current one):

\n\n
history.pushState({}, null, newUrl);\n
\n", + "upvotes": 204, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cd082fcc3049e92222", + "creator": "Craig Jacobs", + "createdAt": 1472429668000, + "text": "Best to test first with if (history.pushState) {} Just in case old browser.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f326cd082fcc3049e92224", + "creator": "kkatusic", + "createdAt": 1510222774000, + "text": "This doesn't work any more you will get in Firefox: The operation is insecure.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909f7", + "creator": "Prathamesh Rasam", + "createdAt": 1439639732000, + "text": "

Use history.pushState() from the HTML 5 History API.

\n\n

Refer to the HTML5 History API for more details.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f5", + "creator": "Shine", + "createdAt": 1390553348000, + "text": "

Before HTML5 we can use:

\n\n
parent.location.hash = \"hello\";\n
\n\n

and:

\n\n
window.location.replace(\"http:www.example.com\");\n
\n\n

This method will reload your page, but HTML5 introduced the history.pushState(page, caption, replace_url) that should not reload your page.

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909f8", + "creator": "Suraj", + "createdAt": 1445934793000, + "text": "

Below is the function to change the URL without reloading the page. It is only supported for HTML5.

\n\n
  function ChangeUrl(page, url) {\n        if (typeof (history.pushState) != \"undefined\") {\n            var obj = {Page: page, Url: url};\n            history.pushState(obj, obj.Page, obj.Url);\n        } else {\n            window.location.href = \"homePage\";\n            // alert(\"Browser does not support HTML5.\");\n        }\n    }\n\n  ChangeUrl('Page1', 'homePage');\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909fa", + "creator": "Dheeraj Thedijje", + "createdAt": 1575020880000, + "text": "

You can use this beautiful and simple function to do so anywhere on your application.

\n
function changeurl(url, title) {\n    var new_url = '/' + url;\n    window.history.pushState('data', title, new_url);\n    \n}\n
\n

You can not only edit the URL but you can update the title along with it.

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cd082fcc3049e9222a", + "creator": "BIOHAZARD", + "createdAt": 1581075414000, + "text": "Works like a charm even for anchors window.history.pushState('data', 'Title', '#new_location');", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326cd082fcc3049e9222c", + "creator": "xeruf", + "createdAt": 1612813254000, + "text": "why don't you use the 'title' argument as title in pushState?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909f9", + "creator": "Alireza", + "createdAt": 1498982582000, + "text": "

In modern browsers and HTML5, there is a method called pushState on window history. That will change the URL and push it to the history without loading the page.

\n

You can use it like this, it will take 3 parameters, 1) state object 2) title and a URL):

\n
window.history.pushState({page: "another"}, "another page", "example.html");\n
\n

This will change the URL, but not reload the page. Also, it doesn't check if the page exists, so if you do some JavaScript code that is reacting to the URL, you can work with them like this.

\n

Also, there is history.replaceState() which does exactly the same thing, except it will modify the current history instead of creating a new one!

\n

Also you can create a function to check if history.pushState exist, then carry on with the rest like this:

\n
function goTo(page, title, url) {\n  if ("undefined" !== typeof history.pushState) {\n    history.pushState({page: page}, title, url);\n  } else {\n    window.location.assign(url);\n  }\n}\n\ngoTo("another page", "example", 'example.html');\n
\n

Also, you can change the # for <HTML5 browsers, which won't reload the page. That's the way Angular uses to do SPA according to hashtag...

\n

Changing # is quite easy, doing like:

\n
window.location.hash = "example";\n
\n

And you can detect it like this:

\n
window.onhashchange = function () {\n  console.log("#changed", window.location.hash);\n}\n
\n", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cd082fcc3049e9222f", + "creator": "J0ANMM", + "createdAt": 1531828487000, + "text": "Would window.onhashchange and window.onpopstate do the same in this case?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326cd082fcc3049e92231", + "creator": "MD. Atiqur Rahman", + "createdAt": 1536933263000, + "text": "How to load page contents as well? It just replaces the URL", + "upvotes": 2470, + "upvoterUsernames": [], + "downvotes": 2470, + "downvoterUsernames": [] + }, + { + "_id": "62f326cd082fcc3049e92233", + "creator": "Johann", + "createdAt": 1537028848000, + "text": "Load content as you would normally do with ajax.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909fc", + "creator": "Vinay Kaithwas", + "createdAt": 1595054071000, + "text": "

Your new url.

\n
let newUrlIS =  window.location.origin + '/user/profile/management';\n
\n

In a sense, calling pushState() is similar to setting window.location = "#foo", in that both will also create and activate another history entry associated with the current document. But pushState() has a few advantages:

\n
history.pushState({}, null, newUrlIS);\n
\n

You can check out the root: https://developer.mozilla.org/en-US/docs/Web/API/History_API

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cd082fcc3049e92235", + "creator": "xeruf", + "createdAt": 1612813211000, + "text": "please add a reference to the supposed "advantages" of pushState :)", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909fb", + "creator": "Omar bakhsh", + "createdAt": 1591744039000, + "text": "

This is all you will need to navigate without reload

\n

\r\n
\r\n
// add setting without reload \nlocation.hash = \"setting\";\n\n// if url change with hash do somthing\nwindow.addEventListener('hashchange', () => {\n    console.log('url hash changed!');\n});\n\n// if url change do somthing (dont detect changes with hash)\n//window.addEventListener('locationchange', function(){\n//    console.log('url changed!');\n//})\n\n\n// remove #setting without reload \n\nhistory.back();
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cd082fcc3049e92237", + "creator": "Sebastian Simon", + "createdAt": 1648338974000, + "text": "“I would like to access the portion before the # hash if possible.”", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909fd", + "creator": "Webdeveloper011", + "createdAt": 1610290941000, + "text": "

This code works for me. I used it into my application in ajax.

\n
history.pushState({ foo: 'bar' }, '', '/bank');\n
\n

Once a page load into an ID using ajax, It does change the browser url automatically without reloading the page.

\n

This is ajax function bellow.

\n
function showData(){\n    $.ajax({\n      type: "POST",\n      url: "Bank.php", \n      data: {}, \n      success: function(html){          \n        $("#viewpage").html(html).show();\n        $("#viewpage").css("margin-left","0px");\n      }\n    });\n  }\n
\n

Example: From any page or controller like "Dashboard", When I click on the bank, it loads bank list using the ajax code without reloading the page. At this time, browser URL will not be changed.

\n
history.pushState({ foo: 'bar' }, '', '/bank');\n
\n

But when I use this code into the ajax, it change the browser url without reloading the page.\nThis is the full ajax code here in the bellow.

\n
function showData(){\n        $.ajax({\n          type: "POST",\n          url: "Bank.php", \n          data: {}, \n          success: function(html){          \n            $("#viewpage").html(html).show();\n            $("#viewpage").css("margin-left","0px");\n            history.pushState({ foo: 'bar' }, '', '/bank');\n          }\n        });\n      }\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909fe", + "creator": "James Bond", + "createdAt": 1628186121000, + "text": "

Simply use, it will not reload the page, but just the URL :

\n
$('#form_name').attr('action', '/shop/index.htm').submit();\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ce082fcc3049e92239", + "creator": "Stefan Reich", + "createdAt": 1630048125000, + "text": "This makes no sense", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c6082fcc3049e909e7", + "creator": "Ninjakannon", + "createdAt": 1639065019000, + "text": "window.history.replaceState(null, document.title, "/page2.php") is probably what most people are looking for.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1840277, + "uvac": 1840297 + } + }, + { + "_id": "62f321bb082fcc3049e8fef0", + "title": "Set a default parameter value for a JavaScript function", + "title-lowercase": "set a default parameter value for a javascript function", + "creator": "Tilendor", + "createdAt": 1242936437000, + "status": "open", + "text": "

I would like a JavaScript function to have optional arguments which I set a default on, which get used if the value isn't defined (and ignored if the value is passed). In Ruby you can do it like this:

\n\n
def read_file(file, delete_after = false)\n  # code\nend\n
\n\n

Does this work in JavaScript?

\n\n
function read_file(file, delete_after = false) {\n  // Code\n}\n
\n", + "upvotes": 4343, + "upvoterUsernames": [], + "downvotes": 1758, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1400980, + "answers": 27, + "answerItems": [ + { + "_id": "62f321c9082fcc3049e90c18", + "creator": "Russ Cam", + "createdAt": 1242936588000, + "text": "
function read_file(file, delete_after) {\n    delete_after = delete_after || \"my default here\";\n    //rest of code\n}\n
\n\n

This assigns to delete_after the value of delete_after if it is not a falsey value otherwise it assigns the string \"my default here\". For more detail, check out Doug Crockford's survey of the language and check out the section on Operators.

\n\n

This approach does not work if you want to pass in a falsey value i.e. false, null, undefined, 0 or \"\". If you require falsey values to be passed in you would need to use the method in Tom Ritter's answer.

\n\n

When dealing with a number of parameters to a function, it is often useful to allow the consumer to pass the parameter arguments in an object and then merge these values with an object that contains the default values for the function

\n\n
function read_file(values) {\n    values = merge({ \n        delete_after : \"my default here\"\n    }, values || {});\n\n    // rest of code\n}\n\n// simple implementation based on $.extend() from jQuery\nfunction merge() {\n    var obj, name, copy,\n        target = arguments[0] || {},\n        i = 1,\n        length = arguments.length;\n\n    for (; i < length; i++) {\n        if ((obj = arguments[i]) != null) {\n            for (name in obj) {\n                copy = obj[name];\n\n                if (target === copy) {\n                    continue;\n                }\n                else if (copy !== undefined) {\n                    target[name] = copy;\n                }\n            }\n        }\n    }\n\n    return target;\n};\n
\n\n

to use

\n\n
// will use the default delete_after value\nread_file({ file: \"my file\" }); \n\n// will override default delete_after value\nread_file({ file: \"my file\", delete_after: \"my value\" }); \n
\n", + "upvotes": 964, + "upvoterUsernames": [], + "downvotes": 339, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925db", + "creator": "Tom Ritter", + "createdAt": 1242936679000, + "text": "I find this insufficient, because I may want to pass in false.", + "upvotes": 184, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925dc", + "creator": "Russ Cam", + "createdAt": 1242936974000, + "text": "I find it's adequate for most situations", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925de", + "creator": "beerwin", + "createdAt": 1445507159000, + "text": "This may be better this way: delete_after = typeof(delete_after) != 'undefined' ? delete_after : "my default here";", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925e0", + "creator": "Sumesh TG", + "createdAt": 1530792743000, + "text": "This works for variebles as well as objects. Thank you.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c1c", + "creator": "jshbrmn", + "createdAt": 1444148181000, + "text": "

As an update...with ECMAScript 6 you can FINALLY set default values in function parameter declarations like so:

\n\n
function f (x, y = 7, z = 42) {\n  return x + y + z\n}\n\nf(1) === 50\n
\n\n

As referenced by - http://es6-features.org/#DefaultParameterValues

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925e4", + "creator": "user", + "createdAt": 1446770909000, + "text": "This answer is not useful because it is a duplicate", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c1d", + "creator": "Martin Wantke", + "createdAt": 1447659274000, + "text": "

Just use an explicit comparison with undefined.

\n\n
function read_file(file, delete_after)\n{\n    if(delete_after === undefined) { delete_after = false; }\n}\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c1f", + "creator": "vivek", + "createdAt": 1463640008000, + "text": "

being a long time C++ developer (Rookie to web development :)), when I first came across this situation, I did the parameter assignment in the function definition, like it is mentioned in the question, as follows.

\n\n
function myfunc(a,b=10)\n
\n\n

But beware that it doesn't work consistently across browsers. For me it worked on chrome on my desktop, but did not work on chrome on android.\nSafer option, as many have mentioned above is -

\n\n
    function myfunc(a,b)\n    {\n    if (typeof(b)==='undefined') b = 10;\n......\n    }\n
\n\n

Intention for this answer is not to repeat the same solutions, what others have already mentioned, but to inform that parameter assignment in the function definition may work on some browsers, but don't rely on it.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925e7", + "creator": "DiMono", + "createdAt": 1468896774000, + "text": "Assignment within the function definition also doesn't work in IE 11, which is the most current version of IE for Windows 8.1.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925e9", + "creator": "gcampbell", + "createdAt": 1474189201000, + "text": "In case anyone's wondering, function myfunc(a,b=10) is ES6 syntax, there are links to compatibility tables in other answers.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c1a", + "creator": "TJ L", + "createdAt": 1242937126000, + "text": "

I find something simple like this to be much more concise and readable personally.

\n\n
function pick(arg, def) {\n   return (typeof arg == 'undefined' ? def : arg);\n}\n\nfunction myFunc(x) {\n  x = pick(x, 'my default');\n} \n
\n", + "upvotes": 251, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925eb", + "creator": "JasonDavis", + "createdAt": 1413166088000, + "text": "@andersand I like the idea of something like util.default(arg, "defaul value")) would you or @tj111 mind posting a quick demo?", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925ed", + "creator": "user2039981", + "createdAt": 1429635559000, + "text": "I actually recommend this one, i use it and call it "por" which stands for "parameter or"", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925ef", + "creator": "OsamaBinLogin", + "createdAt": 1445353611000, + "text": "Don't say typeof arg == 'undefined', instead say arg === undefined", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c1b", + "creator": "Felix Kling", + "createdAt": 1432913127000, + "text": "

In ECMAScript 6 you will actually be able to write exactly what you have:

\n\n
function read_file(file, delete_after = false) {\n  // Code\n}\n
\n\n

This will set delete_after to false if it s not present or undefined. You can use ES6 features like this one today with transpilers such as Babel.

\n\n

See the MDN article for more information.

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925f2", + "creator": "Adriano Resende", + "createdAt": 1436360741000, + "text": "Waiting for browser any get ECMAScript 6", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925f4", + "creator": "harryg", + "createdAt": 1436441510000, + "text": "What would Babel transpile this to?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c1e", + "creator": "Takács Zsolt", + "createdAt": 1447673374000, + "text": "

that solution is work for me in js:

\n\n
function read_file(file, delete_after) {\n    delete_after = delete_after || false;\n    // Code\n}\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925f6", + "creator": "Dmitri Pavlutin", + "createdAt": 1450604017000, + "text": "What happens if delete_after is 0 or null? It will not work correct for these cases.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925f7", + "creator": "Stephan Bijzitter", + "createdAt": 1485944467000, + "text": "@Rutrus: I don't understand anything you just said.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925f9", + "creator": "aashah7", + "createdAt": 1493351454000, + "text": "What about delete_after = delete_after === undefined ? false : delete_after?", + "upvotes": 8150, + "upvoterUsernames": [], + "downvotes": 8150, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c19", + "creator": "Tom Ritter", + "createdAt": 1242936652000, + "text": "

From ES6/ES2015, default parameters are in the language specification.

\n
function read_file(file, delete_after = false) {\n  // Code\n}\n
\n

just works.

\n

Reference: Default Parameters - MDN

\n
\n

Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.

\n
\n

In ES6, you can simulate default named parameters via destructuring:

\n
// the `= {}` below lets you call the function without any parameters\nfunction myFor({ start = 5, end = 1, step = -1 } = {}) { // (A)\n    // Use the variables `start`, `end` and `step` here\n    ···\n}\n\n// sample call using an object\nmyFor({ start: 3, end: 0 });\n\n// also OK\nmyFor();\nmyFor({});\n
\n

Pre ES2015,

\n

There are a lot of ways, but this is my preferred method — it lets you pass in anything you want, including false or null. (typeof null == "object")

\n
function foo(a, b) {\n  a = typeof a !== 'undefined' ? a : 42;\n  b = typeof b !== 'undefined' ? b : 'default_b';\n  ...\n}\n
\n", + "upvotes": 6540, + "upvoterUsernames": [], + "downvotes": 3036, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327f1082fcc3049e925fc", + "creator": "Triang3l", + "createdAt": 1348852232000, + "text": "This is too slow, use a = ((a != null) ? a : 42);.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e925fe", + "creator": "Rick Eyre", + "createdAt": 1374246866000, + "text": "This didn't work for me. If you pass in no value you can't use typeof on the parameter. You have to instead use var === undefined", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92600", + "creator": "Riveascore", + "createdAt": 1435342878000, + "text": "And we don't have to worry about a ending up as a global variable if it wasn't specified in the function call?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92602", + "creator": "Sebi", + "createdAt": 1440636775000, + "text": "@Sebi Also that's how it roughly looks when you compile/preprocess TypeScript function with parameters having a default value.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92603", + "creator": "cregox", + "createdAt": 1448365360000, + "text": "why not simply using the less cryptic if (typeof a === 'undefined') a = 42;?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92605", + "creator": "Diptox", + "createdAt": 1452267386000, + "text": "you can use a = a || 42; it's more easy", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92607", + "creator": "bevanb", + "createdAt": 1471975859000, + "text": "Note that setting defaults in the parameter declaration doesn't currently work in mobile Safari or Chrome.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e92609", + "creator": "Sarath S Nair", + "createdAt": 1485749935000, + "text": "You can simply write function foo(a, b) { a = a || 42; b = b || 'default_b'; ... }", + "upvotes": 185, + "upvoterUsernames": [], + "downvotes": 185, + "downvoterUsernames": [] + }, + { + "_id": "62f327f1082fcc3049e9260b", + "creator": "Taco タコス", + "createdAt": 1573004266000, + "text": "+1 This particular Q&A is a perfect example of the fact that the majority of developers will come to SO before anywhere else. :)", + "upvotes": 173, + "upvoterUsernames": [], + "downvotes": 173, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c21", + "creator": "Steven Johnston", + "createdAt": 1470235754000, + "text": "

To anyone interested in having there code work in Microsoft Edge, do not use defaults in function parameters.

\n\n
function read_file(file, delete_after = false) {\n    #code\n}\n
\n\n

In that example Edge will throw an error \"Expecting ')'\"

\n\n

To get around this use

\n\n
function read_file(file, delete_after) {\n  if(delete_after == undefined)\n  {\n    delete_after = false;\n  }\n  #code\n}\n
\n\n

As of Aug 08 2016 this is still an issue

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c20", + "creator": "Muhammad Awais", + "createdAt": 1468929571000, + "text": "

Yes, This will work in Javascript. You can also do that:

\n\n
function func(a=10,b=20)\n{\n    alert (a+' and '+b);\n}\n\nfunc(); // Result: 10 and 20\n\nfunc(12); // Result: 12 and 20\n\nfunc(22,25); // Result: 22 and 25\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c22", + "creator": "Thalaivar", + "createdAt": 1476554294000, + "text": "

Default Parameter Values

\n\n

With ES6, you can do perhaps one of the most common idioms in JavaScript relates to setting a default value for a function parameter. The way we’ve done this for years should look quite familiar:

\n\n
function foo(x,y) {\n x = x || 11;\n y = y || 31;\n console.log( x + y );\n}\nfoo(); // 42\nfoo( 5, 6 ); // 11\nfoo( 5 ); // 36\nfoo( null, 6 ); // 17\n
\n\n

This pattern is most used, but is dangerous when we pass values like

\n\n
foo(0, 42)\nfoo( 0, 42 ); // 53 <-- Oops, not 42\n
\n\n

Why? Because the 0 is falsy, and so the x || 11 results in 11, not the directly passed in 0. To fix this gotcha, some people will instead write the check more verbosely like this:

\n\n
function foo(x,y) {\n x = (x !== undefined) ? x : 11;\n y = (y !== undefined) ? y : 31;\n console.log( x + y );\n}\nfoo( 0, 42 ); // 42\nfoo( undefined, 6 ); // 17\n
\n\n

we can now examine a nice helpful syntax added as of ES6 to streamline the assignment of default values to missing arguments:

\n\n
function foo(x = 11, y = 31) {\n console.log( x + y );\n}\n\nfoo(); // 42\nfoo( 5, 6 ); // 11\nfoo( 0, 42 ); // 42\nfoo( 5 ); // 36\nfoo( 5, undefined ); // 36 <-- `undefined` is missing\nfoo( 5, null ); // 5 <-- null coerces to `0`\nfoo( undefined, 6 ); // 17 <-- `undefined` is missing\nfoo( null, 6 ); // 6 <-- null coerces to `0`\n
\n\n

x = 11 in a function declaration is more like x !== undefined ? x : 11 than the much more common idiom x || 11

\n\n

Default Value Expressions

\n\n

Function default values can be more than just simple values like 31; they can be any valid expression, even a function call:

\n\n
function bar(val) {\n console.log( \"bar called!\" );\n return y + val;\n}\nfunction foo(x = y + 3, z = bar( x )) {\n console.log( x, z );\n}\nvar y = 5;\nfoo(); // \"bar called\"\n // 8 13\nfoo( 10 ); // \"bar called\"\n // 10 15\ny = 6;\nfoo( undefined, 10 ); // 9 10\n
\n\n

As you can see, the default value expressions are lazily evaluated, meaning they’re only run if and when they’re needed — that is, when a parameter’s argument is omitted or is undefined.

\n\n

A default value expression can even be an inline function expression call — commonly referred to as an Immediately Invoked Function Expression (IIFE):

\n\n
function foo( x =\n (function(v){ return v + 11; })( 31 )\n) {\n console.log( x );\n}\nfoo(); // 42\n
\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c23", + "creator": "Sachin Kumar", + "createdAt": 1508100050000, + "text": "

As per the syntax

\n\n
function [name]([param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]]) {\n   statements\n}\n
\n\n

you can define the default value of formal parameters.\nand also check undefined value by using typeof function.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c25", + "creator": "Tính Ngô Quang", + "createdAt": 1528448111000, + "text": "
function helloWorld(name, symbol = '!!!') {\n    name = name || 'worlds';\n    console.log('hello ' + name + symbol);\n}\n\nhelloWorld(); // hello worlds!!!\n\nhelloWorld('john'); // hello john!!!\n\nhelloWorld('john', '(>.<)'); // hello john(>.<)\n\nhelloWorld('john', undefined); // hello john!!!\n\nhelloWorld(undefined, undefined); // hello worlds!!!\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c24", + "creator": "Doug Coburn", + "createdAt": 1508106982000, + "text": "

I would highly recommend extreme caution when using default parameter values in javascript. It often creates bugs when used in conjunction with higher order functions like forEach, map, and reduce. For example, consider this line of code:

\n\n
['1', '2', '3'].map(parseInt); // [1, NaN, NaN]\n
\n\n

parseInt has an optional second parameter function parseInt(s, [radix=10]) but map calls parseInt with three arguments: (element, index, and array).

\n\n

I suggest you separate your required parameters form your optional/default valued arguments. If your function takes 1,2, or 3 required parameters for which no default value makes sense, make them positional parameters to the function, any optional parameters should follow as named attributes of a single object. If your function takes 4 or more, perhaps it makes more sense to supply all arguments via attributes of a single object parameter.

\n\n

In your case I would suggest you write your deleteFile function like this: (edited per instead's comments)...

\n\n

\r\n
\r\n
// unsafe\r\nfunction read_file(fileName, deleteAfter=false) {\r\n    if (deleteAfter) {\r\n        console.log(`Reading and then deleting ${fileName}`);\r\n    } else {\r\n        console.log(`Just reading ${fileName}`);\r\n    }\r\n}\r\n\r\n// better\r\nfunction readFile(fileName, options) {\r\n  const deleteAfter = !!(options && options.deleteAfter === true);\r\n  read_file(fileName, deleteAfter);\r\n}\r\n\r\nconsole.log('unsafe...');\r\n['log1.txt', 'log2.txt', 'log3.txt'].map(read_file);\r\n\r\nconsole.log('better...');\r\n['log1.txt', 'log2.txt', 'log3.txt'].map(readFile);
\r\n
\r\n
\r\n

\n\n

Running the above snippet illustrates the dangers lurking behind default argument values for unused parameters.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c27", + "creator": "BlackBeard", + "createdAt": 1530791690000, + "text": "

Use this if you want to use latest ECMA6 syntax:

\n\n

\r\n
\r\n
function myFunction(someValue = \"This is DEFAULT!\") {\r\n  console.log(\"someValue --> \", someValue);\r\n}\r\n\r\nmyFunction(\"Not A default value\") // calling the function without default value\r\nmyFunction()  // calling the function with default value
\r\n
\r\n
\r\n

\n\n

It is called default function parameters. It allows formal parameters to be initialized with default values if no value or undefined is passed.\nNOTE: It wont work with Internet Explorer or older browsers.

\n\n

For maximum possible compatibility use this:

\n\n

\r\n
\r\n
function myFunction(someValue) {\r\n  someValue = (someValue === undefined) ? \"This is DEFAULT!\" : someValue;\r\n  console.log(\"someValue --> \", someValue);\r\n}\r\n\r\nmyFunction(\"Not A default value\") // calling the function without default value\r\nmyFunction()  // calling the function with default value
\r\n
\r\n
\r\n

\n\n

Both functions have exact same behavior as each of these example rely on the fact that the parameter variable will be undefined if no parameter value was passed when calling that function.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f2082fcc3049e92611", + "creator": "MagicLAMP", + "createdAt": 1542335481000, + "text": "NOTE: as @BlackBeard says, function(param=value) WONT work with IE. Use the compatible version.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c9082fcc3049e90c26", + "creator": "dutta", + "createdAt": 1530435053000, + "text": "

\r\n
\r\n
function throwIfNoValue() {\r\nthrow new Error('Missing argument');\r\n}\r\nfunction foo(argValue = throwIfNoValue()) {\r\nreturn argValue ;\r\n}
\r\n
\r\n
\r\n

\n\n

Here foo() is a function which has a parameter named argValue. If we don’t pass anything in the function call here, then the function throwIfNoValue() will be called and the returned result will be assigned to the only argument argValue. This is how a function call can be used as a default parameter. Which makes the code more simplified and readable.

\n\n

This example has been taken from here

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c28", + "creator": "Willem van der Veen", + "createdAt": 1533743317000, + "text": "

If you are using ES6+ you can set default parameters in the following manner:

\n\n

\r\n
\r\n
function test (foo = 1, bar = 2) {\r\n  console.log(foo, bar);\r\n}\r\n\r\ntest(5); // foo gets overwritten, bar remains default parameter
\r\n
\r\n
\r\n

\n\n

If you need ES5 syntax you can do it in the following manner:

\n\n

\r\n
\r\n
function test(foo, bar) {\r\n  foo = foo || 2;\r\n  bar = bar || 0;\r\n  \r\n  console.log(foo, bar);\r\n}\r\n\r\ntest(5); // foo gets overwritten, bar remains default parameter
\r\n
\r\n
\r\n

\n\n

In the above syntax the OR operator is used. The OR operator always returns the first value if this can be converted to true if not it returns the righthandside value. When the function is called with no corresponding argument the parameter variable (bar in our example) is set to undefined by the JS engine. undefined Is then converted to false and thus does the OR operator return the value 0.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c29", + "creator": "Angel Politis", + "createdAt": 1534051572000, + "text": "

ES6: As already mentioned in most answers, in ES6, you can simply initialise a parameter along with a value.

\n\n
\n\n

ES5: Most of the given answers aren't good enough for me because there are occasions where I may have to pass falsey values such as 0, null and undefined to a function. To determine if a parameter is undefined because that's the value I passed instead of undefined due to not have been defined at all I do this:

\n\n
function foo (param1, param2) {\n   param1 = arguments.length >= 1 ? param1 : \"default1\";\n   param2 = arguments.length >= 2 ? param2 : \"default2\";\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2a", + "creator": "Muhammad Rizwan", + "createdAt": 1537830175000, + "text": "
def read_file(file, delete_after = false)\n  # code\nend\n
\n\n

Following code may work in this situation including ECMAScript 6 (ES6) as well as earlier versions.

\n\n

\r\n
\r\n
function read_file(file, delete_after) {\r\n    if(delete_after == undefined)\r\n        delete_after = false;//default value\r\n\r\n    console.log('delete_after =',delete_after);\r\n}\r\nread_file('text1.txt',true);\r\nread_file('text2.txt');
\r\n
\r\n
\r\n

\n\n

as default value in languages works when the function's parameter value is skipped when calling, in JavaScript it is assigned to undefined. This approach doesn't look attractive programmatically but have backward compatibility.

\n", + "upvotes": 177, + "upvoterUsernames": [], + "downvotes": 177, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2b", + "creator": "Akrion", + "createdAt": 1539155099000, + "text": "

If for some reason you are not on ES6 and are using lodash here is a concise way to default function parameters via _.defaultTo method:

\n\n

\r\n
\r\n
var fn = function(a, b) {\r\n  a = _.defaultTo(a, 'Hi')\r\n  b = _.defaultTo(b, 'Mom!')\r\n\r\n  console.log(a, b)\r\n}\r\n\r\nfn()                 // Hi Mom!\r\nfn(undefined, null)  // Hi Mom!\r\nfn(NaN, NaN)         // Hi Mom!\r\nfn(1)                // 1 \"Mom!\"\r\nfn(null, 2)          // Hi 2\r\nfn(false, false)     // false false\r\nfn(0, 2)             // 0 2
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js\"></script>
\r\n
\r\n
\r\n

\n\n

Which will set the default if the current value is NaN, null, or undefined

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2c", + "creator": "Alireza", + "createdAt": 1547796541000, + "text": "

Yes, using default parameters is fully supported in ES6:

\n\n
function read_file(file, delete_after = false) {\n  // Code\n}\n
\n\n

or

\n\n
const read_file = (file, delete_after = false) => {\n    // Code\n}\n
\n\n

but prior in ES5 you could easily do this:

\n\n
function read_file(file, delete_after) {\n  var df = delete_after || false;\n  // Code\n}\n
\n\n

Which means if the value is there, use the value, otherwise, use the second value after || operation which does the same thing...

\n\n

Note: also there is a big difference between those if you pass a value to ES6 one even the value be falsy, that will be replaced with new value, something like null or \"\"... but ES5 one only will be replaced if only the passed value is truthy, that's because the way || working...

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2d", + "creator": "Aman Pandey", + "createdAt": 1560417975000, + "text": "

Just a different approach to set default params is to use object map of arguments, instead of arguments directly.\nFor example,

\n\n
const defaultConfig = {\n category: 'Animals',\n legs: 4\n};\n\nfunction checkOrganism(props) {\n const category = props.category || defaultConfig.category;\n const legs = props.legs || defaultConfig.legs;\n}\n
\n\n

This way, it's easy to extend the arguments and not worry about argument length mismatch.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2e", + "creator": "new QOpenGLWidget", + "createdAt": 1563294400000, + "text": "

The answer is yes. In fact, there are many languages who support default parameters. Python is one of them:

\n\n
def(a, enter=\"Hello\"):\n   print(a+enter)\n
\n\n

Even though this is Python 3 code due to the parentheses, default parameters in functions also work in JS.

\n\n

For example, and in your case:

\n\n

\r\n
\r\n
function read_file(file, deleteAfter=false){\r\n  console.log(deleteAfter);\r\n}\r\n\r\nread_file(\"test.txt\");
\r\n
\r\n
\r\n

\n\n

But sometimes you don't really need default parameters.

\n\n

You can just define the variable right after the start of the function, like this:

\n\n

\r\n
\r\n
function read_file(file){\r\n  var deleteAfter = false;\r\n  console.log(deleteAfter);\r\n}\r\n\r\nread_file(\"test.txt\");
\r\n
\r\n
\r\n

\n\n

In both of my examples, it returns the same thing. But sometimes they actually could be useful, like in very advanced projects.

\n\n

So, in conclusion, default parameter values can be used in JS. But it is almost the same thing as defining a variable right after the start of the function. However, sometimes they are still very useful. As you have may noticed, default parameter values take 1 less line of code than the standard way which is defining the parameter right after the start of the function.

\n\n

EDIT: And this is super important! This will not work in IE. See documentation. So with IE you have to use the \"define variable at top of function\" method. Default parameters won't work in IE.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c2f", + "creator": "Shivang Gupta", + "createdAt": 1576744744000, + "text": "

Just to showcase my skills too (lol), above function can written even without having named arguments as below:

\n\n

ES5 and above

\n\n
function foo() {\n    a = typeof arguments[0] !== 'undefined' ? a : 42;\n    b = typeof arguments[1] !== 'undefined' ? b : 'default_b';\n    ...\n}\n
\n\n

ES6 and above

\n\n
function foo(...rest) {\n    a = typeof rest[0] !== 'undefined' ? a : 42;\n    b = typeof rest[1] !== 'undefined' ? b : 'default_b';\n    ...\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c30", + "creator": "Kamil Kiełczewski", + "createdAt": 1593527404000, + "text": "

Yes - proof:

\n

\r\n
\r\n
function read_file(file, delete_after = false) {\n  // Code\n  console.log({file,delete_after});\n}\n\n\n\n// TEST\nread_file(\"A\");\nread_file(\"B\",true);\nread_file(\"C\",false);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c31", + "creator": "maxxioso", + "createdAt": 1620066612000, + "text": "

I've noticed a few answers mentioning that using default params isn't portable to other browsers, but it's only fair to point out that you can use transpilers like Babel to convert your code into ES5 syntax for browsers that have limited support for modern JS features.

\n

So this:

\n
function read_file(file, delete_after = false) {\n  // Code\n}\n
\n

would be transpiled as this (try it out in the Babel REPL -> https://babeljs.io/repl/):

\n
"use strict";\n\nfunction read_file(file) {\n\n  var delete_after =\n    arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n  \n  //Code...\n\n}\n
\n

Of course, if you have no intention of using transpilation, then setting default params in the body of the function like others have demonstrated is perfectly fine as well.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c9082fcc3049e90c32", + "creator": "Engr.Aftab Ufaq", + "createdAt": 1627301579000, + "text": "
export const getfilesize = (bytes, decimals = 2) => {\n    if (bytes === 0){ \n        return '0 Bytes';\n    }else{\n        const k = 1024;\n        const dm = decimals < 0 ? 0 : decimals;\n        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n        const i = Math.floor(Math.log(bytes) / Math.log(k));\n        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];\n\n    }\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f3082fcc3049e9261a", + "creator": "ggorlen", + "createdAt": 1655498332000, + "text": "This doesn't really add anything new to the existing answers.", + "upvotes": 7493, + "upvoterUsernames": [], + "downvotes": 7493, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1405323, + "uvac": 1405350 + } + }, + { + "_id": "62f321bb082fcc3049e8feea", + "title": "How can I get query string values in JavaScript?", + "title-lowercase": "how can i get query string values in javascript?", + "creator": "Subliminal Hash", + "createdAt": 1243066248000, + "status": "open", + "text": "

Is there a plugin-less way of retrieving query string values via jQuery (or without)?

\n\n

If so, how? If not, is there a plugin which can do so?

\n", + "upvotes": 4926, + "upvoterUsernames": [], + "downvotes": 2231, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 4752944, + "answers": 66, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90b4b", + "creator": "alex", + "createdAt": 1243066480000, + "text": "

If you're using jQuery, you can use a library, such as jQuery BBQ: Back Button & Query Library.

\n\n
\n

...jQuery BBQ provides a full .deparam() method, along with both hash state management, and fragment / query string parse and merge utility methods.

\n
\n\n

Edit: Adding Deparam Example:

\n\n

\r\n
\r\n
 var DeparamExample = function() {\r\n            var params = $.deparam.querystring();\r\n\r\n            //nameofparam is the name of a param from url\r\n            //code below will get param if ajax refresh with hash\r\n            if (typeof params.nameofparam == 'undefined') {\r\n                params = jQuery.deparam.fragment(window.location.href);\r\n            }\r\n            \r\n            if (typeof params.nameofparam != 'undefined') {\r\n                var paramValue = params.nameofparam.toString();\r\n                  \r\n            }\r\n        };
\r\n
\r\n
\r\n

\n\n

If you want to just use plain JavaScript, you could use...

\n\n
var getParamValue = (function() {\n    var params;\n    var resetParams = function() {\n            var query = window.location.search;\n            var regex = /[?&;](.+?)=([^&;]+)/g;\n            var match;\n\n            params = {};\n\n            if (query) {\n                while (match = regex.exec(query)) {\n                    params[match[1]] = decodeURIComponent(match[2]);\n                }\n            }    \n        };\n\n    window.addEventListener\n    && window.addEventListener('popstate', resetParams);\n\n    resetParams();\n\n    return function(param) {\n        return params.hasOwnProperty(param) ? params[param] : null;\n    }\n\n})();​\n
\n\n

Because of the new HTML History API and specifically history.pushState() and history.replaceState(), the URL can change which will invalidate the cache of parameters and their values.

\n\n

This version will update its internal cache of parameters each time the history changes.

\n", + "upvotes": 280, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b4c", + "creator": "brandonjp", + "createdAt": 1269028328000, + "text": "

Roshambo on snipplr.com has a simple script to achieve this described in Get URL Parameters with jQuery | Improved. With his script you also easily get to pull out just the parameters you want.

\n

Here's the gist:

\n
$.urlParam = function(name, url) {\n    if (!url) {\n     url = window.location.href;\n    }\n    var results = new RegExp('[\\\\?&]' + name + '=([^&#]*)').exec(url);\n    if (!results) {\n        return undefined;\n    }\n    return results[1] || undefined;\n}\n
\n

Then just get your parameters from the query string.

\n

So if the URL/query string was xyz.example/index.html?lang=de.

\n

Just call var langval = $.urlParam('lang');, and you've got it.

\n

UZBEKJON has a great blog post on this as well, Get URL parameters & values with jQuery.

\n", + "upvotes": 355, + "upvoterUsernames": [], + "downvotes": 132, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b4d", + "creator": "Jet", + "createdAt": 1278794892000, + "text": "
function GET() {\n        var data = [];\n        for(x = 0; x < arguments.length; ++x)\n            data.push(location.href.match(new RegExp(\"/\\?\".concat(arguments[x],\"=\",\"([^\\n&]*)\")))[1])\n                return data;\n    }\n\n\nexample:\ndata = GET(\"id\",\"name\",\"foo\");\nquery string : ?id=3&name=jet&foo=b\nreturns:\n    data[0] // 3\n    data[1] // jet\n    data[2] // b\nor\n    alert(GET(\"id\")[0]) // return 3\n
\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b4e", + "creator": "Ryan Phelan", + "createdAt": 1286311777000, + "text": "

Here's my stab at making Andy E's excellent solution into a full fledged jQuery plugin:

\n\n
;(function ($) {\n    $.extend({      \n        getQueryString: function (name) {           \n            function parseParams() {\n                var params = {},\n                    e,\n                    a = /\\+/g,  // Regex for replacing addition symbol with a space\n                    r = /([^&=]+)=?([^&]*)/g,\n                    d = function (s) { return decodeURIComponent(s.replace(a, \" \")); },\n                    q = window.location.search.substring(1);\n\n                while (e = r.exec(q))\n                    params[d(e[1])] = d(e[2]);\n\n                return params;\n            }\n\n            if (!this.queryStringParams)\n                this.queryStringParams = parseParams(); \n\n            return this.queryStringParams[name];\n        }\n    });\n})(jQuery);\n
\n\n

The syntax is:

\n\n
var someVar = $.getQueryString('myParam');\n
\n\n

Best of both worlds!

\n", + "upvotes": 129, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b50", + "creator": "shanimal", + "createdAt": 1289911786000, + "text": "

Code golf:

\n\n
var a = location.search&&location.search.substr(1).replace(/\\+/gi,\" \").split(\"&\");\nfor (var i in a) {\n    var s = a[i].split(\"=\");\n    a[i]  = a[unescape(s[0])] = unescape(s[1]);\n}\n
\n\n

Display it!

\n\n
for (i in a) {\n    document.write(i + \":\" + a[i] + \"<br/>\");   \n};\n
\n\n

On my Mac: test.htm?i=can&has=cheezburger displays

\n\n
0:can\n1:cheezburger\ni:can\nhas:cheezburger\n
\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b4f", + "creator": "Vadim", + "createdAt": 1289901308000, + "text": "

Here is my version of query string parsing code on GitHub.

\n\n

It's \"prefixed\" with jquery.*, but the parsing function itself don't use jQuery. It's pretty fast, but still open for few simple performance optimizations.

\n\n

Also it supports list & hash-tables encoding in the URL, like:

\n\n
arr[]=10&arr[]=20&arr[]=100\n
\n\n

or

\n\n
hash[key1]=hello&hash[key2]=moto&a=How%20are%20you\n
\n\n
\n\n
jQuery.toQueryParams = function(str, separator) {\n    separator = separator || '&'\n    var obj = {}\n    if (str.length == 0)\n        return obj\n    var c = str.substr(0,1)\n    var s = c=='?' || c=='#'  ? str.substr(1) : str; \n\n    var a = s.split(separator)\n    for (var i=0; i<a.length; i++) {\n        var p = a[i].indexOf('=')\n        if (p < 0) {\n            obj[a[i]] = ''\n            continue\n        }\n        var k = decodeURIComponent(a[i].substr(0,p)),\n            v = decodeURIComponent(a[i].substr(p+1))\n\n        var bps = k.indexOf('[')\n        if (bps < 0) {\n            obj[k] = v\n            continue;\n        } \n\n        var bpe = k.substr(bps+1).indexOf(']')\n        if (bpe < 0) {\n            obj[k] = v\n            continue;\n        }\n\n        var bpv = k.substr(bps+1, bps+bpe-1)\n        var k = k.substr(0,bps)\n        if (bpv.length <= 0) {\n            if (typeof(obj[k]) != 'object') obj[k] = []\n            obj[k].push(v)\n        } else {\n            if (typeof(obj[k]) != 'object') obj[k] = {}\n            obj[k][bpv] = v\n        }\n    }\n    return obj;\n\n}\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32766082fcc3049e9249b", + "creator": "brauliobo", + "createdAt": 1568916710000, + "text": "nice but doesnt deal with the case filters[topic][]=list", + "upvotes": 4352, + "upvoterUsernames": [], + "downvotes": 4352, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b54", + "creator": "Mic", + "createdAt": 1300097620000, + "text": "

I use regular expressions a lot, but not for that.

\n

It seems easier and more efficient to me to read the query string once in my application, and build an object from all the key/value pairs like:

\n
var search = function() {\n  var s = window.location.search.substr(1),\n    p = s.split(/\\&/), l = p.length, kv, r = {};\n  if (l === 0) {return false;}\n  while (l--) {\n    kv = p[l].split(/\\=/);\n    r[kv[0]] = decodeURIComponent(kv[1] || '') || true;\n  }\n  return r;\n}();\n
\n

For a URL like http://example.com?param1=val1&param2=val2 you can get their value later in your code as search.param1 and search.param2.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b51", + "creator": "Mohammad Arif", + "createdAt": 1292329200000, + "text": "

Roshambo jQuery method wasn't taking care of decode URL

\n\n
\n

http://snipplr.com/view/26662/get-url-parameters-with-jquery--improved/

\n
\n\n

Just added that capability also while adding in the return statement

\n\n
return decodeURIComponent(results[1].replace(/\\+/g, \" \")) || 0;\n
\n\n

Now you can find the updated gist:

\n\n
$.urlParam = function(name){\nvar results = new RegExp('[\\\\?&]' + name + '=([^&#]*)').exec(window.location.href);\nif (!results) { return 0; }\nreturn decodeURIComponent(results[1].replace(/\\+/g, \" \")) || 0;\n}\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b52", + "creator": "Clint", + "createdAt": 1296229404000, + "text": "

This is a function I created a while back and I'm quite happy with. It is not case sensitive - which is handy. Also, if the requested QS doesn't exist, it just returns an empty string.

\n\n

I use a compressed version of this. I'm posting uncompressed for the novice types to better explain what's going on.

\n\n

I'm sure this could be optimized or done differently to work faster, but it's always worked great for what I need.

\n\n

Enjoy.

\n\n
function getQSP(sName, sURL) {\n    var theItmToRtn = \"\";\n    var theSrchStrg = location.search;\n    if (sURL) theSrchStrg = sURL;\n    var sOrig = theSrchStrg;\n    theSrchStrg = theSrchStrg.toUpperCase();\n    sName = sName.toUpperCase();\n    theSrchStrg = theSrchStrg.replace(\"?\", \"&\") theSrchStrg = theSrchStrg + \"&\";\n    var theSrchToken = \"&\" + sName + \"=\";\n    if (theSrchStrg.indexOf(theSrchToken) != -1) {\n        var theSrchTokenLth = theSrchToken.length;\n        var theSrchTokenLocStart = theSrchStrg.indexOf(theSrchToken) + theSrchTokenLth;\n        var theLocOfNextAndSign = theSrchStrg.indexOf(\"&\", theSrchTokenLocStart);\n        theItmToRtn = unescape(sOrig.substring(theSrchTokenLocStart, theLocOfNextAndSign));\n    }\n    return unescape(theItmToRtn);\n}\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b53", + "creator": "James", + "createdAt": 1299001742000, + "text": "

Improved version of Artem Barger's answer:

\n\n
function getParameterByName(name) {\n    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);\n    return match && decodeURIComponent(match[1].replace(/\\+/g, ' '));\n}\n
\n\n

For more information on improvement see: http://james.padolsey.com/javascript/bujs-1-getparameterbyname/

\n", + "upvotes": 1266, + "upvoterUsernames": [], + "downvotes": 589, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b55", + "creator": "AlfaTeK", + "createdAt": 1300202460000, + "text": "

Just another recommendation. The plugin Purl allows to retrieve all parts of URL, including anchor, host, etc.

\n

It can be used with or without jQuery.

\n

Usage is very simple and cool:

\n
var url = $.url('http://example.com/folder/dir/index.html?item=value'); // jQuery version\nvar url = purl('http://example.com/folder/dir/index.html?item=value'); // plain JS version\nurl.attr('protocol'); // returns 'http'\nurl.attr('path'); // returns '/folder/dir/index.html'\n
\n

However, as of Nov 11, 2014, Purl is no longer maintained and the author recommends using URI.js instead. The jQuery plugin is different in that it focuses on elements - for usage with strings, just use URI directly, with or without jQuery. Similar code would look as such, fuller docs here:

\n
var url = new URI('http://example.com/folder/dir/index.html?item=value'); // plain JS version\nurl.protocol(); // returns 'http'\nurl.path(); // returns '/folder/dir/index.html'\n
\n", + "upvotes": 743, + "upvoterUsernames": [], + "downvotes": 344, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b56", + "creator": "Eneko Alonso", + "createdAt": 1304467159000, + "text": "

I would rather use split() instead of Regex for this operation:

\n\n
function getUrlParams() {\n    var result = {};\n    var params = (window.location.search.split('?')[1] || '').split('&');\n    for(var param in params) {\n        if (params.hasOwnProperty(param)) {\n            var paramParts = params[param].split('=');\n            result[paramParts[0]] = decodeURIComponent(paramParts[1] || \"\");\n        }\n    }\n    return result;\n}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32766082fcc3049e924a2", + "creator": "dogonaroof", + "createdAt": 1549626672000, + "text": "I wasn't wanting to use Regex and went with this solution. I find it works rather nicely. Thanks!", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b58", + "creator": "skaurus", + "createdAt": 1313664321000, + "text": "

Here's my edit to this excellent answer - with added ability to parse query strings with keys without values.

\n\n
var url = 'http://sb.com/reg/step1?param';\nvar qs = (function(a) {\n    if (a == \"\") return {};\n    var b = {};\n    for (var i = 0; i < a.length; ++i) {\n        var p=a[i].split('=', 2);\n        if (p[1]) p[1] = decodeURIComponent(p[1].replace(/\\+/g, \" \"));\n        b[p[0]] = p[1];\n    }\n    return b;\n})((url.split('?'))[1].split('&'));\n
\n\n

IMPORTANT! The parameter for that function in the last line is different. It's just an example of how one can pass an arbitrary URL to it. You can use last line from Bruno's answer to parse the current URL.

\n\n

So what exactly changed? With url http://sb.com/reg/step1?param= results will be same. But with url http://sb.com/reg/step1?param Bruno's solution returns an object without keys, while mine returns an object with key param and undefined value.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b57", + "creator": "mynameistechno", + "createdAt": 1306218763000, + "text": "

I took this answer and added support for optionally passing the URL in as a parameter; falls back to window.location.search. Obviously this is useful for getting the query string parameters from URLs that are not the current page:

\n\n
(function($, undef) {\n  $.QueryString = function(url) {\n    var pairs, qs = null, index, map = {};\n    if(url == undef){\n      qs = window.location.search.substr(1);\n    }else{\n      index = url.indexOf('?');\n      if(index == -1) return {};\n      qs = url.substring(index+1);\n    }\n    pairs = qs.split('&');\n    if (pairs == \"\") return {};\n    for (var i = 0; i < pairs.length; ++i)\n    {\n      var p = pairs[i].split('=');\n      if(p.length != 2) continue;\n      map[p[0]] = decodeURIComponent(p[1].replace(/\\+/g, \" \"));\n    }\n    return map;\n  };\n})(jQuery);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b59", + "creator": "Anoop", + "createdAt": 1318790282000, + "text": "

The following code will create an object which has two methods:

\n\n
    \n
  1. isKeyExist: Check if a particular parameter exist
  2. \n
  3. getValue: Get the value of a particular parameter.
  4. \n
\n\n

 

\n\n
var QSParam = new function() {\n       var qsParm = {};\n       var query = window.location.search.substring(1);\n       var params = query.split('&');\n       for (var i = 0; i < params.length; i++) {\n           var pos = params[i].indexOf('=');\n           if (pos > 0) {\n               var key = params[i].substring(0, pos);\n               var val = params[i].substring(pos + 1);\n               qsParm[key] = val;\n           }\n       }\n       this.isKeyExist = function(query){\n           if(qsParm[query]){\n               return true;\n           }\n           else{\n              return false;\n           }\n       };\n       this.getValue = function(query){\n           if(qsParm[query])\n           {\n               return qsParm[query];\n           }\n           throw \"URL does not contain query \"+ query;\n       }\n};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5a", + "creator": "user981090", + "createdAt": 1325723167000, + "text": "

Try this:

\n\n
String.prototype.getValueByKey = function(k){\n    var p = new RegExp('\\\\b'+k+'\\\\b','gi');\n    return this.search(p) != -1 ? decodeURIComponent(this.substr(this.search(p)+k.length+1).substr(0,this.substr(this.search(p)+k.length+1).search(/(&|;|$)/))) : \"\";\n};\n
\n\n

Then call it like so:

\n\n
if(location.search != \"\") location.search.getValueByKey(\"id\");\n
\n\n

You can use this for cookies also:

\n\n
if(navigator.cookieEnabled) document.cookie.getValueByKey(\"username\");\n
\n\n

This only works for strings that have key=value[&|;|$]... will not work on objects/arrays.

\n\n

If you don't want to use String.prototype...\nmove it to a function and pass the string as an argument

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5b", + "creator": "Anatoly Mironov", + "createdAt": 1326238568000, + "text": "

I like Ryan Phelan's solution. But I don't see any point of extending jQuery for that? There is no usage of jQuery functionality.

\n\n

On the other hand, I like the built-in function in Google Chrome: window.location.getParameter.

\n\n

So why not to use this? Okay, other browsers don't have. So let's create this function if it does not exist:

\n\n
if (!window.location.getParameter ) {\n  window.location.getParameter = function(key) {\n    function parseParams() {\n        var params = {},\n            e,\n            a = /\\+/g,  // Regex for replacing addition symbol with a space\n            r = /([^&=]+)=?([^&]*)/g,\n            d = function (s) { return decodeURIComponent(s.replace(a, \" \")); },\n            q = window.location.search.substring(1);\n\n        while (e = r.exec(q))\n            params[d(e[1])] = d(e[2]);\n\n        return params;\n    }\n\n    if (!this.queryStringParams)\n        this.queryStringParams = parseParams(); \n\n    return this.queryStringParams[key];\n  };\n}\n
\n\n

This function is more or less from Ryan Phelan, but it is wrapped differently: clear name and no dependencies of other javascript libraries. More about this function on my blog.

\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5c", + "creator": "alkos333", + "createdAt": 1328727198000, + "text": "
function getUrlVar(key){\n    var result = new RegExp(key + \"=([^&]*)\", \"i\").exec(window.location.search); \n    return result && unescape(result[1]) || \"\"; \n}\n
\n\n

https://gist.github.com/1771618

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5d", + "creator": "Sagiv Ofek", + "createdAt": 1329746400000, + "text": "

Keep it simple in plain JavaScript code:

\n\n
function qs(key) {\n    var vars = [], hash;\n    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');\n    for(var i = 0; i < hashes.length; i++)\n    {\n        hash = hashes[i].split('=');\n        vars.push(hash[0]);\n        vars[hash[0]] = hash[1];\n    }\n    return vars[key];\n}\n
\n\n

Call it from anywhere in the JavaScript code:

\n\n
var result = qs('someKey');\n
\n", + "upvotes": 91, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5e", + "creator": "rodneyrehm", + "createdAt": 1329747481000, + "text": "

If you're doing more URL manipulation than simply parsing the querystring, you may find URI.js helpful. It is a library for manipulating URLs - and comes with all the bells and whistles. (Sorry for self-advertising here)

\n\n

to convert your querystring into a map:

\n\n
var data = URI('?foo=bar&bar=baz&foo=world').query(true);\ndata == {\n  \"foo\": [\"bar\", \"world\"],\n  \"bar\": \"baz\"\n}\n
\n\n

(URI.js also \"fixes\" bad querystrings like ?&foo&&bar=baz& to ?foo&bar=baz)

\n", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b5f", + "creator": "BMitch", + "createdAt": 1329943259000, + "text": "

Here's my own take on this. This first function decodes a URL string into an object of name/value pairs:

\n\n
url_args_decode = function (url) {\n  var args_enc, el, i, nameval, ret;\n  ret = {};\n  // use the DOM to parse the URL via an 'a' element\n  el = document.createElement(\"a\");\n  el.href = url;\n  // strip off initial ? on search and split\n  args_enc = el.search.substring(1).split('&');\n  for (i = 0; i < args_enc.length; i++) {\n    // convert + into space, split on =, and then decode \n    args_enc[i].replace(/\\+/g, ' ');\n    nameval = args_enc[i].split('=', 2);\n    ret[decodeURIComponent(nameval[0])]=decodeURIComponent(nameval[1]);\n  }\n  return ret;\n};\n
\n\n

And as an added bonus, if you change some of the args, you can use this second function to put the array of args back into the URL string:

\n\n
url_args_replace = function (url, args) {\n  var args_enc, el, name;\n  // use the DOM to parse the URL via an 'a' element\n  el = document.createElement(\"a\");\n  el.href = url;\n  args_enc = [];\n  // encode args to go into url\n  for (name in args) {\n    if (args.hasOwnProperty(name)) {\n      name = encodeURIComponent(name);\n      args[name] = encodeURIComponent(args[name]);\n      args_enc.push(name + '=' + args[name]);\n    }\n  }\n  if (args_enc.length > 0) {\n    el.search = '?' + args_enc.join('&');\n  } else {\n    el.search = '';\n  }\n  return el.href;\n};\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32767082fcc3049e924ab", + "creator": "adardesign", + "createdAt": 1346161888000, + "text": "If your anyway using the jQuery, Then the later method (url_args_replace) can be used via $.param()", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32767082fcc3049e924ad", + "creator": "greg", + "createdAt": 1366251009000, + "text": "Why do you use document.createElement("a") ?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b60", + "creator": "vol7ron", + "createdAt": 1333746858000, + "text": "
http://someurl.com?key=value&keynovalue&keyemptyvalue=&&keynovalue=nowhasvalue#somehash\n
\n\n\n\n

Code:

\n\n\n\n

How to Call:

\n\n\n\n

Output:

\n\n\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b63", + "creator": "Tomamais", + "createdAt": 1343334545000, + "text": "

I like this one (taken from jquery-howto.blogspot.co.uk):

\n\n
// get an array with all querystring values\n// example: var valor = getUrlVars()[\"valor\"];\nfunction getUrlVars() {\n    var vars = [], hash;\n    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');\n    for (var i = 0; i < hashes.length; i++) {\n        hash = hashes[i].split('=');\n        vars.push(hash[0]);\n        vars[hash[0]] = hash[1];\n    }\n    return vars;\n}\n
\n\n

Works great for me.

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b62", + "creator": "Imdad", + "createdAt": 1341220193000, + "text": "

The following function returns an object version of your queryString. \nYou can simply write obj.key1 and obj.key2 to access values of key1 and key2 in parameter.

\n\n
function getQueryStringObject()\n{\n    var querystring = document.location.search.replace('?','').split( '&' );\n    var objQueryString={};\n    var key=\"\",val=\"\";\n    if(typeof querystring == 'undefined')\n    {\n        return (typeof querystring);\n    }\n    for(i=0;i<querystring.length;i++)\n    {\n        key=querystring[i].split(\"=\")[0];\n        val=querystring[i].split(\"=\")[1];\n        objQueryString[key] = val;\n    }\n    return objQueryString;\n}\n
\n\n

And to use this function you can write

\n\n
var obj= getQueryStringObject();\nalert(obj.key1);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b64", + "creator": "Paul Sweatte", + "createdAt": 1346113661000, + "text": "

URLSearchParams

\n

Firefox 44+, Opera 36+, Edge 17+, Safari 10.3+ and Chrome 49+ support the URLSearchParams API:

\n\n

There is a google-suggested URLSearchParams polyfill for the stable versions of IE.

\n

It is not standardized by W3C, but it is a living standard by WhatWG.

\n

You can use it on location:

\n
const params = new URLSearchParams(location.search);\n
\n

or

\n
const params = (new URL(location)).searchParams;\n
\n

Or of course on any URL:

\n
const url = new URL('https://example.com?foo=1&bar=2');\nconst params = new URLSearchParams(url.search);\n
\n

You can get params also using a shorthand .searchParams property on the URL object, like this:

\n
const params = new URL('https://example.com?foo=1&bar=2').searchParams;\nparams.get('foo'); // "1"\nparams.get('bar'); // "2" \n
\n

You read/set parameters through the get(KEY), set(KEY, VALUE), append(KEY, VALUE) API. You can also iterate over all values for (let p of params) {}.

\n

A reference implementation and a sample page are available for auditing and testing.

\n", + "upvotes": 1121, + "upvoterUsernames": [], + "downvotes": 438, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32768082fcc3049e924b1", + "creator": "chri3g91", + "createdAt": 1644416208000, + "text": "const params = new URL(location.href).searchParams; to not rely on static text for the url", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32768082fcc3049e924b3", + "creator": "luke_mclachlan", + "createdAt": 1649924345000, + "text": "@chri3g91 in fact, as the answers shows, you can use const params = new URL(location).searchParams;", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b61", + "creator": "IT ppl", + "createdAt": 1337847520000, + "text": "

This one works fine. Regular expressions in some of the other answers introduce unnecessary overhead.

\n
function getQuerystring(key) {\n    var query = window.location.search.substring(1);\n    var vars = query.split("&");\n    for (var i = 0; i < vars.length; i++) {\n        var pair = vars[i].split("=");\n        if (pair[0] == key) {\n            return pair[1];\n        }\n    }\n}\n
\n

taken from here

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32768082fcc3049e924b5", + "creator": "IT ppl", + "createdAt": 1337851328000, + "text": "@Rup you are right...actually in my code it'll always be a number rather than any special characters, so missed ...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32768082fcc3049e924b7", + "creator": "Roc Boronat", + "createdAt": 1574646676000, + "text": "Thanks for it! It's the only method supported by the Kindle's Experimental Browser. Freak project here! :·)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b66", + "creator": "Martin Borthiry", + "createdAt": 1346706521000, + "text": "

Just use two splits:

\n\n
function get(n) {\n    var half = location.search.split(n + '=')[1];\n    return half !== undefined ? decodeURIComponent(half.split('&')[0]) : null;\n}\n
\n\n

I was reading all the previous and more complete answers. But I think that is the simplest and faster method. You can check in this jsPerf benchmark

\n\n

To solve the problem in Rup's comment, add a conditional split by changing the first line to the two below. But absolute accuracy means it's now slower than regexp (see jsPerf).

\n\n
function get(n) {\n    var half = location.search.split('&' + n + '=')[1];\n    if (!half) half = location.search.split('?' + n + '=')[1];\n    return half !== undefined ? decodeURIComponent(half.split('&')[0]) : null;\n}\n
\n\n

So if you know you won't run into Rup's counter-case, this wins. Otherwise, regexp.

\n\n
\n

Or if you have control of the querystring and can guarantee that a value you are trying to get will never contain any URL encoded\n characters (having these in a value would be a bad idea) - you can use\n the following slightly more simplified and readable version of the 1st option:

\n\n
    function getQueryStringValueByName(name) {\n        var queryStringFromStartOfValue = location.search.split(name + '=')[1];\n         return queryStringFromStartOfValue !== undefined ? queryStringFromStartOfValue.split('&')[0] : null;\n
\n
\n", + "upvotes": 164, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b68", + "creator": "Rob", + "createdAt": 1351715505000, + "text": "

There is a nice little url utility for this with some cool sugaring:

\n\n
http://www.example.com/path/index.html?silly=willy#chucky=cheese\n\nurl();            // http://www.example.com/path/index.html?silly=willy#chucky=cheese\nurl('domain');    // example.com\nurl('1');         // path\nurl('-1');        // index.html\nurl('?');         // silly=willy\nurl('?silly');    // willy\nurl('?poo');      // (an empty string)\nurl('#');         // chucky=cheese\nurl('#chucky');   // cheese\nurl('#poo');      // (an empty string)\n
\n\n

Check out more examples and download here: https://github.com/websanova/js-url#url

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b67", + "creator": "Albireo", + "createdAt": 1351240196000, + "text": "\n\n

This function converts the querystring to a JSON-like object, it also handles value-less and multi-value parameters:

\n\n
\"use strict\";\nfunction getQuerystringData(name) {\n    var data = { };\n    var parameters = window.location.search.substring(1).split(\"&\");\n    for (var i = 0, j = parameters.length; i < j; i++) {\n        var parameter = parameters[i].split(\"=\");\n        var parameterName = decodeURIComponent(parameter[0]);\n        var parameterValue = typeof parameter[1] === \"undefined\" ? parameter[1] : decodeURIComponent(parameter[1]);\n        var dataType = typeof data[parameterName];\n        if (dataType === \"undefined\") {\n            data[parameterName] = parameterValue;\n        } else if (dataType === \"array\") {\n            data[parameterName].push(parameterValue);\n        } else {\n            data[parameterName] = [data[parameterName]];\n            data[parameterName].push(parameterValue);\n        }\n    }\n    return typeof name === \"string\" ? data[name] : data;\n}\n
\n\n

We perform a check for undefined on parameter[1] because decodeURIComponent returns the string \"undefined\" if the variable is undefined, and that's wrong.

\n\n

Usage:

\n\n
\"use strict\";\nvar data = getQuerystringData();\nvar parameterValue = getQuerystringData(\"parameterName\");\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b65", + "creator": "Paul", + "createdAt": 1346331615000, + "text": "

Here is a fast way to get an object similar to the PHP $_GET array:

\n\n
function get_query(){\n    var url = location.search;\n    var qs = url.substring(url.indexOf('?') + 1).split('&');\n    for(var i = 0, result = {}; i < qs.length; i++){\n        qs[i] = qs[i].split('=');\n        result[qs[i][0]] = decodeURIComponent(qs[i][1]);\n    }\n    return result;\n}\n
\n\n

Usage:

\n\n
var $_GET = get_query();\n
\n\n

For the query string x=5&y&z=hello&x=6 this returns the object:

\n\n
{\n  x: \"6\",\n  y: undefined,\n  z: \"hello\"\n}\n
\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b69", + "creator": "Anoop", + "createdAt": 1353003313000, + "text": "

One-liner to get the query:

\n\n
var value = location.search.match(new RegExp(key + \"=(.*?)($|\\&)\", \"i\"))[1];\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6a", + "creator": "Code Spy", + "createdAt": 1359091794000, + "text": "

This the most simple and small function JavaScript to get int ans String parameter value from URL

\n\n
/* THIS FUNCTION IS TO FETCH INT PARAMETER VALUES */\n\nfunction getParameterint(param) {\n            var val = document.URL;\n            var url = val.substr(val.indexOf(param))  \n            var n=parseInt(url.replace(param+\"=\",\"\"));\n            alert(n); \n}\ngetParameteraint(\"page\");\ngetParameteraint(\"pagee\");\n\n/*THIS FUNCTION IS TO FETCH STRING PARAMETER*/\nfunction getParameterstr(param) {\n            var val = document.URL;\n            var url = val.substr(val.indexOf(param))  \n            var n=url.replace(param+\"=\",\"\");\n            alert(n); \n}\ngetParameterstr(\"str\");\n
\n\n

Source And DEMO : http://bloggerplugnplay.blogspot.in/2012/08/how-to-get-url-parameter-in-javascript.html

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6b", + "creator": "Nijikokun", + "createdAt": 1359146140000, + "text": "

I developed a small library using techniques listed here to create an easy to use, drop-in solution to anyones troubles; It can be found here:

\n\n

https://github.com/Nijikokun/query-js

\n\n

Usage

\n\n

Fetching specific parameter/key:

\n\n
query.get('param');\n
\n\n

Using the builder to fetch the entire object:

\n\n
var storage = query.build();\nconsole.log(storage.param);\n
\n\n

and tons more... check the github link for more examples.

\n\n

Features

\n\n
    \n
  1. Caching on both decoding and parameters
  2. \n
  3. Supports hash query strings #hello?page=3
  4. \n
  5. Supports passing custom queries
  6. \n
  7. Supports Array / Object Parameters user[]=\"jim\"&user[]=\"bob\"
  8. \n
  9. Supports empty management &&
  10. \n
  11. Supports declaration parameters without values name&hello=\"world\"
  12. \n
  13. Supports repeated parameters param=1&param=2
  14. \n
  15. Clean, compact, and readable source 4kb
  16. \n
  17. AMD, Require, Node support
  18. \n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6e", + "creator": "tim", + "createdAt": 1360876849000, + "text": "

I needed an object from the query string, and I hate lots of code. It may not be the most robust in the universe, but it's just a few lines of code.

\n\n
var q = {};\nlocation.href.split('?')[1].split('&').forEach(function(i){\n    q[i.split('=')[0]]=i.split('=')[1];\n});\n
\n\n

A URL like this.htm?hello=world&foo=bar will create:

\n\n
{hello:'world', foo:'bar'}\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6c", + "creator": "gewel", + "createdAt": 1360141782000, + "text": "
function GetQueryStringParams(sParam)\n{\n    var sPageURL = window.location.search.substring(1);\n    var sURLVariables = sPageURL.split('&');\n\n    for (var i = 0; i < sURLVariables.length; i++)\n    {\n        var sParameterName = sURLVariables[i].split('=');\n        if (sParameterName[0] == sParam)\n        {\n            return sParameterName[1];\n        }\n    }\n}​\n
\n

And this is how you can use this function assuming the URL is

\n
\n

http://example.com/?stringtext=jquery&stringword=jquerybyexample

\n
\n
var tech = GetQueryStringParams('stringtext');\nvar blog = GetQueryStringParams('stringword');\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6d", + "creator": "orip", + "createdAt": 1360578681000, + "text": "

From the MDN:

\n\n
function loadPageVar (sVar) {\n  return unescape(window.location.search.replace(new RegExp(\"^(?:.*[&\\\\?]\" + escape(sVar).replace(/[\\.\\+\\*]/g, \"\\\\$&\") + \"(?:\\\\=([^&]*))?)?.*$\", \"i\"), \"$1\"));\n}\n\nalert(loadPageVar(\"name\"));\n
\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b6f", + "creator": "Gabriel Ryan Nahmias", + "createdAt": 1363494265000, + "text": "

I believe this to be an accurate and concise way to achieve this (modified from http://css-tricks.com/snippets/javascript/get-url-variables/):

\n\n
function getQueryVariable(variable) {\n\n    var query = window.location.search.substring(1),            // Remove the ? from the query string.\n        vars = query.split(\"&\");                                // Split all values by ampersand.\n\n    for (var i = 0; i < vars.length; i++) {                     // Loop through them...\n        var pair = vars[i].split(\"=\");                          // Split the name from the value.\n        if (pair[0] == variable) {                              // Once the requested value is found...\n            return ( pair[1] == undefined ) ? null : pair[1];   // Return null if there is no value (no equals sign), otherwise return the value.\n        }\n    }\n\n    return undefined;                                           // Wasn't found.\n\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b70", + "creator": "Roi", + "createdAt": 1364518747000, + "text": "

A very lightweight jQuery method:

\n\n
var qs = window.location.search.replace('?','').split('&'),\n    request = {};\n$.each(qs, function(i,v) {\n    var initial, pair = v.split('=');\n    if(initial = request[pair[0]]){\n        if(!$.isArray(initial)) {\n            request[pair[0]] = [initial]\n        }\n        request[pair[0]].push(pair[1]);\n    } else {\n        request[pair[0]] = pair[1];\n    }\n    return;\n});\nconsole.log(request);\n
\n\n

And to alert, for example ?q

\n\n
alert(request.q)\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b72", + "creator": "Ph0en1x", + "createdAt": 1365863827000, + "text": "

The problem with the top answer on that question is that it's not-supported parameters placed after #, but sometimes it's needed to get this value also.

\n\n

I modified the answer to let it parse a full query string with a hash sign also:

\n\n
var getQueryStringData = function(name) {\n    var result = null;\n    var regexS = \"[\\\\?&#]\" + name + \"=([^&#]*)\";\n    var regex = new RegExp(regexS);\n    var results = regex.exec('?' + window.location.href.split('?')[1]);\n    if (results != null) {\n        result = decodeURIComponent(results[1].replace(/\\+/g, \" \"));\n    }\n    return result;\n};\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32769082fcc3049e924c0", + "creator": "Ph0en1x", + "createdAt": 1366640130000, + "text": "Yes, I know. But in my app i integrate 3rd party js navigation, which have some parameters after hash sign.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32769082fcc3049e924c2", + "creator": "etlds", + "createdAt": 1403794280000, + "text": "For example, in the Google search page, the searching query is followed by the hash sign '#'.", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b71", + "creator": "Kevin Cox", + "createdAt": 1364859602000, + "text": "

If you want array-style parameters URL.js supports arbitrarily nested array-style parameters as well as string indexes (maps). It also handles URL decoding.

\n\n
url.get(\"val[0]=zero&val[1]=one&val[2]&val[3]=&val[4]=four&val[5][0]=n1&val[5][1]=n2&val[5][2]=n3&key=val\", {array:true});\n// Result\n{\n    val: [\n        'zero',\n        'one',\n        true,\n        '',\n        'four',\n        [ 'n1', 'n2', 'n3' ]\n    ]\n    key: 'val'\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b73", + "creator": "Mikhus", + "createdAt": 1366355896000, + "text": "

I did a small URL library for my needs here: https://github.com/Mikhus/jsurl

\n\n

It's a more common way of manipulating the URLs in JavaScript. Meanwhile it's really lightweight (minified and gzipped < 1 KB) and has a very simple and clean API. And it does not need any other library to work.

\n\n

Regarding the initial question, it's very simple to do:

\n\n
var u = new Url; // Current document URL\n// or\nvar u = new Url('http://user:pass@example.com:8080/some/path?foo=bar&bar=baz#anchor');\n\n// Looking for query string parameters\nalert( u.query.bar);\nalert( u.query.foo);\n\n// Modifying query string parameters\nu.query.foo = 'bla';\nu.query.woo = ['hi', 'hey']\n\nalert(u.query.foo);\nalert(u.query.woo);\nalert(u);\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32769082fcc3049e924c5", + "creator": "brauliobo", + "createdAt": 1568917161000, + "text": "also doesnt't support object arrays like filters[topic][]=list", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b74", + "creator": "nkh", + "createdAt": 1368535630000, + "text": "

If you are using Browserify, you can use the url module from Node.js:

\n\n
var url = require('url');\n\nurl.parse('http://example.com/?bob=123', true).query;\n\n// returns { \"bob\": \"123\" }\n
\n\n

Further reading: URL Node.js v0.12.2 Manual & Documentation

\n\n

EDIT: You can use URL interface, its quite widely adopted in almost all the new browser and if the code is going to run on an old browser you can use a polyfill like this one. Here's a code example on how to use URL interface to get query parameters (aka search parameters)

\n\n
const url = new URL('http://example.com/?bob=123');\nurl.searchParams.get('bob'); \n
\n\n

You can also use URLSearchParams for it, here's an example from MDN to do it with URLSearchParams:

\n\n
var paramsString = \"q=URLUtils.searchParams&topic=api\";\nvar searchParams = new URLSearchParams(paramsString);\n\n//Iterate the search parameters.\nfor (let p of searchParams) {\n  console.log(p);\n}\n\nsearchParams.has(\"topic\") === true; // true\nsearchParams.get(\"topic\") === \"api\"; // true\nsearchParams.getAll(\"topic\"); // [\"api\"]\nsearchParams.get(\"foo\") === null; // true\nsearchParams.append(\"topic\", \"webdev\");\nsearchParams.toString(); // \"q=URLUtils.searchParams&topic=api&topic=webdev\"\nsearchParams.set(\"topic\", \"More webdev\");\nsearchParams.toString(); // \"q=URLUtils.searchParams&topic=More+webdev\"\nsearchParams.delete(\"topic\");\nsearchParams.toString(); // \"q=URLUtils.searchParams\"\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32769082fcc3049e924c7", + "creator": "Baran Emre", + "createdAt": 1563355642000, + "text": "OHH this is awesome! URLSearchParams works perfect. Many thanks!", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32769082fcc3049e924c9", + "creator": "TheWhiteLlama", + "createdAt": 1569793613000, + "text": "should probably be the best voted, no hassle with custom functions and additional libraries. It also seems to be mostly adopted by the browsers 2019.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b76", + "creator": "soheil bijavar", + "createdAt": 1373448457000, + "text": "

See this post or use this:

\n\n
<script type=\"text/javascript\" language=\"javascript\">\n    $(document).ready(function()\n    {\n        var urlParams = {};\n        (function ()\n        {\n            var match,\n            pl= /\\+/g,  // Regular expression for replacing addition symbol with a space\n            search = /([^&=]+)=?([^&]*)/g,\n            decode = function (s) { return decodeURIComponent(s.replace(pl, \" \")); },\n            query  = window.location.search.substring(1);\n\n            while (match = search.exec(query))\n                urlParams[decode(match[1])] = decode(match[2]);\n        })();\n\n        if (urlParams[\"q1\"] === 1)\n        {\n            return 1;\n        }\n    });\n</script>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3276a082fcc3049e924cc", + "creator": "Rup", + "createdAt": 1373456542000, + "text": "... although it's the same as Andy E's solution above.", + "upvotes": 267, + "upvoterUsernames": [], + "downvotes": 267, + "downvoterUsernames": [] + }, + { + "_id": "62f3276a082fcc3049e924ce", + "creator": "Peter Mortensen", + "createdAt": 1469369324000, + "text": "The link is broken.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b75", + "creator": "Robert Bolton", + "createdAt": 1368756855000, + "text": "

If you do not wish to use a JavaScript library you can use the JavaScript string functions to parse window.location. Keep this code in an external .js file and you can use it over and over again in different projects.

\n\n
// Example - window.location = \"index.htm?name=bob\";\n\nvar value = getParameterValue(\"name\");\n\nalert(\"name = \" + value);\n\nfunction getParameterValue(param)\n{\n    var url = window.location;\n    var parts = url.split('?');\n    var params = parts[1].split('&');\n    var val = \"\";\n\n    for ( var i=0; i<params.length; i++)\n    {\n        var paramNameVal = params[i].split('=');\n\n        if ( paramNameVal[0] == param )\n        {\n            val = paramNameVal[1];\n        }\n    }\n    return val;\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b78", + "creator": "Pushkraj P. Lanjekar", + "createdAt": 1374584405000, + "text": "

Use:

\n\n
  $(document).ready(function () {\n      var urlParams = {};\n      (function () {\n          var match,\n          pl = /\\+/g, // Regex for replacing addition symbol with a space\n              search = /([^&=]+)=?([^&]*)/g,\n              decode = function (s) {\n                  return decodeURIComponent(s.replace(pl, \" \"));\n              },\n              query = window.location.search.substring(1);\n\n          while (match = search.exec(query))\n              urlParams[decode(match[1])] = decode(match[2]);\n      })();\n      if (urlParams[\"q1\"] === 1) {\n          return 1;\n      }\n
\n\n

Please check and let me know your comments. Also refer to How to get querystring value using jQuery.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b79", + "creator": "clyfe", + "createdAt": 1375008127000, + "text": "

There's a robust implementation in Node.js's source
\nhttps://github.com/joyent/node/blob/master/lib/querystring.js

\n\n

Also TJ's qs does nested params parsing
\nhttps://github.com/visionmedia/node-querystring

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b77", + "creator": "farnoush resa", + "createdAt": 1373704709000, + "text": "

I recommend Dar Lessons as a good plugin. I have worked with it fo a long time. You can also use the following code.\nJus put var queryObj = {}; before document.ready and put the bellow code in the beginning of document.ready. After this code you can use queryObj[\"queryObjectName\"] for any query object you have

\n\n
var querystring = location.search.replace('?', '').split('&');\nfor (var i = 0; i < querystring.length; i++) {\n    var name = querystring[i].split('=')[0];\n    var value = querystring[i].split('=')[1];\n    queryObj[name] = value;\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3276a082fcc3049e924d3", + "creator": "Rup", + "createdAt": 1374049870000, + "text": "Dar Lessons has now released 1.1 which fixes the script injection attack.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b7a", + "creator": "kayz1", + "createdAt": 1376338551000, + "text": "
var getUrlParameters = function (name, url) {\n    if (!name) {\n        return undefined;\n    }\n\n    name = name.replace(/[\\[]/, '\\\\[').replace(/[\\]]/, '\\\\]');\n    url = url || location.search;\n\n    var regex = new RegExp('[\\\\?&#]' + name + '=?([^&#]*)', 'gi'), result, resultList = [];\n\n    while (result = regex.exec(url)) {\n        resultList.push(decodeURIComponent(result[1].replace(/\\+/g, ' ')));\n    }\n\n    return resultList.length ? resultList.length === 1 ? resultList[0] : resultList : undefined;\n};\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3276a082fcc3049e924d4", + "creator": "allenhwkim", + "createdAt": 1377376826000, + "text": "this is a lot better than accepted solution to handle array values in query string. i.e. arr[]=1&arr[]=2", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b7c", + "creator": "BlueMark", + "createdAt": 1378546371000, + "text": "

There are many solutions to retrieve URI query values, I prefer this one because it's short and works great:

\n\n
function get(name){\n   if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search))\n      return decodeURIComponent(name[1]);\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b7b", + "creator": "user2579312", + "createdAt": 1378543037000, + "text": "

I used this code (JavaScript) to get the what is passed through the URL:

\n\n
function getUrlVars() {\n            var vars = {};\n            var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {\n                vars[key] = value;\n            });\n            return vars;\n        }\n
\n\n

Then to assign the value to a variable, you only have to specify which parameter you want to get, ie if the URL is example.com/?I=1&p=2&f=3

\n\n

You can do this to get the values:

\n\n
var getI = getUrlVars()[\"I\"];\nvar getP = getUrlVars()[\"p\"];\nvar getF = getUrlVars()[\"f\"];\n
\n\n

then the values would be:

\n\n
getI = 1, getP = 2 and getF = 3\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3276a082fcc3049e924d7", + "creator": "JoshYates1980", + "createdAt": 1496862159000, + "text": "but what if you have example.com/3 (MVC routing)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b7d", + "creator": "Ankit_Shah55", + "createdAt": 1379093361000, + "text": "

This is very simple method to get parameter value(query string)

\n\n

Use gV(para_name) function to retrieve its value

\n\n
var a=window.location.search;\na=a.replace(a.charAt(0),\"\"); //Removes '?'\na=a.split(\"&\");\n\nfunction gV(x){\n for(i=0;i<a.length;i++){\n  var b=a[i].substr(0,a[i].indexOf(\"=\"));\n  if(x==b){\n   return a[i].substr(a[i].indexOf(\"=\")+1,a[i].length)}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b7e", + "creator": "Mat Ryer", + "createdAt": 1380562628000, + "text": "

We've just released arg.js, a project aimed at solving this problem once and for all. It's traditionally been so difficult but now you can do:

\n\n
var name = Arg.get(\"name\");\n
\n\n

or getting the whole lot:

\n\n
var params = Arg.all();\n
\n\n

and if you care about the difference between ?query=true and #hash=true then you can use the Arg.query() and Arg.hash() methods.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b80", + "creator": "Pithikos", + "createdAt": 1390272808000, + "text": "

This will parse variables AND arrays from a URL string. It uses neither regex or any external library.

\n\n
function url2json(url) {\n   var obj={};\n   function arr_vals(arr){\n      if (arr.indexOf(',') > 1){\n         var vals = arr.slice(1, -1).split(',');\n         var arr = [];\n         for (var i = 0; i < vals.length; i++)\n            arr[i]=vals[i];\n         return arr;\n      }\n      else\n         return arr.slice(1, -1);\n   }\n   function eval_var(avar){\n      if (!avar[1])\n          obj[avar[0]] = '';\n      else\n      if (avar[1].indexOf('[') == 0)\n         obj[avar[0]] = arr_vals(avar[1]);\n      else\n         obj[avar[0]] = avar[1];\n   }\n   if (url.indexOf('?') > -1){\n      var params = url.split('?')[1];\n      if(params.indexOf('&') > 2){\n         var vars = params.split('&');\n         for (var i in vars)\n            eval_var(vars[i].split('='));\n      }\n      else\n         eval_var(params.split('='));\n   }\n   return obj;\n}\n
\n\n

Example:

\n\n
var url = \"http://www.x.com?luckyNums=[31,21,6]&name=John&favFoods=[pizza]&noVal\"\nconsole.log(url2json(url));\n
\n\n

Output:

\n\n
[object]\n   noVal: \"\"\n   favFoods: \"pizza\"\n   name:     \"John\"\n   luckyNums:\n      0: \"31\"\n      1: \"21\"\n      2: \"6\"\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b7f", + "creator": "acjay", + "createdAt": 1384960679000, + "text": "

If you have Underscore.js or lodash, a quick and dirty way to get this done is:

\n\n
_.object(window.location.search.slice(1).split('&').map(function (val) { return val.split('='); }));\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b82", + "creator": "cocco", + "createdAt": 1390654449000, + "text": "

Get all querystring parameters including checkbox values (arrays).

\n\n

Considering the correct & normal use of GET parameters, the things I see it's missing, on most functions, is the support for arrays and removing the hash data.

\n\n

So I wrote this function:

\n\n
function qs(a){\n if(!a)return {};\n a=a.split('#')[0].split('&');\n var b=a.length,c={},d,k,v;\n while(b--){\n  d=a[b].split('=');\n  k=d[0].replace('[]',''),v=decodeURIComponent(d[1]||'');\n  c[k]?typeof c[k]==='string'?(c[k]=[v,c[k]]):(c[k].unshift(v)):c[k]=v;\n }\n return c\n}\n
\n\n

Using shorthand operators & while-- loop, the performance should be very good to.

\n\n

Support:

\n\n
    \n
  1. Empty values (key= / key)
  2. \n
  3. Key value (key=value)
  4. \n
  5. Arrays (key[]=value)
  6. \n
  7. Hash (the hash tag is split out)
  8. \n
\n\n

Notes:

\n\n

It does not support object arrays (key[key]=value)

\n\n

If the space is + it remains a +.

\n\n

Add .replace(/\\+/g, \" \") if you need.

\n\n

Usage:

\n\n
qs('array[]=1&array[]=2&key=value&empty=&empty2#hash')\n
\n\n

Return:

\n\n
{\n    \"empty\": \"\",\n    \"key\": \"value\",\n    \"array\": [\n        \"1\",\n        \"2\"\n    ]\n}\n
\n\n

Demo:

\n\n

http://jsfiddle.net/ZQMrt/1/

\n\n

Info

\n\n

If you don't understand something or you can't read the function just ask. I'm happy to explain what I did here.

\n\n

If you think the function is unreadable and unmaintainable I'm happy to rewrite the function for you, but consider that shorthand & bitwise operators are always faster than a standard syntax (maybe read about shorthands and bitwise operators in the ECMA-262 book or use your favorite search engine). Rewriting the code in a standard readable syntax means performance loss.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3276b082fcc3049e924de", + "creator": "brauliobo", + "createdAt": 1568916969000, + "text": "I need one that supports object arrays", + "upvotes": 2260, + "upvoterUsernames": [], + "downvotes": 2260, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b81", + "creator": "sonorita", + "createdAt": 1390377496000, + "text": "

This will work... You need to call this function where you need get the parameter by passing its name...

\n\n
function getParameterByName(name)\n{\n  name = name.replace(/[\\[]/,\"\\\\\\[\").replace(/[\\]]/,\"\\\\\\]\");\n  var regexS = \"[\\\\?&]\"+name+\"=([^&#]*)\";\n  var regex = new RegExp( regexS );\n  var results = regex.exec( window.location.href );\n  alert(results[1]);\n  if (results == null)\n    return \"\";\n  else\n    return results[1];\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b84", + "creator": "Dorian", + "createdAt": 1392229169000, + "text": "

This didn't work for me, I want to match ?b as the b parameter is present, and not match ?return as the r parameter, here is my solution.

\n\n
window.query_param = function(name) {\n  var param_value, params;\n\n  params = location.search.replace(/^\\?/, '');\n  params = _.map(params.split('&'), function(s) {\n    return s.split('=');\n  });\n\n  param_value = _.select(params, function(s) {\n    return s.first === name;\n  })[0];\n\n  if (param_value) {\n    return decodeURIComponent(param_value[1] || '');\n  } else {\n    return null;\n  }\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b83", + "creator": "Saidulu Buchhala", + "createdAt": 1391070851000, + "text": "

A simple solution with plain JavaScript and regular expressions:

\n\n
alert(getQueryString(\"p2\"));\n\nfunction getQueryString (Param) {\n    return decodeURI(\"http://www.example.com/?p1=p11&p2=p2222\".replace(new RegExp(\"^(?:.*[&?]\" + encodeURI(Param).replace(/[.+*]/g, \"$&\") + \"(?:=([^&]*))?)?.*$\", \"i\"), \"$1\"));\n}\n
\n\n

JsFiddle

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b89", + "creator": "Vladimir Kornea", + "createdAt": 1401416229000, + "text": "

Here's what I'm using:

\n\n
/**\n * Examples:\n * getUrlParams()['myparam']    // url defaults to the current page\n * getUrlParams(url)['myparam'] // url can be just a query string\n *\n * Results of calling `getUrlParams(url)['myparam']` with various urls:\n * example.com                               (undefined)\n * example.com?                              (undefined)\n * example.com?myparam                       (empty string)\n * example.com?myparam=                      (empty string)\n * example.com?myparam=0                     (the string '0')\n * example.com?myparam=0&myparam=override    (the string 'override')\n *\n * Origin: http://stackoverflow.com/a/23946023/2407309\n */\nfunction getUrlParams (url) {\n    var urlParams = {} // return value\n    var queryString = getQueryString()\n    if (queryString) {\n        var keyValuePairs = queryString.split('&')\n        for (var i = 0; i < keyValuePairs.length; i++) {\n            var keyValuePair = keyValuePairs[i].split('=')\n            var paramName = keyValuePair[0]\n            var paramValue = keyValuePair[1] || ''\n            urlParams[paramName] = decodeURIComponent(paramValue.replace(/\\+/g, ' '))\n        }\n    }\n    return urlParams // functions below\n    function getQueryString () {\n        var reducedUrl = url || window.location.search\n        reducedUrl = reducedUrl.split('#')[0] // Discard fragment identifier.\n        var queryString = reducedUrl.split('?')[1]\n        if (!queryString) {\n            if (reducedUrl.search('=') !== false) { // URL is a query string.\n                queryString = reducedUrl\n            }\n        }\n        return queryString\n    } // getQueryString\n} // getUrlParams\n
\n\n

Returning 'override' rather than '0' in the last case makes it consistent with PHP. Works in IE7.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a8082fcc3049e924e2", + "creator": "Vladimir Kornea", + "createdAt": 1416310999000, + "text": "Parameter names should never need decoding.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327a8082fcc3049e924e4", + "creator": "Sudar", + "createdAt": 1427872747000, + "text": "The solution is good, but why no semicolon at the end of statements?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327a8082fcc3049e924e6", + "creator": "NiCk Newman", + "createdAt": 1436804075000, + "text": "'Amazing how many overly complicated and incomplete solutions are posted here' Lol, the irony..", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b8b", + "creator": "Konstantin Tarkus", + "createdAt": 1403904158000, + "text": "
// Parse query string\nvar params = {}, queryString = location.hash.substring(1),\n    regex = /([^&=]+)=([^&]*)/g,\n    m;\nwhile (m = regex.exec(queryString)) {\n    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);\n}\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a8082fcc3049e924e8", + "creator": "hackel", + "createdAt": 1503515920000, + "text": "Uh, hash != querystring.", + "upvotes": 210, + "upvoterUsernames": [], + "downvotes": 210, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b8c", + "creator": "Peter T Bosse II", + "createdAt": 1404129910000, + "text": "

This function will return a parsed JavaScript object with any arbitrarily nested values using recursion as necessary.

\n\n

Here's a jsfiddle example.

\n\n
[\n  '?a=a',\n  '&b=a',\n  '&b=b',\n  '&c[]=a',\n  '&c[]=b',\n  '&d[a]=a',\n  '&d[a]=x',\n  '&e[a][]=a',\n  '&e[a][]=b',\n  '&f[a][b]=a',\n  '&f[a][b]=x',\n  '&g[a][b][]=a',\n  '&g[a][b][]=b',\n  '&h=%2B+%25',\n  '&i[aa=b',\n  '&i[]=b',\n  '&j=',\n  '&k',\n  '&=l',\n  '&abc=foo',\n  '&def=%5Basf%5D',\n  '&ghi=[j%3Dkl]',\n  '&xy%3Dz=5',\n  '&foo=b%3Dar',\n  '&xy%5Bz=5'\n].join('');\n
\n\n

Given any of the above test examples.

\n\n
var qs = function(a) {\n  var b, c, e;\n  b = {};\n  c = function(d) {\n    return d && decodeURIComponent(d.replace(/\\+/g, \" \"));\n  };\n  e = function(f, g, h) {\n    var i, j, k, l;\n    h = h ? h : null;\n    i = /(.+?)\\[(.+?)?\\](.+)?/g.exec(g);\n    if (i) {\n      [j, k, l] = [i[1], i[2], i[3]]\n      if (k === void 0) {\n        if (f[j] === void 0) {\n          f[j] = [];\n        }\n        f[j].push(h);\n      } else {\n        if (typeof f[j] !== \"object\") {\n          f[j] = {};\n        }\n        if (l) {\n          e(f[j], k + l, h);\n        } else {\n          e(f[j], k, h);\n        }\n      }\n    } else {\n      if (f.hasOwnProperty(g)) {\n        if (Array.isArray(f[g])) {\n          f[g].push(h);\n        } else {\n          f[g] = [].concat.apply([f[g]], [h]);\n        }\n      } else {\n        f[g] = h;\n      }\n      return f[g];\n    }\n  };\n  a.replace(/^(\\?|#)/, \"\").replace(/([^#&=?]+)?=?([^&=]+)?/g, function(m, n, o) {\n    n && e(b, c(n), c(o));\n  });\n  return b;\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b8a", + "creator": "Damien", + "createdAt": 1402050763000, + "text": "

Most pretty but basic:

\n\n
data = {};\n$.each(\n    location.search.substr(1).split('&').filter(Boolean).map(function(kvpairs){\n        return kvpairs.split('=')\n    }),\n    function(i,values) {\n        data[values.shift()] = values.join('=')\n    }\n);\n
\n\n

It doesn't handle values lists such as ?a[]=1&a[]2

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b85", + "creator": "filip", + "createdAt": 1392412446000, + "text": "

The shortest possible expression in terms of size to obtain a query object seems to be:

\n\n
var params = {};\nlocation.search.substr(1).replace(/([^&=]*)=([^&]*)&?/g,\n  function () { params[decodeURIComponent(arguments[1])] = decodeURIComponent(arguments[2]); });\n
\n\n

You can make use of the A element to parse a URI from a string into its location-like components (to get rid of #..., for example):

\n\n
var a = document.createElement('a');\na.href = url;\n// Parse a.search.substr(1)... as above\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b86", + "creator": "Shlomi Hassid", + "createdAt": 1397438616000, + "text": "

Quick, easy, and fast:

\n\n

The function:

\n\n
function getUrlVar() {\n    var result = {};\n    var location = window.location.href.split('#');\n    var parts = location[0].replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {\n        result [key] = value;\n    });\n    return result;\n}\n
\n\n

Usage:

\n\n
var varRequest = getUrlVar()[\"theUrlVarName\"];\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b87", + "creator": "George", + "createdAt": 1397459933000, + "text": "

For those who wants a short method (with limitations):

\n\n
location.search.split('myParameter=')[1]\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b88", + "creator": "krisrak", + "createdAt": 1397672192000, + "text": "

Here is String prototype implementation:

\n\n
String.prototype.getParam = function( str ){\n    str = str.replace(/[\\[]/,\"\\\\\\[\").replace(/[\\]]/,\"\\\\\\]\");\n    var regex = new RegExp( \"[\\\\?&]*\"+str+\"=([^&#]*)\" );    \n    var results = regex.exec( this );\n    if( results == null ){\n        return \"\";\n    } else {\n        return results[1];\n    }\n}\n
\n\n

Example call:

\n\n
var status = str.getParam(\"status\")\n
\n\n

str can be a query string or url

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c7082fcc3049e90acd", + "creator": "user456814", + "createdAt": 1375312172000, + "text": "possible duplicate of JavaScript query string", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 4757871, + "uvac": 4757937 + } + }, + { + "_id": "62f321bb082fcc3049e8febc", + "title": "How do I check whether a checkbox is checked in jQuery?", + "title-lowercase": "how do i check whether a checkbox is checked in jquery?", + "creator": "Prasad", + "createdAt": 1243091799000, + "status": "open", + "text": "

I need to check the checked property of a checkbox and perform an action based on the checked property using jQuery.

\n

For example, if the age checkbox is checked, then I need to show a textbox to enter age, else hide the textbox.

\n

But the following code returns false by default:

\n

\r\n
\r\n
if ($('#isAgeSelected').attr('checked')) {\n  $(\"#txtAge\").show();\n} else {\n  $(\"#txtAge\").hide();\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<input type=\"checkbox\" id=\"isAgeSelected\"/>\n<div id=\"txtAge\" style=\"display:none\">\n  Age is selected\n</div>
\r\n
\r\n
\r\n

\n

How do I successfully query the checked property?

\n", + "upvotes": 8995, + "upvoterUsernames": [], + "downvotes": 3959, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 4596647, + "answers": 66, + "answerItems": [ + { + "_id": "62f321bf082fcc3049e9024c", + "creator": "karim79", + "createdAt": 1243092033000, + "text": "
\n

How do I successfully query the checked property?

\n
\n\n

The checked property of a checkbox DOM element will give you the checked state of the element.

\n\n

Given your existing code, you could therefore do this:

\n\n
if(document.getElementById('isAgeSelected').checked) {\n    $(\"#txtAge\").show();\n} else {\n    $(\"#txtAge\").hide();\n}\n
\n\n

However, there's a much prettier way to do this, using toggle:

\n\n

\r\n
\r\n
$('#isAgeSelected').click(function() {\r\n    $(\"#txtAge\").toggle(this.checked);\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<input type=\"checkbox\" id=\"isAgeSelected\"/>\r\n<div id=\"txtAge\" style=\"display:none\">Age is something</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 5354, + "upvoterUsernames": [], + "downvotes": 1731, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9024d", + "creator": "xenon", + "createdAt": 1243092864000, + "text": "

I believe you could do this:

\n\n
if ($('#isAgeSelected :checked').size() > 0)\n{\n    $(\"#txtAge\").show(); \n} else { \n    $(\"#txtAge\").hide();\n}\n
\n", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32382082fcc3049e9146f", + "creator": "Kevin Dark", + "createdAt": 1634226083000, + "text": "This is the best answer for selecting ONLY those that are checked in the first place. $('#isAgeSelected :checked')", + "upvotes": 1313, + "upvoterUsernames": [], + "downvotes": 1313, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9024e", + "creator": "Prasad", + "createdAt": 1243143025000, + "text": "

This worked for me:

\n\n
$get(\"isAgeSelected \").checked == true\n
\n\n

Where isAgeSelected is the id of the control.

\n\n

Also, @karim79's answer works fine. I am not sure what I missed at the time I tested it.

\n\n

Note, this is answer uses Microsoft Ajax, not jQuery

\n", + "upvotes": 173, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32382082fcc3049e91472", + "creator": "Kemuel Sanchez", + "createdAt": 1631060158000, + "text": "The question specifically requests for a jQuery solution.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90250", + "creator": "tsw_mik", + "createdAt": 1291905845000, + "text": "

I was having the same problem and none of the posted solutions seemed to work and then I found out that it's because ASP.NET renders the CheckBox control as a SPAN with INPUT inside, so the CheckBox ID is actually an ID of a SPAN, not an INPUT, so you should use:

\n\n
$('#isAgeSelected input')\n
\n\n

rather than

\n\n
$('#isAgeSelected')\n
\n\n

and then all methods listed above should work.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9024f", + "creator": "Pradeep", + "createdAt": 1282225138000, + "text": "

I am using this and this is working absolutely fine:

\n\n
$(\"#checkkBoxId\").attr(\"checked\") ? alert(\"Checked\") : alert(\"Unchecked\");\n
\n\n

Note: If the checkbox is checked it will return true otherwise undefined, so better check for the \"TRUE\" value.

\n", + "upvotes": 193, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90251", + "creator": "Nertim", + "createdAt": 1292525453000, + "text": "

I ran in to the exact same issue. I have an ASP.NET checkbox

\n\n
<asp:CheckBox ID=\"chkBox1\" CssClass='cssChkBox1' runat=\"server\" />\n
\n\n

In the jQuery code I used the following selector to check if the checkbox was checked or not, and it seems to work like a charm.

\n\n
if ($(\"'.cssChkBox1 input[type=checkbox]'\").is(':checked'))\n{ ... } else { ... }\n
\n\n

I'm sure you can also use the ID instead of the CssClass,

\n\n
if ($(\"'#cssChkBox1 input[type=checkbox]'\").is(':checked'))\n{ ... } else { ... }\n
\n\n

I hope this helps you.

\n", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90252", + "creator": "ashish amatya", + "createdAt": 1294313615000, + "text": "

This works for me:

\n\n
/* isAgeSelected being id for checkbox */\n\n$(\"#isAgeSelected\").click(function(){\n  $(this).is(':checked') ? $(\"#txtAge\").show() : $(\"#txtAge\").hide();\n});\n
\n", + "upvotes": 99, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90253", + "creator": "Tim Abell", + "createdAt": 1305063380000, + "text": "

Here's an example that includes initialising the show/hide to match the state of the checkbox when the page loads; taking account of the fact that firefox remembers the state of checkboxes when you refresh the page, but won't remember the state of the shown/hidden elements.

\n\n
$(function() {\n    // initialise visibility when page is loaded\n    $('tr.invoiceItemRow').toggle($('#showInvoiceItems').attr('checked'));\n    // attach click handler to checkbox\n    $('#showInvoiceItems').click(function(){ $('tr.invoiceItemRow').toggle(this.checked);})\n});\n
\n\n

(with help from other answers on this question)

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90254", + "creator": "Bhanu Krishnan", + "createdAt": 1308737681000, + "text": "

Use jQuery's is() function:

\n
if($("#isAgeSelected").is(':checked'))\n    $("#txtAge").show();  // checked\nelse\n    $("#txtAge").hide();  // unchecked\n
\n", + "upvotes": 4069, + "upvoterUsernames": [], + "downvotes": 1930, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32382082fcc3049e9147a", + "creator": "Kai Neuwerth", + "createdAt": 1609923760000, + "text": "A little bit cleaner solution would be $("#txtAge").toggle($("#isAgeSelected").is(':checked')).", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32382082fcc3049e9147c", + "creator": "Jean-Roch B.", + "createdAt": 1648672469000, + "text": "@KaiNeuwerth IMO it's not cleaner, just shorter.", + "upvotes": 2085, + "upvoterUsernames": [], + "downvotes": 2085, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90255", + "creator": "SeanDowney", + "createdAt": 1308850158000, + "text": "

Using jQuery > 1.6

\n\n
<input type=\"checkbox\" value=\"1\" name=\"checkMeOut\" id=\"checkMeOut\" checked=\"checked\" />\n\n// traditional attr\n$('#checkMeOut').attr('checked'); // \"checked\"\n// new property method\n$('#checkMeOut').prop('checked'); // true\n
\n\n

Using the new property method:

\n\n
if($('#checkMeOut').prop('checked')) {\n    // something when checked\n} else {\n    // something else when not\n}\n
\n", + "upvotes": 886, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90256", + "creator": "arviman", + "createdAt": 1317863572000, + "text": "

Using the Click event handler for the checkbox property is unreliable, as the checked property can change during the execution of the event handler itself!

\n\n

Ideally, you'd want to put your code into a change event handler such as it is fired every time the value of the check box is changed (independent of how it's done so).

\n\n
$('#isAgeSelected').bind('change', function () {\n\n   if ($(this).is(':checked'))\n     $(\"#txtAge\").show();\n   else\n     $(\"#txtAge\").hide();\n});\n
\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90257", + "creator": "Dalius I", + "createdAt": 1328538668000, + "text": "

My way of doing this is:

\n\n
if ( $(\"#checkbox:checked\").length ) {       \n    alert(\"checkbox is checked\");\n} else {\n    alert(\"checkbox is not checked\");\n}\n
\n", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90258", + "creator": "Joshua Harris", + "createdAt": 1328640217000, + "text": "

I verified in Firefox 9.0.1 that the following works for catching the state of a checkbox post change:

\n\n
$(\"#mycheckbox\").change(function() {\n    var value = $(this).prop(\"checked\") ? 'true' : 'false';                     \n    alert(value);\n});\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90259", + "creator": "fe_lix_", + "createdAt": 1329059743000, + "text": "
$(selector).attr('checked') !== undefined\n
\n\n

This returns true if the input is checked and false if it is not.

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025a", + "creator": "Octavian A. Damiean", + "createdAt": 1332271143000, + "text": "

I decided to post an answer on how to do that exact same thing without jQuery. Just because I'm a rebel.

\n\n
var ageCheckbox = document.getElementById('isAgeSelected');\nvar ageInput = document.getElementById('txtAge');\n\n// Just because of IE <333\nageCheckbox.onchange = function() {\n    // Check if the checkbox is checked, and show/hide the text field.\n    ageInput.hidden = this.checked ? false : true;\n};\n
\n\n

First you get both elements by their ID. Then you assign the checkboxe's onchange event a function that checks whether the checkbox got checked and sets the hidden property of the age text field appropriately. In that example using the ternary operator.

\n\n

Here is a fiddle for you to test it.

\n\n

Addendum

\n\n

If cross-browser compatibility is an issue then I propose to set the CSS display property to none and inline.

\n\n
elem.style.display = this.checked ? 'inline' : 'none';\n
\n\n

Slower but cross-browser compatible.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025b", + "creator": "Salman A", + "createdAt": 1335527661000, + "text": "

Since jQuery 1.6, the behavior of jQuery.attr() has changed and users are encouraged not to use it to retrieve an element's checked state. Instead, you should use jQuery.prop():

\n\n
$(\"#txtAge\").toggle(\n    $(\"#isAgeSelected\").prop(\"checked\") // For checked attribute it returns true/false;\n                                        // Return value changes with checkbox state\n);\n
\n\n

Two other possibilities are:

\n\n
$(\"#txtAge\").get(0).checked\n$(\"#txtAge\").is(\":checked\")\n
\n", + "upvotes": 170, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025c", + "creator": "MDMoore313", + "createdAt": 1355115755000, + "text": "

The top answer didn't do it for me. This did though:

\n\n
<script type=\"text/javascript\">\n    $(document).ready(function(){\n\n        $(\"#li_13\").click(function(){\n            if($(\"#agree\").attr('checked')){\n                $(\"#saveForm\").fadeIn();\n            }\n            else\n            {\n                $(\"#saveForm\").fadeOut();\n            }\n        });\n    });\n</script>\n
\n\n

Basically when the element #li_13 is clicked, it checks if the element # agree (which is the checkbox) is checked by using the .attr('checked') function. If it is then fadeIn the #saveForm element, and if not fadeOut the saveForm element.

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025d", + "creator": "Abdul Hamid", + "createdAt": 1358516368000, + "text": "
if( undefined == $('#isAgeSelected').attr('checked') ) {\n    $(\"#txtAge\").hide();\n} else {\n    $(\"#txtAge\").show();\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025e", + "creator": "user1996628", + "createdAt": 1358762036000, + "text": "
if( undefined == $('#isAgeSelected').attr('checked') ) {\n    $(\"#txtAge\").hide();\n} else {\n    $(\"#txtAge\").show();\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90260", + "creator": "cauleyfj", + "createdAt": 1361759218000, + "text": "

This was my workaround:

\n\n
$('#vcGoButton').click(function () {\n    var buttonStatus = $('#vcChangeLocation').prop('checked');\n    console.log(\"Status is \" + buttonStatus);\n    if (buttonStatus) {\n        var address = $('#vcNewLocation').val();\n        var cabNumber = $('#vcVehicleNumber').val();\n        $.get('postCabLocation.php',\n              {address: address, cabNumber: cabNumber},\n              function(data) {\n                  console.log(\"Changed vehicle \" + cabNumber + \" location to \" + address );\n              });\n    }\n    else {\n        console.log(\"VC go button clicked, but no location action\");\n    }\n});\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9025f", + "creator": "Richard Maxwell", + "createdAt": 1358959206000, + "text": "

Use:

\n\n
$(this).toggle($(\"input:checkbox\", $(this))[0].checked);\n
\n\n

When you are selecting out of context, remember you need the [0] to access the checkbox.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90261", + "creator": "Madan Sapkota", + "createdAt": 1362202704000, + "text": "

Include jQuery from the local file system. I used Google's CDN, and there are also many CDNs to choose from.

\n\n
<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\n
\n\n

The code will execute as soon as a checkbox inside mycheck class is clicked. If the current clicked checkbox is checked then it will disable all others and enable the current one. If the current one is unchecked, it will again enable all checkboxes for rechecking.

\n\n
<script type=\"text/javascript\">\n    $(document).ready(function() {\n\n        var checkbox_selector = '.mycheck input[type=checkbox]';\n\n        $(checkbox_selector).click(function() {\n            if ($($(this)).is(':checked')) {\n\n                // Disable all checkboxes\n                $(checkbox_selector).attr('disabled', 'disabled');\n\n                // Enable current one\n                $($(this)).removeAttr('disabled');\n            }\n            else {\n                // If unchecked open all checkbox\n                $(checkbox_selector).removeAttr('disabled');\n            }\n        });\n    });\n</script>\n
\n\n

Simple form to test

\n\n
<form method=\"post\" action=\"\">\n    <div class=\"mycheck\">\n        <input type=\"checkbox\" value=\"1\" /> Television\n        <input type=\"checkbox\" value=\"2\" /> Computer\n        <input type=\"checkbox\" value=\"3\" /> Laptop\n        <input type=\"checkbox\" value=\"4\" /> Camera\n        <input type=\"checkbox\" value=\"5\" /> Music Systems\n    </div>\n</form>\n
\n\n

Output screen:

\n\n

\"Enter

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90262", + "creator": "ijarlax", + "createdAt": 1370869979000, + "text": "
if($(\"#checkkBoxId\").is(':checked')){\n  alert(\"Checked=true\");\n}\n
\n\n

or

\n\n
if($(\"#checkkBoxId\").attr('checked') == true){\n  alert(\"checked=true\");\n}\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90264", + "creator": "Rajesh Omanakuttan", + "createdAt": 1377400317000, + "text": "

If you are using an updated version of jquery, you must go for .prop method to resolve your issue:

\n

$('#isAgeSelected').prop('checked') will return true if checked and false if unchecked. I confirmed it and I came across this issue earlier. $('#isAgeSelected').attr('checked') and $('#isAgeSelected').is('checked') is returning undefined which is not a worthy answer for the situation. So do as given below.

\n
if($('#isAgeSelected').prop('checked')) {\n    $("#txtAge").show();\n} else {\n    $("#txtAge").hide();\n}\n
\n", + "upvotes": 160, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90263", + "creator": "Daryl H", + "createdAt": 1373903434000, + "text": "

This is the minimal amount of code I think I needed to do something like this effectively. I found this method to be useful; it returns an array of the check boxes that are checked and then you can use their value (this solution uses jQuery):

\n\n
// This is how you get them\nvar output = \"\";\nvar checkedBoxes = $(\"DivCheckBoxesAreIn\").children(\"input:checked\");\nif(checkedBoxes.length <= 0) {\n    alert('Please select check boxes');\n    return false;\n};\n\n// And this is how you use them:\ncheckedBoxes.each(function() {\n    output +=  this.value + \", \";\n};\n
\n\n

Printing \"output\" will give you a comma-separated list of your values.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90265", + "creator": "Nishant Kumar", + "createdAt": 1378449799000, + "text": "

I am using this:

\n\n
 <input type=\"checkbox\" id=\"isAgeSelected\" value=\"1\" /> <br/>\n <input type=\"textbox\" id=\"txtAge\" />\n\n $(\"#isAgeSelected\").is(':checked') ? $(\"#txtAge\").show() : $(\"#txtAge\").hide();\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90266", + "creator": "Udit Bhardwaj", + "createdAt": 1380469505000, + "text": "

Use:

\n\n
<input type=\"checkbox\" id=\"abc\" value=\"UDB\">UDB\n<input type=\"checkbox\" id=\"abc\" value=\"Prasad\">Prasad\n
\n\n\n\n
$('input#abc').click(function(){\n  if($(this).is(':checked'))\n  {\n    var checkedOne=$(this).val()\n    alert(checkedOne);\n\n    // Do some other action\n  }\n})\n
\n\n

This can help if you want that the required action has to be done only when you check the box not at the time you remove the check.

\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90267", + "creator": "subham.saha1004", + "createdAt": 1381386202000, + "text": "

The checked attribute of an input type=\"checkbox\" is mapped with the defaultChecked property, not with the checked property.

\n\n

So when doing something in a page when a checkbox is checked on uncheked, use the prop() method instead. It fetches the property value and changes as the state of the checkbox changes.

\n\n

Using attr() or getAttribute(in pure JavaScript) in these cases are not the proper way of doing things.

\n\n

if elem is the concerned checkbox then do something like this to fetch the value:

\n\n
elem.checked\n
\n\n

or

\n\n
$(elem).prop('checked')\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90268", + "creator": "Somnath Kharat", + "createdAt": 1383137024000, + "text": "

1) If your HTML markup is:

\n\n
<input type=\"checkbox\"  />\n
\n\n

attr used:

\n\n
$(element).attr(\"checked\"); // Will give you undefined as initial value of checkbox is not set\n
\n\n

If prop is used:

\n\n
$(element).prop(\"checked\"); // Will give you false whether or not initial value is set\n
\n\n

2) If your HTML markup is:

\n\n
 <input type=\"checkbox\"  checked=\"checked\" />// May be like this also  checked=\"true\"\n
\n\n

attr used:

\n\n
$(element).attr(\"checked\") // Will return checked whether it is checked=\"true\"\n
\n\n

Prop used:

\n\n
$(element).prop(\"checked\") // Will return true whether checked=\"checked\"\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9026a", + "creator": "usayee", + "createdAt": 1388745505000, + "text": "

This example is for button.

\n\n

Try the following:

\n\n
<input type=\"button\" class=\"check\" id=\"checkall\" value=\"Check All\" />  &nbsp; <input type=\"button\" id=\"remove\" value=\"Delete\" /> <br/>\n\n<input type=\"checkbox\" class=\"cb-element\"  value=\"1\" /> Checkbox  1 <br/>\n<input type=\"checkbox\" class=\"cb-element\"  value=\"2\" /> Checkbox  2 <br/>\n<input type=\"checkbox\" class=\"cb-element\"  value=\"3\" /> Checkbox  3 <br/>\n\n\n$('#remove').attr('disabled', 'disabled'); \n\n$(document).ready(function() {  \n\n    $('.cb-element').click(function() {\n\n        if($(this).prop('checked'))\n        {\n            $('#remove').attr('disabled', false);\n        }\n        else\n        {\n            $('#remove').attr('disabled', true);\n        }\n    });   \n\n    $('.check:button').click(function()\n{\n    var checked = !$(this).data('checked');\n    $('input:checkbox').prop('checked', checked);\n    $(this).data('checked', checked);\n\n    if(checked == true)\n    {\n        $(this).val('Uncheck All');\n         $('#remove').attr('disabled', false);\n    }\n\n    else if(checked == false)\n    {\n        $(this).val('Check All');\n        $('#remove').attr('disabled', true);\n    }\n});\n});\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90269", + "creator": "Tsonev", + "createdAt": 1384512644000, + "text": "

I'm sure it's not some revelation, but I didn't see it all in one example:

\n\n

Selector for all checked checkboxes(on the page):

\n\n
$('input[type=checkbox]:checked')\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9026c", + "creator": "Khaled.K", + "createdAt": 1400046139000, + "text": "

Automated

\n\n
$(document).ready(function()\n{\n    $('#isAgeSelected').change(function()\n    {\n        alert( 'value =' + $('#chkSelect').attr('checked') );\n    });\n});\n
\n\n

HTML

\n\n
<b> <input type=\"isAgeSelected\" id=\"chkSelect\" /> Age Check </b>\n\n<br/><br/>\n\n<input type=\"button\" id=\"btnCheck\" value=\"check\" />\n
\n\n

jQuery

\n\n
$(document).ready(function()\n{\n    $('#btnCheck').click(function()\n    {\n        var isChecked = $('#isAgeSelected').attr('checked');\n\n        if (isChecked == 'checked')\n            alert('check-box is checked');\n        else\n            alert('check-box is not checked');\n    })\n});\n
\n\n

Ajax

\n\n
function check()\n{\n    if (isAgeSelected())\n        alert('check-box is checked');\n    else\n        alert('check-box is not checked');\n}\n\nfunction isAgeSelected()\n{\n    return ($get(\"isAgeSelected\").checked == true);\n}\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9026b", + "creator": "JJ_Coder4Hire", + "createdAt": 1398133887000, + "text": "

Setter:

\n\n
$(\"#chkmyElement\")[0].checked = true;\n
\n\n

Getter:

\n\n
if($(\"#chkmyElement\")[0].checked) {\n   alert(\"enabled\");\n} else {\n   alert(\"disabled\");\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9026e", + "creator": "Tarion", + "createdAt": 1409395018000, + "text": "

I would actually prefere the change event.

\n\n
$('#isAgeSelected').change(function() {\n    $(\"#txtAge\").toggle(this.checked);\n});\n
\n\n

Demo Fiddle

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9026d", + "creator": "ungalcrys", + "createdAt": 1400616209000, + "text": "

jQuery 1.6+

\n\n
$('#isAgeSelected').prop('checked')\n
\n\n

jQuery 1.5 and below

\n\n
$('#isAgeSelected').attr('checked')\n
\n\n

Any version of jQuery

\n\n
// Assuming an event handler on a checkbox\nif (this.checked)\n
\n\n

All credit goes to Xian.

\n", + "upvotes": 267, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32385082fcc3049e91498", + "creator": "vapcguy", + "createdAt": 1434732635000, + "text": "Technically, this.checked is using straight Javascript. But I love that cross-jQuery-version answer!", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9026f", + "creator": "Parth Chavda", + "createdAt": 1413272887000, + "text": "

There are many ways to check if a checkbox is checked or not:

\n\n

Way to check using jQuery

\n\n
if (elem.checked)\nif ($(elem).prop(\"checked\"))\nif ($(elem).is(\":checked\"))\nif ($(elem).attr('checked'))\n
\n\n

Check example or also document:

\n\n\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90270", + "creator": "NoWar", + "createdAt": 1415378222000, + "text": "

In case if you need to use CSS class as jQuery selector you can do following:

\n\n
$(document).ready(function () {\n        $('.myOptionCheckbox').change(function () {            \n            if ($(this).prop('checked') == true) {\n                console.log(\"checked\");           \n            }\n            else {\n                console.log(\"unchecked\");                \n            }\n        });\n    });\n
\n\n

It works fine for checkboxes and radioboxes as well.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90271", + "creator": "Jasper", + "createdAt": 1416918358000, + "text": "
$(document).ready(function() {    \n    $('#agecheckbox').click(function() {\n        if($(this).is(\":checked\"))\n        {\n            $('#agetextbox').show();\n        } else {\n            $('#agetextbox').hide();\n        }\n    });\n});\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90272", + "creator": "Sudha", + "createdAt": 1420547695000, + "text": "

$(\"#isAgeSelected\").prop('checked', true);

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32385082fcc3049e9149e", + "creator": "bronze man", + "createdAt": 1428022265000, + "text": "This code can set the checkbox to checked status.I do not think this answer is relative to the question.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90273", + "creator": "Bram", + "createdAt": 1420722644000, + "text": "

Toggle: 0/1 or else

\n\n
<input type=\"checkbox\" id=\"nolunch\" />\n<input id=\"checklunch />\"\n\n    $('#nolunch').change(function () {\n    if ($(this).is(':checked')) {\n        $('#checklunch').val('1');\n    };\n    if ($(this).is(':checked') == false) {\n        $('#checklunch').val('0');\n    };\n});\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90274", + "creator": "zamoldar", + "createdAt": 1427665511000, + "text": "

A selector returns multiple objects, and it must take the first item in the array:

\n\n
// Collection\nvar chckremember = $(\"#chckremember\");\n\n\n// Result boolen\nvar ischecked=chckremember[0].checked;\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90275", + "creator": "Ferhat KOÇER", + "createdAt": 1429024654000, + "text": "

This function is alternative and stable:

\n\n
$('#isAgeSelected').context.checked\n(return True/False)\n
\n\n

Example:

\n\n
if($('#isAgeSelected').context.checked){ //if Checkbox is checked then bla bla..\n    /*.....*/\n}else{\n    /*.....*/\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90276", + "creator": "Jitendra Damor", + "createdAt": 1433394436000, + "text": "

I think it will be the simple one

\n\n
$('#isAgeSelected').change(function() {\n    if($(this).is(\":checked\")) {\n        $('#txtAge').show();\n    }\nelse{\n        $('#txtAge').hide();\n    }                                          \n});\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90277", + "creator": "argie cruz", + "createdAt": 1435111994000, + "text": "
$('#chk').change(function() { \n    (this.checked)? alert('true') : alert('false');\n});\n\n\n\n($('#chk')[0].checked)? alert('true') : alert('false');\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90278", + "creator": "Vajira Lasantha", + "createdAt": 1435542128000, + "text": "

For older versions of jQuery, I had to use following,

\n\n
$('#change_plan').live('click', function() {\n     var checked = $('#change_plan').attr('checked');\n     if(checked) {\n          //Code       \n     }\n     else {\n          //Code       \n     }\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90279", + "creator": "Ormoz", + "createdAt": 1440270586000, + "text": "

Though you have proposed a JavaScript solution for your problem (displaying a textbox when a checkbox is checked), this problem could be solved just by css. With this approach, your form works for users who have disabled JavaScript.

\n\n

Assuming that you have the following HTML:

\n\n
<label for=\"show_textbox\">Show Textbox</label>\n<input id=\"show_textbox\" type=\"checkbox\" />\n<input type=\"text\" />\n
\n\n

You can use the following CSS to achieve the desired functionality:

\n\n
 #show_textbox:not(:checked) + input[type=text] {display:none;}\n
\n\n

For other scenarios, you may think of appropriate CSS selectors.

\n\n

Here is a Fiddle to demonstrate this approach.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027a", + "creator": "Sangeet Shah", + "createdAt": 1440418776000, + "text": "

This is some different method to do the same thing:

\n\n

\r\n
\r\n
$(document).ready(function (){\r\n\r\n    $('#isAgeSelected').click(function() {\r\n        // $(\"#txtAge\").toggle(this.checked);\r\n\r\n        // Using a pure CSS selector\r\n        if ($(this.checked)) {\r\n            alert('on check 1');\r\n        };\r\n\r\n        // Using jQuery's is() method\r\n        if ($(this).is(':checked')) {\r\n            alert('on checked 2');\r\n        };\r\n\r\n        //  // Using jQuery's filter() method\r\n        if ($(this).filter(':checked')) {\r\n            alert('on checked 3');\r\n        };\r\n    });\r\n});
\r\n
<script src=\"http://code.jquery.com/jquery-1.9.1.js\"></script>\r\n<input type=\"checkbox\" id=\"isAgeSelected\"/>\r\n<div id=\"txtAge\" style=\"display:none\">Age is something</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027b", + "creator": "Mohammed Safeer", + "createdAt": 1441302151000, + "text": "

Try this,

\n\n
$('#isAgeSelected').click(function() {\n    if(this.checked){\n        $(\"#txtAge\").show();\n    } else{\n        $(\"#txtAge\").hide();\n    } \n});\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027c", + "creator": "Muhammad Awais", + "createdAt": 1461845238000, + "text": "

You can use:

\n\n
  if(document.getElementById('isAgeSelected').checked)\n    $(\"#txtAge\").show();  \n  else\n    $(\"#txtAge\").hide();\n
\n\n
\n\n
if($(\"#isAgeSelected\").is(':checked'))\n  $(\"#txtAge\").show();  \nelse\n  $(\"#txtAge\").hide();\n
\n\n

Both of them should work.

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027d", + "creator": "Vikash", + "createdAt": 1461871850000, + "text": "
if($('#isAgeSelected').prop('checked')) {\n    // do your action \n}\n
\n", + "upvotes": 690, + "upvoterUsernames": [], + "downvotes": 690, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027e", + "creator": "Hamid N K", + "createdAt": 1469612814000, + "text": "

Use this:

\n\n
if ($('input[name=\"salary_in.Basic\"]:checked').length > 0)\n
\n\n

The length is greater than zero if the checkbox is checked.

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9027f", + "creator": "Lalji Dhameliya", + "createdAt": 1472470699000, + "text": "

Use:

\n\n
<input type=\"checkbox\" name=\"planned_checked\" checked id=\"planned_checked\"> Planned\n\n$(\"#planned_checked\").change(function() {\n    if($(this).prop('checked')) {\n        alert(\"Checked Box Selected\");\n    } else {\n        alert(\"Checked Box deselect\");\n    }\n});\n
\n\n

\r\n
\r\n
    $(\"#planned_checked\").change(function() {\r\n        if($(this).prop('checked')) {\r\n            alert(\"Checked Box Selected\");\r\n        } else {\r\n            alert(\"Checked Box deselect\");\r\n        }\r\n    });
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\r\n<input type=\"checkbox\" name=\"planned_checked\" checked id=\"planned_checked\"> Planned
\r\n
\r\n
\r\n

\n", + "upvotes": 260, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90280", + "creator": "Iter Ator", + "createdAt": 1475231458000, + "text": "

What about this solution?

\n\n
$(\"#txtAge\")[\n    $(\"#isAgeSelected\").is(':checked') ?\n    'show' :\n    'hide'\n]();\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90281", + "creator": "Sandeep Sherpur", + "createdAt": 1476338599000, + "text": "

This code will help you

\n\n
$('#isAgeSelected').click(function(){\n   console.log(this.checked);\n   if(this.checked == true) {\n        $(\"#txtAge\").show();\n    } else {\n       $(\"#txtAge\").hide();\n   }\n});\n
\n", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90282", + "creator": "Jacob", + "createdAt": 1495148589000, + "text": "

I'm using jQuery 1.11.1 and I had troubles with setting and reading checkbox value as well.

\n\n

I finally solved it by these two functions:

\n\n
function setCheckboxValue(checkBoxId, checked) {\n    if (checkBoxId && (checked === true || checked === false)) {\n        var elem = $('#' + checkBoxId);\n        if (checked === true) {\n            elem.attr('checked', 'checked');\n        } else {\n            elem.removeAttr('checked');\n        }\n    }\n}\n\nfunction isChecked(checkBoxId) {\n    return $('#' + checkBoxId).attr('checked') != null;\n}\n
\n\n

It might looks a little bit dirty but it solves all the wired issue I had among different types of browsers.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90283", + "creator": "Himanshu Upadhyay", + "createdAt": 1531554426000, + "text": "

Simply use it like below

\n\n
 $('#isAgeSelected').change(function() {\n     if ($(this).is(\":checked\")) { // or if($(\"#isAgeSelected\").attr('checked') == true){\n         $('#txtAge').show();\n     } else {\n         $('#txtAge').hide();\n     }\n });\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90284", + "creator": "Ir Calif", + "createdAt": 1533633825000, + "text": "

In case you need to know if a checkbox is checked in pure javascript you should use this code .

\n\n
let checkbox =document.getElementById('myCheckboxId');\nif(checkbox.checked) {\n    alert(\"element is checked\");\n} else {\n    alert(\"element is  ot checked\");\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90285", + "creator": "sedhal", + "createdAt": 1535957806000, + "text": "

I need to check the checked property of a checkbox and perform an action based on the checked property using jQuery.

\n\n

E.X -

\n\n

1) Run On load to get checkbox value if the age checkbox is checked, then I need to show a text box to enter age, else hide the text box.

\n\n

2) if the age checkbox is checked, then I need to show a text box to enter age, else hide the text box using click event of checkbox.

\n\n

so code not returns false by default:

\n\n

Try the following:

\n\n
<html>\n        <head>\n            <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n        </head>\n        <body>\n            <h1>Jquery Demo</h1>\n            <input type=\"checkbox\" name=\"isAge\" checked id=\"isAge\"> isAge <br/>\n            <div id=\"Age\" style=\"display:none\">\n              <label>Enter your age</label>\n              <input type=\"number\" name=\"age\">\n            </div>\n            <script type=\"text/javascript\">\n            if(document.getElementById('isAge').checked) {\n                $('#Age').show();\n            } else {\n                $('#Age').hide();\n            }   \n            $('#isAge').click(function() {\n                if(document.getElementById('isAge').checked) {\n                    $('#Age').show();\n                } else {\n                    $('#Age').hide();\n                }\n            }); \n            </script>\n        </body>\n    </html>\n
\n\n

Here is a modified version : https://jsfiddle.net/sedhal/0hygLtrz/7/

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90286", + "creator": "abrahamcalf", + "createdAt": 1536627094000, + "text": "

Using pure JavaScript:

\n\n
let checkbox = document.getElementById('checkboxID');\n\nif(checkbox.checked) {\n  alert('is checked');\n} else {\n  alert('not checked yet');\n}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90288", + "creator": "Javed Khan", + "createdAt": 1559105181000, + "text": "

Please try below code to check checkbox is checked or not

\n\n
$(document).ready(function(){\n\n    $(\"#isAgeSelected\").on('change',function(){\n\n    if($(\"#isAgeSelected\").is(':checked'))\n        $(\"#txtAge\").show();  // checked\n    else{\n        $(\"#txtAge\").hide();  // unchecked\n    }\n\n   });\n\n});\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90287", + "creator": "Praneeth Madush", + "createdAt": 1553828062000, + "text": "

You Can Try This code:

\n\n
$('#isAgeSelected').click(function(){\n   console.log(this.checked);\n   if(this.checked == true) {\n        $(\"#txtAge\").show();\n    } else {\n       $(\"#txtAge\").hide();\n   }\n});\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9028a", + "creator": "Ajay Katariya", + "createdAt": 1570090813000, + "text": "

You can try the change event of checkbox to track the :checked state change.

\n

\r\n
\r\n
$(\"#isAgeSelected\").on('change', function() {\n  if ($(\"#isAgeSelected\").is(':checked'))\n    alert(\"checked\");\n  else {\n    alert(\"unchecked\");\n  }\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<input type=\"checkbox\" id=\"isAgeSelected\" />\n<div id=\"txtAge\" style=\"display:none\">\n  Age is selected\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90289", + "creator": "Kamil Kiełczewski", + "createdAt": 1566967154000, + "text": "

In pure js checkbox state is easier to read

\n\n
isAgeSelected.checked\n
\n\n

\r\n
\r\n
function check() {\r\n  txtAge.style.display= isAgeSelected.checked ? 'block':'none';\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\nAge <input type=\"checkbox\" id=\"isAgeSelected\"/>\r\n\r\n<button onclick=\"check()\">Check</button>\r\n\r\n<div id=\"txtAge\" style=\"display:none\">\r\nAge is selected\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9028c", + "creator": "rcoro", + "createdAt": 1575850781000, + "text": "

Hi you can use plain Javascript, like so:

\n\n

\r\n
\r\n
document.getElementById('checkboxOption').addEventListener('click',      \r\n   event => console.log(event.target.checked)\r\n);
\r\n
<label><input type=\"checkbox\" id=\"checkboxOption\">Check Option</label>
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9028b", + "creator": "Dan Walters", + "createdAt": 1570096254000, + "text": "

To act on a checkbox being checked or unchecked on click.

\n

\r\n
\r\n
$('#customCheck1').click(function() {\n  if (this.checked) {\n    console.log('checked');\n  } else {\n    console.log('un-checked');\n  }\n});
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n\n<input type=\"checkbox\" id=\"customCheck1\">
\r\n
\r\n
\r\n

\n

EDIT: Not a nice programming expression if (boolean == true) though .checked property might return other type variables as well..

\n

It is better to use .prop("checked") instead. It returns true and false only.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9028d", + "creator": "Sarvesh Patel", + "createdAt": 1580710821000, + "text": "

\r\n
\r\n
$(document).on(\"click\",\"#isAgeSelected\",function(){\r\n  if($(this).prop(\"checked\") == true){\r\n    $(\"#txtAge\").show();\r\n  }\r\n  else if($(this).prop(\"checked\") == false){\r\n    $(\"#txtAge\").hide();\r\n  }\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n <input type=\"checkbox\" id=\"isAgeSelected\"/>\r\n\r\n<div id=\"txtAge\" style=\"display:none\">\r\n<input type=\"text\" name=\"age\" placeholder=\"Please enter age\" />\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 4605642, + "uvac": 4605708 + } + }, + { + "_id": "62f321bb082fcc3049e8feb5", + "title": "How do I include a JavaScript file in another JavaScript file?", + "title-lowercase": "how do i include a javascript file in another javascript file?", + "creator": "Alec Smart", + "createdAt": 1244116790000, + "status": "open", + "text": "

How do I include a JavaScript file inside another JavaScript file, similar to @import in CSS?

\n", + "upvotes": 8061, + "upvoterUsernames": [], + "downvotes": 2023, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 3987599, + "answers": 62, + "answerItems": [ + { + "_id": "62f321bd082fcc3049e900cd", + "creator": "Svitlana Maksymchuk", + "createdAt": 1244116953000, + "text": "

It is possible to dynamically generate a JavaScript tag and append it to HTML document from inside other JavaScript code. This will load targeted JavaScript file.

\n\n
function includeJs(jsFilePath) {\n    var js = document.createElement(\"script\");\n\n    js.type = \"text/javascript\";\n    js.src = jsFilePath;\n\n    document.body.appendChild(js);\n}\n\nincludeJs(\"/path/to/some/file.js\");\n
\n", + "upvotes": 226, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ce", + "creator": "Arnaud Gouder de Beauregard", + "createdAt": 1244117096000, + "text": "

Maybe you can use this function that I found on this page How do I include a JavaScript file in a JavaScript file?:

\n\n
function include(filename)\n{\n    var head = document.getElementsByTagName('head')[0];\n\n    var script = document.createElement('script');\n    script.src = filename;\n    script.type = 'text/javascript';\n\n    head.appendChild(script)\n}\n
\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900cf", + "creator": "nornagon", + "createdAt": 1264224045000, + "text": "

I just wrote this JavaScript code (using Prototype for DOM manipulation):

\n\n
var require = (function() {\n    var _required = {};\n    return (function(url, callback) {\n        if (typeof url == 'object') {\n            // We've (hopefully) got an array: time to chain!\n            if (url.length > 1) {\n                // Load the nth file as soon as everything up to the\n                // n-1th one is done.\n                require(url.slice(0, url.length - 1), function() {\n                    require(url[url.length - 1], callback);\n                });\n            } else if (url.length == 1) {\n                require(url[0], callback);\n            }\n            return;\n        }\n        if (typeof _required[url] == 'undefined') {\n            // Haven't loaded this URL yet; gogogo!\n            _required[url] = [];\n\n            var script = new Element('script', {\n                src: url,\n                type: 'text/javascript'\n            });\n            script.observe('load', function() {\n                console.log(\"script \" + url + \" loaded.\");\n                _required[url].each(function(cb) {\n                    cb.call(); // TODO: does this execute in the right context?\n                });\n                _required[url] = true;\n            });\n\n            $$('head')[0].insert(script);\n        } else if (typeof _required[url] == 'boolean') {\n            // We already loaded the thing, so go ahead.\n            if (callback) {\n                callback.call();\n            }\n            return;\n        }\n\n        if (callback) {\n            _required[url].push(callback);\n        }\n    });\n})();\n
\n\n

Usage:

\n\n
<script src=\"prototype.js\"></script>\n<script src=\"require.js\"></script>\n<script>\n    require(['foo.js','bar.js'], function () {\n        /* Use foo.js and bar.js here */\n    });\n</script>\n
\n\n

Gist: http://gist.github.com/284442.

\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d0", + "creator": "Calmarius", + "createdAt": 1293483839000, + "text": "

You can also assemble your scripts using PHP:

\n\n

File main.js.php:

\n\n
<?php\n    header('Content-type:text/javascript; charset=utf-8');\n    include_once(\"foo.js.php\");\n    include_once(\"bar.js.php\");\n?>\n\n// Main JavaScript code goes here\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d1", + "creator": "Ariel", + "createdAt": 1315506146000, + "text": "

Another way, that in my opinion is much cleaner, is to make a synchronous Ajax request instead of using a <script> tag. Which is also how Node.js handles includes.

\n\n

Here's an example using jQuery:

\n\n
function require(script) {\n    $.ajax({\n        url: script,\n        dataType: \"script\",\n        async: false,           // <-- This is the key\n        success: function () {\n            // all good...\n        },\n        error: function () {\n            throw new Error(\"Could not load script \" + script);\n        }\n    });\n}\n
\n\n

You can then use it in your code as you'd usually use an include:

\n\n
require(\"/scripts/subscript.js\");\n
\n\n

And be able to call a function from the required script in the next line:

\n\n
subscript.doSomethingCool(); \n
\n", + "upvotes": 212, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d3", + "creator": "Wayne Molina", + "createdAt": 1327008459000, + "text": "

I have created a function that will allow you to use similar verbiage to C#/Java to include a JavaScript file. I've tested it a little bit even from inside of another JavaScript file and it seems to work. It does require jQuery though for a bit of \"magic\" at the end.

\n\n

I put this code in a file at the root of my script directory (I named it global.js, but you can use whatever you want. Unless I'm mistaken this and jQuery should be the only required scripts on a given page. Keep in mind this is largely untested beyond some basic usage, so there may or may not be any issues with the way I've done it; use at your own risk yadda yadda I am not responsible if you screw anything up yadda yadda:

\n\n
/**\n* @fileoverview This file stores global functions that are required by other libraries.\n*/\n\nif (typeof(jQuery) === 'undefined') {\n    throw 'jQuery is required.';\n}\n\n/** Defines the base script directory that all .js files are assumed to be organized under. */\nvar BASE_DIR = 'js/';\n\n/**\n* Loads the specified file, outputting it to the <head> HTMLElement.\n*\n* This method mimics the use of using in C# or import in Java, allowing\n* JavaScript files to \"load\" other JavaScript files that they depend on\n* using a familiar syntax.\n*\n* This method assumes all scripts are under a directory at the root and will\n* append the .js file extension automatically.\n*\n* @param {string} file A file path to load using C#/Java \"dot\" syntax.\n*\n* Example Usage:\n* imports('core.utils.extensions');\n* This will output: <script type=\"text/javascript\" src=\"/js/core/utils/extensions.js\"></script>\n*/\nfunction imports(file) {\n    var fileName = file.substr(file.lastIndexOf('.') + 1, file.length);\n\n    // Convert PascalCase name to underscore_separated_name\n    var regex = new RegExp(/([A-Z])/g);\n    if (regex.test(fileName)) {\n        var separated = fileName.replace(regex, \",$1\").replace(',', '');\n        fileName = separated.replace(/[,]/g, '_');\n    }\n\n    // Remove the original JavaScript file name to replace with underscore version\n    file = file.substr(0, file.lastIndexOf('.'));\n\n    // Convert the dot syntax to directory syntax to actually load the file\n    if (file.indexOf('.') > 0) {\n        file = file.replace(/[.]/g, '/');\n    }\n\n    var src = BASE_DIR + file + '/' + fileName.toLowerCase() + '.js';\n    var script = document.createElement('script');\n    script.type = 'text/javascript';\n    script.src = src;\n\n    $('head').find('script:last').append(script);\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d2", + "creator": "rgb_life", + "createdAt": 1322717805000, + "text": "

I came to this question because I was looking for a simple way to maintain a collection of useful JavaScript plugins. After seeing some of the solutions here, I came up with this:

\n\n
    \n
  1. Set up a file called \"plugins.js\" (or extensions.js or whatever you want). Keep your plugin files together with that one master file.

  2. \n
  3. plugins.js will have an array called pluginNames[] that we will iterate over each(),\nthen append a <script> tag to the head for each plugin

  4. \n
\n\n
//set array to be updated when we add or remove plugin files\nvar pluginNames = [\"lettering\", \"fittext\", \"butterjam\", etc.];\n\n//one script tag for each plugin\n$.each(pluginNames, function(){\n    $('head').append('<script src=\"js/plugins/' + this + '.js\"></script>');\n});\n
\n\n
    \n
  1. Manually call just the one file in your head:
    \n<script src=\"js/plugins/plugins.js\"></script>
  2. \n
\n\n

BUT:

\n\n

Even though all of the plugins get dropped into the head tag the way they ought to, they don't always get run by the browser when you click into the page or refresh.

\n\n

I've found it's more reliable to just write the script tags in a PHP include. You only have to write it once and that's just as much work as calling the plugin using JavaScript.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d4", + "creator": "jpierson", + "createdAt": 1330834752000, + "text": "

In a past project I had quite a bit of success using ajile to do imports of reusable JavaScript files. I always wished there was a feature for this built into JavaScript itself.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d5", + "creator": "JMawer", + "createdAt": 1339102087000, + "text": "

Or rather than including at run time, use a script to concatenate prior to upload.

\n\n

I use Sprockets (I don't know if there are others). You build your JavaScript code in separate files and include comments that are processed by the Sprockets engine as includes. For development you can include files sequentially, then for production to merge them...

\n\n

See also:

\n\n\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d6", + "creator": "John Strickler", + "createdAt": 1339102552000, + "text": "

If anyone is looking for something more advanced, try out RequireJS. You'll get added benefits such as dependency management, better concurrency, and avoid duplication (that is, retrieving a script more than once).

\n\n

You can write your JavaScript files in \"modules\" and then reference them as dependencies in other scripts. Or you can use RequireJS as a simple \"go get this script\" solution.

\n\n

Example:

\n\n

Define dependencies as modules:

\n\n

some-dependency.js

\n\n
define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {\n\n     //Your actual script goes here.   \n     //The dependent scripts will be fetched if necessary.\n\n     return libraryObject;  //For example, jQuery object\n});\n
\n\n

implementation.js is your \"main\" JavaScript file that depends on some-dependency.js

\n\n
require(['some-dependency'], function(dependency) {\n\n    //Your script goes here\n    //some-dependency.js is fetched.   \n    //Then your script is executed\n});\n
\n\n

Excerpt from the GitHub README:

\n\n
\n

RequireJS loads plain JavaScript files as well as more defined\n modules. It is optimized for in-browser use, including in a Web\n Worker, but it can be used in other JavaScript environments, like\n Rhino and Node. It implements the Asynchronous Module API.

\n \n

RequireJS uses plain script tags to load modules/files, so it should\n allow for easy debugging. It can be used simply to load existing\n JavaScript files, so you can add it to your existing project without\n having to re-write your JavaScript files.

\n \n

...

\n
\n", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d7", + "creator": "Imdad", + "createdAt": 1341322374000, + "text": "

There is a good news for you. Very soon you will be able to load JavaScript code easily. It will become a standard way of importing modules of JavaScript code and will be part of core JavaScript itself.

\n\n

You simply have to write import cond from 'cond.js'; to load a macro named cond from a file cond.js.

\n\n

So you don't have to rely upon any JavaScript framework nor do you have to explicitly make Ajax calls.

\n\n

Refer to:

\n\n\n", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322aa082fcc3049e9128d", + "creator": "David Spector", + "createdAt": 1622130768000, + "text": "Seven years later, this answer doesn't work: "SyntaxError: import declarations may only appear at top level of a module".", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322aa082fcc3049e9128e", + "creator": "Imdad", + "createdAt": 1622462797000, + "text": "Share your code what you are trying to do.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900d8", + "creator": "Sam4Code", + "createdAt": 1345610014000, + "text": "
var js = document.createElement(\"script\");\n\njs.type = \"text/javascript\";\njs.src = jsFilePath;\n\ndocument.body.appendChild(js);\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900d9", + "creator": "weageoo", + "createdAt": 1345925841000, + "text": "

Better use the jQuery way. To delay the ready event, first call $.holdReady(true).\nExample (source):

\n\n
$.holdReady(true);\n$.getScript(\"myplugin.js\", function() {\n    $.holdReady(false);\n});\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900da", + "creator": "emolaus", + "createdAt": 1358345073000, + "text": "

Don't forget to check out LAB.js!

\n\n
<script type=\"text/javascript\">\n       $LAB\n       .script(\"jquery-1.8.3.js\").wait()\n       .script(\"scripts/clientscript.js\");      \n</script>\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900db", + "creator": "Duncan", + "createdAt": 1362671450000, + "text": "

Now, I may be totally misguided, but here's what I've recently started doing... \nStart and end your JavaScript files with a carriage return, place in the PHP script, followed by one more carriage return.\nThe JavaScript comment \"//\" is ignored by PHP so the inclusion happens anyway. The purpose for the carriage returns is so that the first line of your included JavaScript isn't commented out.

\n\n

Technically, you don't need the comment, but it posts errors in Dreamweaver that annoy me. If you're scripting in an IDE that doesn't post errors, you shouldn't need the comment or the carriage returns.

\n\n
\\n\n//<?php require_once(\"path/to/javascript/dependency.js\"); ?>\n\nfunction myFunction(){\n    // stuff\n}\n\\n\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900dd", + "creator": "tggagne", + "createdAt": 1364153532000, + "text": "

This should do:

\n\n
xhr = new XMLHttpRequest();\nxhr.open(\"GET\", \"/soap/ajax/11.0/connection.js\", false);\nxhr.send();\neval(xhr.responseText);\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900dc", + "creator": "Robert A", + "createdAt": 1362923726000, + "text": "
var s=[\"Hscript.js\",\"checkRobert.js\",\"Hscript.js\"];\nfor(i=0;i<s.length;i++){\n  var script=document.createElement(\"script\");\n  script.type=\"text/javascript\";\n  script.src=s[i];\n  document.getElementsByTagName(\"head\")[0].appendChild(script)\n};\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900df", + "creator": "Dmitry Sheiko", + "createdAt": 1373751843000, + "text": "

Most of solutions shown here imply dynamical loading. I was searching instead for a compiler which assemble all the depended files into a single output file. The same as Less/Sass preprocessors deal with the CSS @import at-rule. Since I didn't find anything decent of this sort, I wrote a simple tool solving the issue.

\n\n

So here is the compiler, https://github.com/dsheiko/jsic, which replaces $import(\"file-path\") with the requested file content securely. Here is the corresponding Grunt plugin: https://github.com/dsheiko/grunt-jsic.

\n\n

On the jQuery master branch, they simply concatenate atomic source files into a single one starting with intro.js and ending with outtro.js. That doesn't suits me as it provides no flexibility on the source code design. Check out how it works with jsic:

\n\n

src/main.js

\n\n
var foo = $import(\"./Form/Input/Tel\");\n
\n\n

src/Form/Input/Tel.js

\n\n
function() {\n    return {\n          prop: \"\",\n          method: function(){}\n    }\n}\n
\n\n

Now we can run the compiler:

\n\n
node jsic.js src/main.js build/mail.js\n
\n\n

And get the combined file

\n\n

build/main.js

\n\n
var foo = function() {\n    return {\n          prop: \"\",\n          method: function(){}\n    }\n};\n
\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900de", + "creator": "Alexis Dumas", + "createdAt": 1370028694000, + "text": "

My usual method is:

\n\n
var require = function (src, cb) {\n    cb = cb || function () {};\n\n    var newScriptTag = document.createElement('script'),\n        firstScriptTag = document.getElementsByTagName('script')[0];\n    newScriptTag.src = src;\n    newScriptTag.async = true;\n    newScriptTag.onload = newScriptTag.onreadystatechange = function () {\n        (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') && (cb());\n    };\n    firstScriptTag.parentNode.insertBefore(newScriptTag, firstScriptTag);\n}\n
\n\n

It works great and uses no page-reloads for me. I've tried the AJAX method (one of the other answers) but it doesn't seem to work as nicely for me.

\n\n

Here's an explanation of how the code works for those that are curious: essentially, it creates a new script tag (after the first one) of the URL. It sets it to asynchronous mode so it doesn't block the rest of the code, but calls a callback when the readyState (the state of the content to be loaded) changes to 'loaded'.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e0", + "creator": "Vicky Gonsalves", + "createdAt": 1382758976000, + "text": "

This script will add a JavaScript file to the top of any other <script> tag:

\n
(function () {\n    var li = document.createElement('script'); \n    li.type = 'text/javascript'; \n    li.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"; \n    li.async = true; \n    var s = document.getElementsByTagName('script')[0]; \n    s.parentNode.insertBefore(li, s);\n})();\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e1", + "creator": "Marcin", + "createdAt": 1383759347000, + "text": "

Here is a Grunt plugin allowing you to use @import \"path/to/file.js\"; syntax in any file including JavaScript files. It can be paired with uglify or watch or any other plugin.

\n\n

It can be installed with npm install: https://npmjs.org/package/grunt-import

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e2", + "creator": "Venu immadi", + "createdAt": 1384334297000, + "text": "

If you want it in pure JavaScript, you can use document.write.

\n
document.write('<script src="myscript.js" type="text/javascript"></script>');\n
\n

If you use the jQuery library, you can use the $.getScript method.

\n
$.getScript("another_script.js");\n
\n", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e3", + "creator": "Ale", + "createdAt": 1385145633000, + "text": "

There is also Head.js. It is very easy to deal with:

\n\n
head.load(\"js/jquery.min.js\",\n          \"js/jquery.someplugin.js\",\n          \"js/jquery.someplugin.css\", function() {\n  alert(\"Everything is ok!\");\n});\n
\n\n

As you see, it's easier than Require.js and as convenient as jQuery's $.getScript method. It also has some advanced features, like conditional loading, feature detection and much more.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e5", + "creator": "Isaac Gregson", + "createdAt": 1394772051000, + "text": "

The @import syntax for achieving CSS-like JavaScript importing is possible using a tool such as Mixture via their special .mix file type (see here). I assume the application does this via one of above-mentioned methods.

\n

From the Mixture documentation on .mix files:

\n
\n

Mix files are simply .js or .css files with .mix. in the file name. A\nmix file simply extends the functionality of a normal style or\nscript file and allows you to import and combine.

\n
\n

Here's an example .mix file that combines multiple .js files into one:

\n
// scripts-global.mix.js\n// Plugins - Global\n\n@import "global-plugins/headroom.js";\n@import "global-plugins/retina-1.1.0.js";\n@import "global-plugins/isotope.js";\n@import "global-plugins/jquery.fitvids.js";\n
\n

Mixture outputs this as scripts-global.js and also as a minified version (scripts-global.min.js).

\n

Note: I'm not in any way affiliated with Mixture, other than using it as a front-end development tool. I came across this question upon seeing a .mix JavaScript file in action (in one of the Mixture boilerplates) and being a bit confused by it ("you can do this?" I thought to myself). Then I realized that it was an application-specific file type (somewhat disappointing, agreed). Nevertheless, figured the knowledge might be helpful for others.

\n

Note: Mixture was discontinued on 2016/07/26 (after being open sourced on 2015/04/12).

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e4", + "creator": "heinob", + "createdAt": 1386762863000, + "text": "

Here is a synchronous version without jQuery:

\n\n
function myRequire( url ) {\n    var ajax = new XMLHttpRequest();\n    ajax.open( 'GET', url, false ); // <-- the 'false' makes it synchronous\n    ajax.onreadystatechange = function () {\n        var script = ajax.response || ajax.responseText;\n        if (ajax.readyState === 4) {\n            switch( ajax.status) {\n                case 200:\n                    eval.apply( window, [script] );\n                    console.log(\"script loaded: \", url);\n                    break;\n                default:\n                    console.log(\"ERROR: script not loaded: \", url);\n            }\n        }\n    };\n    ajax.send(null);\n}\n
\n\n

Note that to get this working cross-domain, the server will need to set allow-origin header in its response.

\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e7", + "creator": "tfont", + "createdAt": 1423358384000, + "text": "

Keep it nice, short, simple, and maintainable! :]

\n
// Third-party plugins / script (don't forget the full path is necessary)\nvar FULL_PATH = '', s =\n[\n    FULL_PATH + 'plugins/script.js'      // Script example\n    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library\n    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS\n    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS\n];\n\nfunction load(url)\n{\n    var ajax = new XMLHttpRequest();\n    ajax.open('GET', url, false);\n    ajax.onreadystatechange = function ()\n    {\n        var script = ajax.response || ajax.responseText;\n        if (ajax.readyState === 4)\n        {\n            switch(ajax.status)\n            {\n                case 200:\n                    eval.apply( window, [script] );\n                    console.log("library loaded: ", url);\n                    break;\n                default:\n                    console.log("ERROR: library not loaded: ", url);\n            }\n        }\n    };\n    ajax.send(null);\n}\n\n// Initialize a single load\nload('plugins/script.js');\n\n// Initialize a full load of scripts\nif (s.length > 0)\n{\n    for (i = 0; i < s.length; i++)\n    {\n        load(s[i]);\n    }\n}\n
\n

This code is simply a short functional example that could require additional feature functionality for full support on any (or given) platform.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ab082fcc3049e9129a", + "creator": "Peter Mortensen", + "createdAt": 1601758424000, + "text": "An explanation would be in order. E.g. what is the idea (principle of operation) and how does it work?", + "upvotes": 2206, + "upvoterUsernames": [], + "downvotes": 2206, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900e6", + "creator": "Turnerj", + "createdAt": 1420102695000, + "text": "

In case you are using Web Workers and want to include additional scripts in the scope of the worker, the other answers provided about adding scripts to the head tag, etc. will not work for you.

\n\n

Fortunately, Web Workers have their own importScripts function which is a global function in the scope of the Web Worker, native to the browser itself as it is part of the specification.

\n\n

Alternatively, as the second highest voted answer to your question highlights, RequireJS can also handle including scripts inside a Web Worker (likely calling importScripts itself, but with a few other useful features).

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e9", + "creator": "Dan Dascalescu", + "createdAt": 1436323272000, + "text": "

Here's the generalized version of how Facebook does it for their ubiquitous Like button:

\n\n

\r\n
\r\n
<script>\r\n  var firstScript = document.getElementsByTagName('script')[0],\r\n      js = document.createElement('script');\r\n  js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';\r\n  js.onload = function () {\r\n    // do stuff with your dynamically loaded script\r\n    snowStorm.snowColor = '#99ccff';\r\n  };\r\n  firstScript.parentNode.insertBefore(js, firstScript);\r\n</script>
\r\n
\r\n
\r\n

\n\n

If it works for Facebook, it will work for you.

\n\n

The reason why we look for the first script element instead of head or body is because some browsers don't create one if missing, but we're guaranteed to have a script element - this one. Read more at http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/.

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900e8", + "creator": "draupnie", + "createdAt": 1429235814000, + "text": "

Statement import is in ECMAScript 6.

\n\n

Syntax

\n\n
import name from \"module-name\";\nimport { member } from \"module-name\";\nimport { member as alias } from \"module-name\";\nimport { member1 , member2 } from \"module-name\";\nimport { member1 , member2 as alias2 , [...] } from \"module-name\";\nimport name , { member [ , [...] ] } from \"module-name\";\nimport \"module-name\" as name;\n
\n", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ea", + "creator": "Manohar Reddy Poreddy", + "createdAt": 1437531330000, + "text": "

I had a simple issue, but I was baffled by responses to this question.

\n\n

I had to use a variable (myVar1) defined in one JavaScript file (myvariables.js) in another JavaScript file (main.js).

\n\n

For this I did as below:

\n\n

Loaded the JavaScript code in the HTML file, in the correct order, myvariables.js first, then main.js:

\n\n
<html>\n    <body onload=\"bodyReady();\" >\n\n        <script src=\"myvariables.js\" > </script>\n        <script src=\"main.js\" > </script>\n\n        <!-- Some other code -->\n    </body>\n</html>\n
\n\n

File: myvariables.js

\n\n
var myVar1 = \"I am variable from myvariables.js\";\n
\n\n

File: main.js

\n\n
// ...\nfunction bodyReady() {\n    // ...\n    alert (myVar1);    // This shows \"I am variable from myvariables.js\", which I needed\n    // ...\n}\n// ...\n
\n\n

As you saw, I had use a variable in one JavaScript file in another JavaScript file, but I didn't need to include one in another. I just needed to ensure that the first JavaScript file loaded before the second JavaScript file, and, the first JavaScript file's variables are accessible in the second JavaScript file, automatically.

\n\n

This saved my day. I hope this helps.

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900eb", + "creator": "Adem İlhan", + "createdAt": 1437720793000, + "text": "

If your intention to load the JavaScript file is using the functions from the imported/included file, you can also define a global object and set the functions as object items. For instance:

\n\n

global.js

\n\n
A = {};\n
\n\n

file1.js

\n\n
A.func1 = function() {\n  console.log(\"func1\");\n}\n
\n\n

file2.js

\n\n
A.func2 = function() {\n  console.log(\"func2\");\n}\n
\n\n

main.js

\n\n
A.func1();\nA.func2();\n
\n\n

You just need to be careful when you are including scripts in an HTML file. The order should be as in below:

\n\n
<head>\n  <script type=\"text/javascript\" src=\"global.js\"></script>\n  <script type=\"text/javascript\" src=\"file1.js\"></script>\n  <script type=\"text/javascript\" src=\"file2.js\"></script>\n  <script type=\"text/javascript\" src=\"main.js\"></script>\n</head>\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ed", + "creator": "MiBol", + "createdAt": 1470597248000, + "text": "

I have the requirement to asynchronously load an array of JavaScript files and at the final make a callback. Basically my best approach is the following:

\n\n
// Load a JavaScript file from other JavaScript file\nfunction loadScript(urlPack, callback) {\n    var url = urlPack.shift();\n    var subCallback;\n\n    if (urlPack.length == 0) subCallback = callback;\n    else subCallback = function () {\n        console.log(\"Log script: \" + new Date().getTime());\n        loadScript(urlPack, callback);\n    }\n\n    // Adding the script tag to the head as suggested before\n    var head = document.getElementsByTagName('head')[0];\n    var script = document.createElement('script');\n    script.type = 'text/javascript';\n    script.src = url;\n\n    // Then bind the event to the callback function.\n    // There are several events for cross browser compatibility.\n    script.onreadystatechange = subCallback;\n    script.onload = subCallback;\n\n    // Fire the loading\n    head.appendChild(script);\n}\n
\n\n

Example:

\n\n
loadScript(\n[\n    \"js/DataTable/jquery.dataTables.js\",\n    \"js/DataTable/dataTables.bootstrap.js\",\n    \"js/DataTable/dataTables.buttons.min.js\",\n    \"js/DataTable/dataTables.colReorder.min.js\",\n    \"js/DataTable/dataTables.fixedHeader.min.js\",\n    \"js/DataTable/buttons.bootstrap.min.js\",\n    \"js/DataTable/buttons.colVis.min.js\",\n    \"js/DataTable/buttons.html5.min.js\"\n], function() { gpLoad(params); });\n
\n\n

The second script will not load until the first is completely loaded, and so...

\n\n

Results:

\n\n

\"Result\"

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ec", + "creator": "Rahul Srivastava", + "createdAt": 1464134802000, + "text": "

I basically do it like the following, creating a new element and attach that to head:

\n\n
var x = document.createElement('script');\nx.src = 'http://example.com/test.js';\ndocument.getElementsByTagName(\"head\")[0].appendChild(x);\n
\n\n

In jQuery:

\n\n
// jQuery\n$.getScript('/path/to/imported/script.js', function()\n{\n    // Script is now loaded and executed.\n    // Put your dependent JavaScript code here.\n});\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f5", + "creator": "Dmitry Sheiko", + "createdAt": 1500563702000, + "text": "

In a modern language with the check if script has already been loaded, it would be:

\n
function loadJs( url ){\n  return new Promise(( resolve, reject ) => {\n    if (document.querySelector( `head > script[ src = "${url}" ]`) !== null ){\n        console.warn( `script already loaded: ${url}` );\n        resolve();\n    }\n    const script = document.createElement( "script" );\n    script.src = url;\n    script.onload = resolve;\n    script.onerror = function( reason ){\n        // This can be useful for your error-handling code\n        reason.message = `error trying to load script ${url}`;\n        reject( reason );\n    };\n    document.head.appendChild( script );\n  });\n}\n
\n

Usage (async/await):

\n
try { await loadJs("https://.../script.js"); }\ncatch(error) { console.log(error); }\n
\n

or

\n
await loadJs( "https://.../script.js" ).catch( err => {} );\n
\n

Usage (Promise):

\n
loadJs( "https://.../script.js" ).then( res => {} ).catch( err => {} );\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322e9082fcc3049e912a3", + "creator": "Flimm", + "createdAt": 1620824434000, + "text": "url needs to be properly escaped here: `head > script[ src = "${url}" ]`", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 102, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900f4", + "creator": "Akshay Vijay Jain", + "createdAt": 1494824457000, + "text": "

It's very simple. Suppose you want to import file A.js in file B.js.

\n\n

Now it's sure you have linked B.js in an HTML file, then just link A.js before B.js in that HTML file. Then the public variables of A.js will be available inside the B.js

\n\n

This does not require a complicated answer.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f6", + "creator": "KthProg", + "createdAt": 1523561897000, + "text": "

Although these answers are great, there is a simple \"solution\" that has been around since script loading existed, and it will cover 99.999% of most people's use cases. Just include the script you need before the script that requires it. For most projects it does not take long to determine which scripts are needed and in what order.

\n\n
<!DOCTYPE HTML>\n<html>\n    <head>\n        <script src=\"script1.js\"></script>\n        <script src=\"script2.js\"></script>\n    </head>\n    <body></body>\n</html>\n
\n\n

If script2 requires script1, this really is the absolute easiest way to do something like this. I'm very surprised no-one has brought this up, as it's the most obvious and simplest answer that will apply in nearly every single case.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322e9082fcc3049e912a5", + "creator": "Peter Mortensen", + "createdAt": 1601757102000, + "text": "But this only works in a web browser? What about offline unit testing (say, under Node.js)?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322e9082fcc3049e912a6", + "creator": "Manohar Reddy Poreddy", + "createdAt": 1632451517000, + "text": "This answer is similar to a detailed 2015 answer here - stackoverflow.com/a/31552759/984471", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900f7", + "creator": "Adam111p", + "createdAt": 1524512118000, + "text": "

Please note that we usually use static scripts. So we want to be taken from the cache as much as possible.

\n

This saves network traffic and speeds up landing.

\n

Usage

\n
$.cachedScript( "ajax/test.js" ).done(function( script, textStatus ) {\n  console.log( textStatus );\n});\n
\n

The cache: true option has been added to the Ajax method.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f8", + "creator": "eQ19", + "createdAt": 1526385469000, + "text": "

If you find there are two or more scripts occupying the same function when they are called, and we cannot be include them at the same time, we need to do it dynamically by user selection.

\n

Including another file in jQuery using $.getScript works since the script will not be cached by default. So we are safe to call other scripts. The calls can be arranged like this:

\n

HTML

\n
<select class="choice">\n  <option value="script1" selected>Script-1</option>\n  <option value="script2">Script-2</option>\n</select>\n
\n

JavaScript

\n
  $(".choice").change(on_change);\n\n    var url = "https://example.com";\n    $.url1 = url + "/script1.js";\n    $.url2 = url + "/script2.js";\n\n  function on_change() {\n    if ($(".choice").val()=="script1") {\n        script1();\n    } else {\n         script2();\n    }\n\n    // script1\n    function script1() {\n      $.getScript($.url1, function( data, textStatus, jqxhr ) {\n          // Execute here\n      });\n    }\n\n    // script2\n    function script2() {\n       $.getScript($.url2, function( data, textStatus, jqxhr ) {\n          // Execute here\n      });\n    }\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f9", + "creator": "Alireza", + "createdAt": 1526992881000, + "text": "

Yes, there is...

\n

Keep reading. In ES6, we can export and import part or whole JavaScript file into another one...

\n

But wait, ES6 is not supported in all the browsers, so you need to transpile it using babel.js for example...

\n

So you create a class like below:

\n
class Person {\n  constructor(name) {\n    this.name = name;\n  }\n\n  build() {\n    return new Person(this);\n  }\n}\n\nmodule.exports = Person;\n
\n

In another JavaScript file, do the import like:

\n
import { Person } from 'Person';\n
\n

You also can require the file like:

\n
const Person = require('./Person');\n
\n

If you are using an older JavaScript version you can use requirejs:

\n
requirejs(["helper/util"], function(util) {\n    // This function is called when scripts/helper/util.js is loaded.\n    // If util.js calls define(), then this function is not fired until\n    // util's dependencies have loaded, and the util argument will hold\n    // the module value for "helper/util".\n});\n
\n

If you want to stick to older version of stuff, like jQuery, you can also use something like getScript:

\n
jQuery.getScript('./another-script.js', function() {\n    // Call back after another-script loaded\n});\n
\n

Last, but not the least, don't forget you can do the traditional way of putting a script together using the <script> tag...

\n
<script src="./first-script.js"></script>\n<script src="./second-script.js"></script>\n<script src="./third-script.js"></script>\n
\n

There are also the async and defer attributes which I should mention here...

\n
\n

Note: There are several ways an external script can be executed:

\n\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900fa", + "creator": "chickens", + "createdAt": 1529734526000, + "text": "

For Node.js only, this worked for me the best!

\n

I've tried most solutions here, but none helped me about just being able to load another file without changing scope. Finally I used this. Which preserves the scope and everything. It is as good as your code is in that point.

\n
const fs = require('fs');\neval(fs.readFileSync('file.js') + '');\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322e9082fcc3049e912aa", + "creator": "Peter Mortensen", + "createdAt": 1601756430000, + "text": "Why the last term? It doesn't seem to do anything.", + "upvotes": 258, + "upvoterUsernames": [], + "downvotes": 258, + "downvoterUsernames": [] + }, + { + "_id": "62f322e9082fcc3049e912ab", + "creator": "chickens", + "createdAt": 1601814024000, + "text": "readFileSync returns buffer. + '' converts that into string.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e900fb", + "creator": "jasonleonhard", + "createdAt": 1532396736000, + "text": "

Import and export modules using ES6 that work with Node.js

\n\n

Name files with .mjs extension instead of .js

\n\n

Create files

\n\n
touch main.mjs lib.mjs\n
\n\n

main.js

\n\n
import { add } from './lib.mjs';\nconsole.log(add(40, 2));\n
\n\n

lib.mjs

\n\n
export let add = (x,y) => {\n  return x + y\n}\n
\n\n

Run

\n\n
node --experimental-modules main.js\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900fd", + "creator": "MishkuMoss", + "createdAt": 1547722000000, + "text": "

I tried this problem with another approach,

\n\n

Ordering of script importing, has no effect in here.

\n\n

index.html

\n\n
<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>Trials</title>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <script src=\"main.js\"></script>\n    <script src=\"scriptA.js\"></script>\n</head>\n\n<body>\n<h3>testing js in js (check console logs)</h3>\n<button onclick=\"fnClick()\">TEST</button>\n</body>\n\n</html>\n
\n\n

main.js

\n\n
function fnClick() {\n  console.log('From\\tAAAAA');\n  var pro = myExpo.hello();\n  console.log(pro);\n}\n
\n\n

scriptA.js

\n\n
myExpo = {\n    hello: function () {\n        console.log('From\\tBBBBB');\n        return \"Hello\";\n    }\n}\n
\n\n

and the result is

\n\n
From    AAAAA\nFrom    BBBBB\nHello\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900fc", + "creator": "Willem van der Veen", + "createdAt": 1533921090000, + "text": "

There are several ways to implement modules in JavaScript. Here are the two most popular ones:

\n

ES6 Modules

\n

Browsers do not support this moduling system yet, so in order for you to use this syntax you must use a bundler like Webpack. Using a bundler is better anyway because this can combine all of your different files into a single (or a couple of related) files. This will serve the files from the server to the client faster because each HTTP request has some associated overhead accompanied with it. Thus by reducing the overall HTTP request we improve the performance. Here is an example of ES6 modules:

\n
// main.js file\n\nexport function add (a, b) {\n  return a + b;\n}\n\nexport default function multiply (a, b) {\n  return a * b;\n}\n\n\n// test.js file\n\nimport {add}, multiply from './main';   // For named exports between curly braces {export1, export2}\n                                        // For default exports without {}\n\nconsole.log(multiply(2, 2));  // logs 4\n\nconsole.log(add(1, 2));  // logs 3\n
\n

CommonJS (used in Node.js)

\n

This moduling system is used in Node.js. You basically add your exports to an object which is called module.exports. You then can access this object via a require('modulePath'). Important here is to realize that these modules are being cached, so if you require() a certain module twice it will return the already created module.

\n
// main.js file\n\nfunction add (a, b) {\n  return a + b;\n}\n\nmodule.exports = add;  // Here we add our 'add' function to the exports object\n\n\n// test.js file\n\nconst add = require('./main');\n\nconsole.log(add(1,2));  // logs 3\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900fe", + "creator": "Andrej", + "createdAt": 1559359298000, + "text": "

You can use my loadScript ES module for loading of the JavaScript files.

\n

Usage:

\n

In your head tag, include the following code:

\n
<script src="https://raw.githack.com/anhr/loadScriptNodeJS/master/build/loadScript.js"></script>\n
\n

or

\n
<script src="https://raw.githack.com/anhr/loadScriptNodeJS/master/build/loadScript.min.js"></script>\n
\n

Now you can use window.loadScript for loading of your JavaScript files.

\n

loadScript.async( src, [options] )

\n

Asynchronous load JavaScript file.

\n

src: URL of an external script file or array of the script file names.

\n

options: the following options are available

\n
onload: function () The onload event occurs when a script has been loaded. Default is undefined.\n\nonerror: function ( str, e ) The onerror event occurs when an error has been occurred. The default is undefined.\n\n    str: error details\n\n    e: event\n\nappendTo: The node to which the new script will be append. The default is the head node.\n
\n

For example

\n
loadScript.async( "JavaScript.js",\n        {\n            onload: function () {\n\n                var str = 'file has been loaded successfully';\n                console.log( str );\n            },\n            onerror: function ( str, e ) {\n\n                console.error( str );\n            },\n        } );\n
\n

Example of usage

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ff", + "creator": "Kamil Dąbrowski", + "createdAt": 1562168629000, + "text": "

A little extension to the library from Dan Dascalescu's answer taken from the Facebook idea.

\n
(function() {\nvar __ = {};\nthis._ = function(name, callback) {\n    if(__[name]==undefined) {\n        __[name] = true;\n        var firstScript = document.getElementsByTagName('script')[0],\n          js = document.createElement('script');\n          js.src =  name;\n          js.onload = callback;\n          firstScript.parentNode.insertBefore(js, firstScript);\n    }\n}\n})();\n\n(new _('https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js', function() {\n snowStorm.snowColor = '#99ccff';\n}));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90100", + "creator": "Kamil Kiełczewski", + "createdAt": 1562775129000, + "text": "

ES6 Modules

\n

Yes, use type="module" in a script tag (support):

\n
<script type="module" src="script.js"></script>\n
\n

And in a script.js file include another file like this:

\n
import { hello } from './module.js';\n...\n// alert(hello());\n
\n

In 'module.js' you must export the function/class that you will import:

\n
export function hello() {\n    return "Hello World";\n}\n
\n

A working example is here.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90101", + "creator": "Torxed", + "createdAt": 1570649396000, + "text": "

So this is a edge case. But if you need to load the JavaScript from a remote source, most modern browsers might block your cross-site requests due to CORS or something similar. So normal

\n\n
<script src=\"https://another-domain.com/example.js\"></script>\n
\n\n

Won't work. And doing the document.createElement('script').src = '...' won't cut it either. Instead, what you could do is load the java-script as a resource via standard GET request, and do this:

\n\n
<script type=\"text/javascript\">\n    var script = document.createElement('script');\n    script.type = 'text/javascript';\n\n    let xhr = new XMLHttpRequest();\n    xhr.open(\"GET\", 'https://raw.githubusercontent.com/Torxed/slimWebSocket/master/slimWebSocket.js', true);\n    xhr.onreadystatechange = function() {\n        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {\n            script.innerHTML = this.responseText; // <-- This one\n            document.head.appendChild(script);\n        }\n    }\n    xhr.send();\n</script>\n
\n\n

By grabbing the content yourself, the browser won't notice malicious intents and allow you go do the request. Then you add it in <script>'s innerHTML instead. This still causes the browser (at least tested in Chrome) to parse/execute the script.

\n\n

Again, this is a edge case use case. And you'll have no backwards compatibility or browser compliance probably. But fun/useful thing to know about.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90102", + "creator": "Hmerman6006", + "createdAt": 1587147610000, + "text": "

I did not see an answer whereby you create an object of all functions and variables in a file and then make that object an argument to refer to it in another file.

\n

E.g., you have files called 'jsMod.js', 'jsView' and 'jsContr.js':

\n
\n    //jsMod.js file\n    JSMODOBJ = {};\n    JSMODOBJ.valueAddition = function(/* element value 1 */ val1,\n                                          /* element value 2 */ val2) {\n        return val1 + val2;\n    }\n\n
\n
\n    //jsView.js file\n    JSVIEWOBJ = {};\n    JSVIEWOBJ.elementColour = function(/* element id to change colour */ id,\n                                          /* css colour classname */ col) {\n        document.getElementById(id).className = col;\n    }\n\n
\n
\n    //jsContr.js file\n    JSCONTROBJ = {};\n    var jsMod = JSMODOBJ;\n    var jsView = JSVIEWOBJ;\n\n    JSCONTROBJ.changeColourByValue = function (val1, val2, id, clss) {\n        if (jsMod.valueAddition(val1,val2) !== 0) {\n            jsView.elementColour(id, clss);\n        }\n    }\n\n
\n

Then you can set the .js files dynamically by echoeing the scripts into your .html or .php file:

\n
<?php\n    echo "<script src = './js/dleafView.js'></script>\n        <script src = './js/dleafModule.js'></script>\n        <script src = './js/dleafContr.js'></script>";\n?>\n
\n

Then just call the control function within a <script type="text/javascript"></script> tag. Of course this will take a lot of time in the beginning to set up, but it saves you time in the long run.

\n

I use this in a slightly different way, but this way also work.

\n", + "upvotes": 1348, + "upvoterUsernames": [], + "downvotes": 1348, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90103", + "creator": "Dangerousgame", + "createdAt": 1589878901000, + "text": "

You shall use this:

\n\n
<script src=\"your_file.js\"></script>\n
\n\n

Easy!

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90104", + "creator": "9pfs supports Ukraine", + "createdAt": 1623367769000, + "text": "

Make a fetch request and eval the result.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90105", + "creator": "smallscript", + "createdAt": 1625447404000, + "text": "

My general solution taken from the efekt.js.st library from EdgeS (which I authored).

\n
\n

shameless plug alert - I am on other stackexchange network sites. This is a relink of https://codereview.stackexchange.com/questions/263764/dynamic-load-css-or-script.

\n
\n

What code or design would you use to support dynamic-loading of css and scripts?

\n

Requirements

\n\n
static loadScriptOrStyle(url, options) {\n  // provenance :<# **Smallscript EdgeS efekt** `efekt.js.st` github libraries #>\n  // returns    :<Promise#onload;onerror>\n  // options    :<# `fIgnoreCache`, `fAppendToHead`, `fUrlIsStyle`, `attrs:{}` #>\n  const head = document.head; let node = options?.fAppendToBody ? document.body : head;\n  const url_loader_cache = document.head.url_loader_cache\n    ? head.url_loader_cache\n    : (head.url_loader_cache = {script:{},link:{}})\n  const kind = (options?.fUrlIsStyle || /\\.css(?:(?:\\?|#).*)?$/i.test(url))\n    ? 'link' : 'script';\n  // check already-loaded cache\n  if(url_loader_cache[kind][url]) {\n    const el = url_loader_cache[kind][url];\n    // support `fIgnoreCache` reload-option; should not use on `head`\n    if(options?.fIgnoreCache)\n      el.remove();\n    else\n      return(new CustomEvent('cache',{detail:el}));\n  }\n  // (re)create and record it\n  const self = document.currentScript;\n  const el = url_loader_cache[kind][url] = document.createElement(kind);\n  const append = (!self || options?.fAppendToHead || options?.fAppendToBody)\n    ? el => node.appendChild(el)\n    : el => self.parentNode.insertBefore(el, self);\n  const load = new Promise((resolve, reject) => {\n    el.onload  = e => {e.detail = el;resolve(e)};\n    el.onerror = e => {e.detail = el;reject(e)};\n    // `onload` or `onerror` possibly alter `cache` value\n    // throw(new URIError(`The ${url} didn't load correctly.`))\n  });\n  // configure `module` attr, as appropriate\n  if(/\\.mjs(?:(?:\\?|#).*)?$/i.test(url))\n    el.type = 'module'\n  // configure other attrs as appropriate (referrer, nonce, etc)\n  for(const key in options?.attrs) {el[key] = attrs[key]}\n  // trigger it\n  if(kind === 'link') el.rel = 'stylesheet', el.href = url; else el.src = url;\n  append(el);\n  return(load);\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90106", + "creator": "Nirvana", + "createdAt": 1634625371000, + "text": "

On the back-end, you can use CommonJS modules. For example:

\n
//a.js\nfunction func () {\n   var result = "OK Bro";\n   return result;\n}\n\nmodule.exports = { func };\n
\n
//b.js\nvar a = require('./a.js');\nconsole.log(a.func);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90107", + "creator": "Enrico König", + "createdAt": 1641464434000, + "text": "

So if you want it quick, and easy...\nTry this:

\n
function include(filename)\n{\n    var head = document.getElementsByTagName('head')[0];\n\n    var script = document.createElement('script');\n    script.src = filename;\n    script.type = 'text/javascript';\n\n    head.appendChild(script)\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90109", + "creator": "Mount Mario", + "createdAt": 1645901164000, + "text": "

You can just use the require(); tag.

\n

For example, if I had a addition.js module that I wanted to add to math.js, I would do this:

\n
//this is math.js\n\n//vars\nlet a = 1;\nlet b = 3;\n\n//start of code\nconst additionfile = require('addition.js');\nwindow.alert("You added " + a + " and " + b + " together, to get " + additionfile.add(a,b) + "!");\n
\n

if you wanted the addition.js file, it would look something like this

\n
function add(a,b) {\n   const sum = a + b;\n   return sum;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322eb082fcc3049e912b7", + "creator": "the_nuts", + "createdAt": 1649826765000, + "text": "This function does not even exist, tested in latest Chrome: Uncaught ReferenceError: require is not defined", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90108", + "creator": "Enrico König", + "createdAt": 1641472646000, + "text": "

Its that simple:

\n
var js = document.createElement("script");\n\njs.type = "text/javascript";\njs.src = jsFilePath;\n\ndocument.body.appendChild(js);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9010a", + "creator": "aderchox", + "createdAt": 1653205368000, + "text": "

But how about the easiest solution? I'm surprised no one has mentioned this so I'm posting it! Use a bundler like Vite.js and then simply do:

\n
import "./path/to/js/file";\n
\n

That's it! The OP has asked for something like "@import in CSS" and this is exactly like that. It is also NOT as rocket science complex as some of the old methods. It is at least undoubtedly the most beginner-friendly method, but I'm sure non-beginners do like it as well.

\n

To get started with Vite for a Vanilla JavaScript project, just have Node and NPM installed, then do:

\n
npm create vite@latest <your-vanilla-js-app-name> --template vanilla\n
\n

E.g.:

\n
npm create vite@latest my-js-app --template vanilla\n
\n

Now add imports like mentioned at the beginning of this answer and call it a day.

\n

Just as a side note: Also another thing that might pop into your mind is namespacing issues, e.g., what if a name you have used in a file you're including is similar to a name you already have in your current file? But that's the nature of JavaScript, right? It's not an issue specific to this method. So you'll need to devise strategies for handling that separately. There's a comprehensive article on Addy Osmani's blog in case you want to learn more about that: Design Patterns for Handling the Global Namespace in JavaScript.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ee", + "creator": "Evgeniy Miroshnichenko", + "createdAt": 1471000709000, + "text": "

You can't import, but you can reference.

\n\n

PhpShtorm IDE. To reference, in one .js file to another .js, just add this to the top of the file:

\n\n
<reference path=\"../js/file.js\" />\n
\n\n

Of course, you should use your own PATH to the JavaScript file.

\n\n

I don't know if it will work in other IDEs. Probably yes, just try. It should work in Visual Studio too.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900ef", + "creator": "xgqfrms", + "createdAt": 1475588854000, + "text": "

Here is maybe another way!

\n

In Node.js you can do that just like the following code shows!

\n

sub.js

\n
    module.exports = {\n      log: function(string) {\n        if(console) console.log(string);\n      }\n      mylog: function(){\n        console.log('just for log test!');\n      }\n    }\n
\n

main.js

\n
    const mylog = require('./sub');\n\n    mylog.log('Hurray, it works! :)');\n    mylog.mylog();\n\n
\n

refs

\n

http://requirejs.org/docs/node.html

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f1", + "creator": "gabriel211", + "createdAt": 1479335789000, + "text": "

Another approach is to use HTML imports. These can contain script references as well as stylesheet references.

\n\n

You can just link an HTML file like

\n\n
<link rel=\"import\" href=\"vendorScripts.html\"/>\n
\n\n

Within the vendorScripts.html file you can include your script references like:

\n\n
<script src=\"scripts/vendors/jquery.js\"></script>\n<script src=\"scripts/vendors/bootstrap.js\"></script>\n<script src=\"scripts/vendors/angular.js\"></script>\n<script src=\"scripts/vendors/angular-route.js\"></script>\n
\n\n

Look at HTML Imports for more details.

\n\n

Unfortunately this only works in Chrome.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f0", + "creator": "Mesut Yiğit", + "createdAt": 1476865134000, + "text": "
var xxx = require(\"../lib/your-library.js\")\n
\n\n

or

\n\n
import xxx from \"../lib/your-library.js\" //get default export\nimport {specificPart} from '../lib/your-library.js' //get named export\nimport * as _name from '../lib/your-library.js'  //get full export to alias _name\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f2", + "creator": "gm2008", + "createdAt": 1492080164000, + "text": "

If you use Angular, then a plugin module $ocLazyLoad can help you to do that.

\n\n

Here are some quotes from its documentation:

\n\n
\n

Load one or more modules & components with multiple files:

\n\n
$ocLazyLoad.load(['testModule.js', 'testModuleCtrl.js', 'testModuleService.js']);\n
\n \n

Load one or more modules with multiple files and specify a type where necessary:\n Note: When using the requireJS style formatting (with js! at the beginning for example), do not specify a file extension. Use one or the other.

\n\n
$ocLazyLoad.load([\n  'testModule.js',\n   {type: 'css', path: 'testModuleCtrl'},\n   {type: 'html', path: 'testModuleCtrl.html'},\n   {type: 'js', path: 'testModuleCtrl'},\n   'js!testModuleService',\n   'less!testModuleLessFile'\n]);\n
\n \n

You can load external libs (not angular):

\n\n
$ocLazyLoad.load(['testModule.js', \n   'bower_components/bootstrap/dist/js/bootstrap.js', 'anotherModule.js']);\n
\n \n

You can also load css and template files:

\n\n
 $ocLazyLoad.load([\n     'bower_components/bootstrap/dist/js/bootstrap.js',\n     'bower_components/bootstrap/dist/css/bootstrap.css',\n     'partials/template1.html'\n ]);\n
\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e900f3", + "creator": "Yairopro", + "createdAt": 1493910996000, + "text": "

Here's a workaround for browsers (not Node.js) using HTML imports.

\n\n

First, all JavaScript classes and scripts are not in .js files, but in .js.html files (the .js.html is just to recognize between HTML pages and complete JavaScript script/classes), inside <script> tags, like this:

\n\n

MyClass.js.html:

\n\n
<script>\n   class MyClass {\n\n      // Your code here..\n\n   }\n\n</script>\n
\n\n

Then if you wish to import your class, you just need to use HTML imports:

\n\n
<link rel=\"import\" href=\"relative/path/to/MyClass.js.html\"/>\n\n<script>\n   var myClass = new MyClass();\n   // Your code here..\n</script>\n
\n\n

EDIT : HTML imports will be dropped

\n\n

HTML imports are dropped, in favor of ES6 modules. \nYou should use ES6 modules.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3995660, + "uvac": 3995722 + } + }, + { + "_id": "62f321bb082fcc3049e8fed1", + "title": "What is the JavaScript version of sleep()?", + "title-lowercase": "what is the javascript version of sleep()?", + "creator": "fmsf", + "createdAt": 1244126470000, + "status": "open", + "text": "

Is there a better way to engineer a sleep in JavaScript than the following pausecomp function (taken from here)?

\n\n
function pausecomp(millis)\n{\n    var date = new Date();\n    var curDate = null;\n    do { curDate = new Date(); }\n    while(curDate-date < millis);\n}\n
\n\n

This is not a duplicate of Sleep in JavaScript - delay between actions; I want a real sleep in the middle of a function, and not a delay before a piece of code executes.

\n", + "upvotes": 4133, + "upvoterUsernames": [], + "downvotes": 849, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 3959633, + "answers": 83, + "answerItems": [ + { + "_id": "62f321c3082fcc3049e90732", + "creator": "Andrew Dunkman", + "createdAt": 1244126811000, + "text": "

You can't do a sleep like that in JavaScript, or, rather, you shouldn't. Running a sleep or a while loop will cause the user's browser to hang until the loop is done.

\n\n

Use a timer, as specified in the link you referenced.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90731", + "creator": "chaos", + "createdAt": 1244126643000, + "text": "

For the love of $DEITY please do not make a busy-wait sleep function. setTimeout and setInterval do everything you need.

\n\n

\r\n
\r\n
var showHide = document.getElementById('showHide');\r\nsetInterval(() => {\r\n    showHide.style.visibility = \"initial\";\r\n    setTimeout(() => {\r\n        showHide.style.visibility = \"hidden\"\r\n    }, 1000);\r\n    ;\r\n}, 2000);   
\r\n
<div id=\"showHide\">Hello! Goodbye!</div>
\r\n
\r\n
\r\n

\n\n

Every two second interval hide text for one second. This shows how to use setInterval and setTimeout to show and hide text each second.

\n", + "upvotes": 244, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259a082fcc3049e91dcc", + "creator": "annakata", + "createdAt": 1244126928000, + "text": "Well not quite everything: setInterval does a much better impression of polling.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dcd", + "creator": "Deniz Dogan", + "createdAt": 1244127080000, + "text": "What would that piece of code not hold back in the JavaScript engine?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dcf", + "creator": "Aaron Dufour", + "createdAt": 1316624042000, + "text": "Unless you need the sleep to be synchronous, in which case this is a completely valid question.", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dd1", + "creator": "chaos", + "createdAt": 1393865248000, + "text": "@PhilLaNasa: If syntactic closure is still scaring one, one seriously needs to buckle down and work through Node 101.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dd3", + "creator": "chaos", + "createdAt": 1395867811000, + "text": "@PhilLaNasa: Any context in which closures are not JS 101 needs a full curriculum redesign, stat.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90734", + "creator": "Pablo Fernandez", + "createdAt": 1244126872000, + "text": "

First:

\n

Define a function you want to execute like this:

\n
function alertWorld(){\n  alert("Hello, World!");\n}\n
\n

Then schedule its execution with the setTimeout method:

\n
setTimeout(alertWorld, 1000)\n
\n

Note two things

\n\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259a082fcc3049e91dd4", + "creator": "Aayush Sinha", + "createdAt": 1616966447000, + "text": "The question was to ask a way to sleep in a blocking wy. setTimeout does not do that. It queues in the Macro task Queue.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90733", + "creator": "Nosredna", + "createdAt": 1244126814000, + "text": "

In JavaScript, I rewrite every function so that it can end as soon as possible. You want the browser back in control so it can make your DOM changes.

\n\n

Every time I've wanted a sleep in the middle of my function, I refactored to use a setTimeout().

\n\n

Edit

\n\n

The infamous sleep, or delay, function within any language is much debated. Some will say that there should always be a signal or callback to fire a given functionality, others will argue that sometimes an arbitrary moment of delay is useful. I say that to each their own and one rule can never dictate anything in this industry.

\n\n

Writing a sleep function is simple and made even more usable with JavaScript Promises:

\n\n
// sleep time expects milliseconds\nfunction sleep (time) {\n  return new Promise((resolve) => setTimeout(resolve, time));\n}\n\n// Usage!\nsleep(500).then(() => {\n    // Do something after the sleep!\n});\n
\n", + "upvotes": 1502, + "upvoterUsernames": [], + "downvotes": 746, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259a082fcc3049e91dd7", + "creator": "chaos", + "createdAt": 1270697238000, + "text": "By way of closure. function foobar(el) { setTimeout(function() { foobar_cont(el); }, 5000); }", + "upvotes": 331, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dd8", + "creator": "Eugenio Miró", + "createdAt": 1279812226000, + "text": "ok, and what if the code is not intended to be used in a webpage?", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dda", + "creator": "sorki", + "createdAt": 1325112333000, + "text": "@Tim loop-safe version: for(i=0; i<5; i++) { (function(i) { setTimeout(function() { console.log(i); }, 1000*i); })(i); }", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91ddc", + "creator": "Anonymous Pi", + "createdAt": 1389280714000, + "text": "@Tezcat I'm making a .html (no hosting) but sadly, all the familiars I send them to use Chrome ;(", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91dde", + "creator": "rlemon", + "createdAt": 1405517117000, + "text": "am I the only one to notice the sleep function is identical to just using setTimeout (minus the reversed arguments) ??", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91de0", + "creator": "ryanwebjackson", + "createdAt": 1632695651000, + "text": "@MemetOlsen You have to put the rest of your code inside the callback, or use async/await if available.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91de2", + "creator": "ryanwebjackson", + "createdAt": 1632851458000, + "text": "Async/await is sugar on top of the same -- it does the same thing as what I am suggesting, but with an easier syntax.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90735", + "creator": "DevinB", + "createdAt": 1244127329000, + "text": "

I agree with the other posters. A busy sleep is just a bad idea.

\n

However, setTimeout does not hold up execution. It executes the next line of the function immediately after the timeout is SET, not after the timeout expires, so that does not accomplish the same task that a sleep would accomplish.

\n

The way to do it is to breakdown your function into before and after parts.

\n
function doStuff()\n{\n  // Do some things\n  setTimeout(continueExecution, 10000) // Wait ten seconds before continuing\n}\n\nfunction continueExecution()\n{\n   // Finish doing things after the pause\n}\n
\n

Make sure your function names still accurately describe what each piece is doing (i.e., GatherInputThenWait and CheckInput, rather than funcPart1 and funcPart2)

\n

This method achieves the purpose of not executing the lines of code you decide until after your timeout, while still returning control back to the client PC to execute whatever else it has queued up.

\n

As pointed out in the comments this will absolutely not work in a loop. You could do some fancy (ugly) hacking to make it work in a loop, but in general that will just make for disastrous spaghetti code.

\n", + "upvotes": 261, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259a082fcc3049e91de3", + "creator": "Nosredna", + "createdAt": 1244128214000, + "text": "Yeah. Where this gets tricky is when you have a loop, or a nested loop even. You have to abandon your for loops and have counters instead.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91de4", + "creator": "Prof", + "createdAt": 1644028018000, + "text": "One-liner: setTimeout(() => someSmallBitOfCodeHere(), 10000);", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90736", + "creator": "Alan Plum", + "createdAt": 1244659832000, + "text": "

If you're using jQuery, someone actually created a \"delay\" plugin that's nothing more than a wrapper for setTimeout:

\n\n
// Delay Plugin for jQuery\n// - http://www.evanbot.com\n// - © 2008 Evan Byrne\n\njQuery.fn.delay = function(time,func){\n    this.each(function(){\n        setTimeout(func,time);\n    });\n\n    return this;\n};\n
\n\n

You can then just use it in a row of function calls as expected:

\n\n
$('#warning')\n.addClass('highlight')\n.delay(1000)\n.removeClass('highlight');\n
\n", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259a082fcc3049e91de7", + "creator": "Nosredna", + "createdAt": 1244660435000, + "text": "That's not a bad solution. Keeps context and chainability.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3259a082fcc3049e91de8", + "creator": "WGroleau", + "createdAt": 1527803013000, + "text": "If you need a delay between two independent calls, yes. If you need delays to slow down a loop, no.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90737", + "creator": "beauburrier", + "createdAt": 1274989500000, + "text": "

For the specific case of wanting to space out a set of calls being executed by a loop, you can use something like the code below with prototype. Without prototype, you can substitute the delay function with setTimeout.

\n\n
function itemHandler(item)\n{\n    alert(item);\n}\n\nvar itemSet = ['a','b','c'];\n\n// Each call to itemHandler will execute\n// 1 second apart\nfor(var i=0; i<itemSet.length; i++)\n{\n    var secondsUntilExecution = i;\n    itemHandler.delay(secondsUntilExecution, item)\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90738", + "creator": "acuth", + "createdAt": 1287431143000, + "text": "

One scenario where you might want a sleep() function rather than using setTimeout() is if you have a function responding to a user click that will ultimately end up opening a new i.e. popup window and you have initiated some processing that requires a short period to complete before the popup is displayed. Moving the open window into a closure means that it typically gets blocked by the browser.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9073a", + "creator": "a_w", + "createdAt": 1308084628000, + "text": "

I've searched for a sleep solution too (not for production code, only for development and tests) and found this article:

\n

JavaScript sleep() or wait()

\n

...and here's another article with client-side solutions: JavaScript sleep

\n

Also, when you are calling alert(), your code will be paused too, while the alert is shown -- you need to find a way to not display alert, but get the same effect. :)

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90739", + "creator": "Ben Flynn", + "createdAt": 1305292897000, + "text": "

(See the updated answer for 2016)

\n

I think it's perfectly reasonable to want to perform an action, wait, and then perform another action. If you are used to writing in multi-threaded languages, you probably have the idea of yielding execution for a set amount of time until your thread wakes up.

\n

The issue here is that JavaScript is a single-thread event-based model. While in a specific case, it might be nice to have the whole engine wait for a few seconds, in general it is bad practice. Suppose I wanted to make use of your functions while writing my own? When I called your method, my methods would all freeze up. If JavaScript could somehow preserve your function's execution context, store it somewhere, then bring it back and continue later, then sleep could happen, but that would basically be threading.

\n

So you are pretty much stuck with what others have suggested -- you'll need to break your code up into multiple functions.

\n

Your question is a bit of a false choice, then. There is no way to sleep in the way you want, nor should you pursue the solution you suggest.

\n", + "upvotes": 991, + "upvoterUsernames": [], + "downvotes": 130, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9073b", + "creator": "naazgull", + "createdAt": 1308678086000, + "text": "

I can understand the purpose of a sleep function if you have to deal with synchronous execution. The setInterval and setTimeout functions create a parallel execution thread which returns the execution sequence back to the main program, which is ineffective if you have to wait for a given result. Of course one may use events and handlers, but in some cases is not what is intended.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9073c", + "creator": "Mainguy", + "createdAt": 1312808730000, + "text": "

A better solution to make things look like what most people want is to use an anonymous function:

\n
alert('start');\nvar a = 'foo';\n// Lots of code\nsetTimeout(function(){  // Beginning of code that should run AFTER the timeout\n    alert(a);\n    // Lots more code\n}, 5000);  // Put the timeout here\n
\n

This is probably the closest you'll get to something that simply does what you want.

\n

Note, if you need multiple sleeps this can get ugly in a hurry and you might actually need to rethink your design.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259b082fcc3049e91df0", + "creator": "Mike", + "createdAt": 1579581146000, + "text": "this is the one that worked for me on desktop browsers and an older mobile phone. The others I tried didn't work on all.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9073d", + "creator": "naugtur", + "createdAt": 1312880028000, + "text": "

First of all - setTimeout and setInterval is what should be used, because of JavaScript's callback-ish nature. If you want to use sleep() it's the control flow or the architecture of your code that is incorrect.

\n

Having said that I suppose I still can help with two implementation of a sleep.

\n

1. Faking synchronous run off the top of my head:

\n
// A module to do that //dual-license: MIT or WTF [you can use it anyhow and leave my nickname in a comment if you want to]\nvar _ = (function(){\n  var queue = [];\n  var play = function(){\n    var go = queue.shift();\n      if(go) {\n        if(go.a) {\n          go.f();\n          play();\n        }\n        else\n        {\n          setTimeout(play, go.t);\n        }\n      }\n  }\n  return {\n    go:function(f){\n      queue.push({a:1, f:f});\n    },\n    sleep:function(t){\n      queue.push({a:0, t:t});\n    },\n    playback:play\n  }\n})();\n
\n

[making playback automatic should also be possible]

\n
// Usage\n\n_.go(function(){\n\n  // Your code\n  console.log('first');\n\n});\n\n_.sleep(5000);\n\n_.go(function(){\n\n  // Your code\n  console.log('next');\n\n});\n\n// This triggers the simulation\n_.playback();\n
\n

2. Real synchronous run

\n

I gave it a lot of thought one day and the only idea I had for a true sleep in JavaScript is technical.

\n

A sleep function would have to be a synchronous Ajax call with a timeout set to the sleep value. That's all and the only way to have a real sleep().

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9073e", + "creator": "Aaron", + "createdAt": 1317066693000, + "text": "

If you want to sleep an anonymous function like one you've created as a handler, I recommend the following:

\n
function()\n{\n    if (!wait_condition)\n    {\n        setTimeout(arguments.callee, 100, /* Comma-separated arguments here */);\n    }\n    // The rest of the function\n}\n
\n

This code says "If the wait condition has not yet been satisfied, call this function again with these arguments." I've used this method to pass in the same arguments to my handlers, effectively making this code a non-polling sleep() (which only works at the start of your function).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90740", + "creator": "CoR", + "createdAt": 1322908821000, + "text": "

Code taken from this link will not freeze the computer. But it works only in Firefox.

\n
/**\n * Netscape compatible WaitForDelay function.\n * You can use it as an alternative to Thread.Sleep() in any major programming language\n * that support it while JavaScript it self doesn't have any built-in function to do such a thing.\n * parameters:\n * (Number) delay in millisecond\n */\nfunction nsWaitForDelay(delay) {\n    /**\n     * Just uncomment this code if you're building an extension for Firefox.\n     * Since Firefox 3, we'll have to ask for user permission to execute XPCOM objects.\n     */\n    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\n\n    // Get the current thread.\n    var thread = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager).currentThread;\n\n    // Create an inner property to be used later as a notifier.\n    this.delayed = true;\n\n    /* Call JavaScript setTimeout function\n      * to execute this.delayed = false\n      * after it finishes.\n      */\n    setTimeout("this.delayed = false;", delay);\n\n    /**\n     * Keep looping until this.delayed = false\n     */\n    while (this.delayed) {\n        /**\n         * This code will not freeze your browser as it's documented in here:\n         * https://developer.mozilla.org/en/Code_snippets/Threads#Waiting_for_a_background_task_to_complete\n         */\n        thread.processNextEvent(true);\n    }\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9073f", + "creator": "mjaggard", + "createdAt": 1320416166000, + "text": "

If (like me) you're using JavaScript with Rhino, you can use...

\n
try\n{\n  java.lang.Thread.sleep(timeInMilliseconds);\n}\ncatch (e)\n{\n  /*\n   * This will happen if the sleep is woken up - you might want to check\n   * if enough time has passed and sleep again if not - depending on how\n   * important the sleep time is to you.\n   */\n}\n
\n", + "upvotes": 184, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259b082fcc3049e91df6", + "creator": "mjaggard", + "createdAt": 1517517760000, + "text": "@RousseauAlexandre Incorrect. It is JavaScript using Rhino (at the time, it could equally be Nashorn these days)", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90741", + "creator": "Michael Erickson", + "createdAt": 1329173442000, + "text": "

A method of an object that needs to use a "sleep" method such as the following:

\n
function SomeObject() {\n    this.SomeProperty = "xxx";\n    return this;\n}\nSomeObject.prototype.SomeMethod = function () {\n    this.DoSomething1(arg1);\n    sleep(500);\n    this.DoSomething2(arg1);\n}\n
\n

Can almost be translated to:

\n
function SomeObject() {\n    this.SomeProperty = "xxx";\n    return this;\n}\nSomeObject.prototype.SomeMethod = function (arg1) {\n    var self = this;\n    self.DoSomething1(arg1);\n    setTimeout(function () {\n        self.DoSomething2(arg1);\n    }, 500);\n}\n
\n

The difference is that the operation of "SomeMethod" returns before the operation "DoSomething2" is executed. The caller of "SomeMethod" cannot depend on this. Since the "Sleep" method does not exists, I use the latter method and design my code accordingly.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90742", + "creator": "Matthew Strawbridge", + "createdAt": 1351771008000, + "text": "

A good alternative in some situations is to display a top-level message panel to stop user interaction, and then hide it again when you get the result you're waiting for (asynchronously). That allows the browser to get on with background tasks, but pauses the workflow until you've got your result back.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90743", + "creator": "Ian Maddox", + "createdAt": 1351908167000, + "text": "

Here you go. As the code says, don't be a bad developer and use this on websites. It's a development utility function.

\n
// Basic sleep function based on ms.\n// DO NOT USE ON PUBLIC FACING WEBSITES.\nfunction sleep(ms) {\n    var unixtime_ms = new Date().getTime();\n    while(new Date().getTime() < unixtime_ms + ms) {}\n}\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259b082fcc3049e91dfa", + "creator": "Andrew Barber", + "createdAt": 1351908983000, + "text": "That's basically the same thing as the OP had.", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3259b082fcc3049e91dfc", + "creator": "WGroleau", + "createdAt": 1527281306000, + "text": "To be more precise, it's what OP asked for an ALTERNATIVE to.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90744", + "creator": "caiocpricci2", + "createdAt": 1356004650000, + "text": "

I needed a busy-wait for testing purposes. I didn't want to split the code as that would be a lot of work, so a simple for did it for me.

\n
for (var i=0; i<1000000; i++){                  \n    // Waiting\n}\n
\n

I didn't see any downside in doing this and it did the trick for me.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259b082fcc3049e91dfe", + "creator": "Viktor Sehr", + "createdAt": 1374265376000, + "text": "That might be compiled away.", + "upvotes": 2626, + "upvoterUsernames": [], + "downvotes": 2626, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90746", + "creator": "FaTaL_ErRoR", + "createdAt": 1360957717000, + "text": "

Or just create this:

\n
function yourFunction(){\n\n   // Do something\n   setInterval(myFunc(), 1000);\n   // Do something else\n}\n\nfunction myFunc(){\n   return;\n}\n
\n

This will just wait the interval specified and call the function which will just do nothing.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259c082fcc3049e91e01", + "creator": "Seeker", + "createdAt": 1361280070000, + "text": "setInterval will call the function frequently after the timer is completed", + "upvotes": 1280, + "upvoterUsernames": [], + "downvotes": 1280, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e03", + "creator": "Mouad Debbar", + "createdAt": 1365970090000, + "text": "and I think you wanted to say: setInterval(myFunc, 1000).", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90745", + "creator": "Rachael", + "createdAt": 1358127127000, + "text": "

It can be done using Java's sleep method. I've tested it in Firefox and Internet Explorer and it doesn't lock the computer, chew up resources, or cause endless server hits. It seems like a clean solution to me.

\n

First you have to get Java loaded up on the page and make its methods available. To do that, I did this:

\n
<html>\n<head>\n\n<script type="text/javascript">\n\n  function load() {\n    var appletRef = document.getElementById("app");\n    window.java = appletRef.Packages.java;\n  } // endfunction\n\n</script>\n\n<body onLoad="load()">\n\n<embed id="app" code="java.applet.Applet" type="application/x-java-applet" MAYSCRIPT="true" width="0" height="0" />\n
\n

Then, all you have to do when you want a painless pause in your JavaScript code is:

\n
java.lang.Thread.sleep(xxx)\n
\n

Where xxx is time in milliseconds. In my case (by way of justification), this was part of back-end order fulfilment at a very small company and I needed to print an invoice that had to be loaded from the server. I did it by loading the invoice (as a webpage) into an iFrame and then printing the iFrame.

\n

Of course, I had to wait until the page was fully loaded before I could print, so the JavaScript code had to pause. I accomplished this by having the invoice page (in the iFrame) change a hidden form field on the parent page with the onLoad event. And the code on the parent page to print the invoice looked like this (irrelevant parts cut for clarity):

\n
var isReady = eval('document.batchForm.ready');\nisReady.value = 0;\n\nframes['rpc_frame'].location.href = url;\n\nwhile (isReady.value == 0) {\n  java.lang.Thread.sleep(250);\n} // endwhile\n\nwindow.frames['rpc_frame'].focus();\nwindow.frames['rpc_frame'].print();\n
\n

So the user pushes the button, the script loads the invoice page, waits, checking every quarter second to see if the invoice page is finished loading, and pops up the print dialog for the user to send it to the printer. QED.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259c082fcc3049e91e06", + "creator": "xaralis", + "createdAt": 1358871449000, + "text": "Seems quite monstrous soulution when considering the simple thing author wanted to achieve.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e08", + "creator": "Vahid Amiri", + "createdAt": 1463059328000, + "text": "This depends on Java Applets which is deprecated.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90748", + "creator": "BishopZ", + "createdAt": 1375132811000, + "text": "

If you write a sleep function like this

\n
var sleep = function(period, decision, callback){\n    var interval = setInterval(function(){\n        if (decision()) {\n            interval = clearInterval(interval);\n            callback();\n        }\n    }, period);\n}\n
\n

and you have an asynchronous function to call multiple times,

\n
var xhr = function(url, callback){\n    // Make an Ajax request\n    // Call a callback when the request fulfils\n}\n
\n

And you set up your project like this:

\n
var ready = false;\n\nfunction xhr1(){\n    xhr(url1, function(){ ready = true;});\n}\nfunction xhr2(){\n    xhr(url2, function(){ ready = true; });\n}\nfunction xhr3(){\n    xhr(url3, function(){ ready = true; });\n}\n
\n

Then you can do this:

\n
xhr1();\nsleep(100, function(){ return done; }, xhr2);\nsleep(100, function(){ return done; }, xhr3);\nsleep(100, function(){ return done; }, function(){\n    // Do more\n});\n
\n

Instead of endless callback indentation like this:

\n
xhr(url1, function(){\n    xhr2(url2, function(){\n        xhr3(url3, function(){\n            // Do more\n        });\n    });\n});\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90747", + "creator": "Homer6", + "createdAt": 1368831198000, + "text": "

For browsers, I agree that setTimeout and setInterval are the way to go.

\n

But for server-side code, it may require a blocking function (for example, so you can effectively have thread synchronization).

\n

If you're using Node.js and Meteor, you may have run into the limitations of using setTimeout in a fiber. Here is the code for server-side sleep.

\n
var Fiber = require('fibers');\n\nfunction sleep(ms) {\n    var fiber = Fiber.current;\n    setTimeout(function() {\n        fiber.run();\n    }, ms);\n    Fiber.yield();\n}\n\nFiber(function() {\n    console.log('wait... ' + new Date);\n    sleep(1000);\n    console.log('ok... ' + new Date);\n}).run();\nconsole.log('back in main');\n
\n

See: Node.js Fibers, Sleep

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9074a", + "creator": "user2882556", + "createdAt": 1381840839000, + "text": "

I use the multithread HTML5 Worker which will be able to abort an synchronous XMLHttpRequest pointing to an unresponsive URL. This does not block the browser.

\n\n

https://gist.github.com/el-gringo/6990785

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90749", + "creator": "StephaneAG", + "createdAt": 1375144309000, + "text": "

In Firebug (and probably other JavaScript consoles), nothing happen after hitting enter, only after the sleep duration specified (...)

\n
function sleepFor(sleepDuration){\n    var now = new Date().getTime();\n    while(new Date().getTime() < now + sleepDuration){ /* Do nothing */ }\n}\n
\n

Example of use:

\n

\r\n
\r\n
function sleepFor(sleepDuration){\n    var now = new Date().getTime();\n    while(new Date().getTime() < now + sleepDuration){ \n        /* Do nothing */ \n    }\n}\n\nfunction sleepThenAct(){\n    sleepFor(2000);\n    console.log(\"Hello, JavaScript sleep!\");\n}\n\nsleepThenAct()
\r\n
\r\n
\r\n

\n

Note: Only for debugging and development

\n", + "upvotes": 495, + "upvoterUsernames": [], + "downvotes": 152, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259c082fcc3049e91e0d", + "creator": "jab", + "createdAt": 1433218989000, + "text": "This is not an answer. It's exactly the same as the code in the question, except slightly shorter.", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e0f", + "creator": "mafu", + "createdAt": 1471609673000, + "text": "Busy waiting, really? In JS? For seconds? If I catch a website doing this, it will be blocked.", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e11", + "creator": "xDaizu", + "createdAt": 1472466682000, + "text": "@mafu That's why it says only for debug/dev... rolleyes", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e13", + "creator": "xDaizu", + "createdAt": 1472467316000, + "text": "@jab That's one of the reasons why I upvoted it. I find hilarious that he optimized his busy waiting loop", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 79, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e14", + "creator": "xDaizu", + "createdAt": 1472545510000, + "text": "@mafu that's true, it's so tricky to come up with a proper example that it probably deserves its own question!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e15", + "creator": "eaorak", + "createdAt": 1513594273000, + "text": "NEVER DO THIS. This will make the CPU to hit 100% on the core that it executes and will block it.", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e16", + "creator": "Wheezil", + "createdAt": 1515869567000, + "text": "This is useful, and perhaps the ONLY way to sleep, within a command-line javascript application, because async/await is not helpful.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e18", + "creator": "Nabi K.A.Z.", + "createdAt": 1528517013000, + "text": "The solution is not good, but in some places async/await it's not responsive unfortunately, and we have to use it mandatorily.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e19", + "creator": "Robert McLean", + "createdAt": 1659531126000, + "text": "I'm using this in a QML (5.15) app. None of the other solutions worked for me, but this one did.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9074b", + "creator": "Gerard ONeill", + "createdAt": 1383246529000, + "text": "

The short answer is no, not in JavaScript by itself. Your solution seems to be the only way to not return control back to the environment.

\n

This is necessary if the environment does not support events. They probably wouldn't support the setTimeout either.

\n

setTimeout is definitely the best way if you are in an event driven environment such as a browser or Node.js.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9074d", + "creator": "tponthieux", + "createdAt": 1398706258000, + "text": "

You can use a closure call setTimeout() with incrementally larger values.

\n
var items = ['item1', 'item2', 'item3'];\n\nfunction functionToExecute(item) {\n  console.log('function executed for item: ' + item);\n}\n\n$.each(items, function (index, item) {\n  var timeoutValue = index * 2000;\n  setTimeout(function() {\n    console.log('waited ' + timeoutValue + ' milliseconds');\n    functionToExecute(item);\n  }, timeoutValue);\n});\n
\n

Result:

\n
waited 0 milliseconds\nfunction executed for item: item1\nwaited 2000 milliseconds\nfunction executed for item: item2\nwaited 4000 milliseconds\nfunction executed for item: item3\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259c082fcc3049e91e1c", + "creator": "chim", + "createdAt": 1483545415000, + "text": "tldr; you'll have to do something like tponthieux's answer when working in a loop context. (myArray.forEach or for(i=1; ...", + "upvotes": 594, + "upvoterUsernames": [], + "downvotes": 594, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e1e", + "creator": "chim", + "createdAt": 1483545559000, + "text": "We should get this answer voted up as it's useful and offers something different from the millions of other answers on this question.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e1f", + "creator": "Peter Mortensen", + "createdAt": 1625589815000, + "text": "Can you elaborate on "a closure call setTimeout()"? And what is the reason for "incrementally larger values"?", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9074c", + "creator": "Shemeer M Ali", + "createdAt": 1384022274000, + "text": "
function sleep(milliseconds) {\n  var start = new Date().getTime();\n  for (var i = 0; i < 1e7; i++) {\n    if ((new Date().getTime() - start) > milliseconds){\n      break;\n    }\n  }\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259c082fcc3049e91e21", + "creator": "EML", + "createdAt": 1402600609000, + "text": "It's the same as the real question. Not much point making it the real answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e23", + "creator": "emery", + "createdAt": 1430933597000, + "text": "Not a good solution - using this in Selenium's JavaScriptExecutor hangs my Chrome browser about 50% of the time on a 2104 MacBook Pro.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3259c082fcc3049e91e25", + "creator": "Peter Mortensen", + "createdAt": 1625589399000, + "text": "An explanation would be in order. What is the idea/gist? How is it different from previous answers?", + "upvotes": 264, + "upvoterUsernames": [], + "downvotes": 264, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9074e", + "creator": "Abdennour TOUMI", + "createdAt": 1401914486000, + "text": "

If you like an advise to not lose performance. setTimeout is your expected sleep.\nHowever, if you want a syntax where code is "divided in middle" by sleep, we can do:

\n
sleep = function(tm, fn){\n   window.setTimeout(fn, tm);\n}\n
\n

Then, prepare functions as in the following:

\n
var fnBeforeSleep = function(){\n\n  // All code before sleep\n\n}\n\nvar fnAfterSleep = function(){\n\n  // All code after sleep\n\n}\n
\n

Then:

\n
fnBeforeSleep();\nsleep(2000, fnAfterSleep);\n\n// Yep! Syntactically, it is very close to:\n\nfnBeforeSleep();\nsleep(2000);\nfnAfterSleep();\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9074f", + "creator": "Gabriel Ratener", + "createdAt": 1403676359000, + "text": "

Most of the answers here are misguided or at the very least outdated. There is no reason JavaScript has to be single threaded, and indeed it isn't. Today all the mainstream browsers support workers. Before this was the case, other JavaScript runtimes like Rhino and Node.js supported multithreading.

\n

'JavaScript is single threaded' is not a valid answer. For example, running a sleep function within a worker would not block any of the code running in the UI thread.

\n

In newer runtimes supporting generators and yield, one could bring similar functionality to the sleep function in a singlethreaded environment:

\n
// This is based on the latest ES6 drafts.\n// JavaScript 1.7+ (SpiderMonkey/Firefox 2+) syntax is slightly different\n\n// Run code you want to sleep here (omit star if using JavaScript 1.7)\nfunction* main(){\n    for (var i = 0; i < 10; i++) {\n        // To sleep for 10 milliseconds 10 times in a row\n        yield 10;\n    }\n\n    yield 5;\n    console.log('I just slept 5 milliseconds!');\n}\n\n// Resume the given generator after ms milliseconds\nfunction resume(ms, generator){\n    setTimeout(function(){\n        // Omit .value if using JavaScript 1.7\n        var nextSleep = generator.next().value;\n        resume(nextSleep, generator);\n    }, ms);\n}\n\n// Initialize a generator and get first sleep for the recursive function\nvar\n    generator = main(),\n    firstSleep = generator.next().value;\n\n// Initialize recursive resume function\nresume(firstSleep, generator);\n
\n

This imitation of sleep is different from a true sleep function as it does not block the thread. It is simply sugar on top of JavaScript's current setTimeout function. This functionality type has been implemented in Task.js and should work today in Firefox.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259d082fcc3049e91e27", + "creator": "Beejor", + "createdAt": 1434314666000, + "text": "Workers aren't implemented in IE, at least through version 10. Which currently represents a large amount of users.", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90750", + "creator": "pguardiario", + "createdAt": 1415056940000, + "text": "

Here's a simple solution using a synchronous XMLHttpRequest:

\n
function sleep(n){\n  var request = new XMLHttpRequest();\n  request.open('GET', '/sleep.php?n=' + n, false);  // `false` makes the request synchronous\n  request.send(null);\n}\n
\n

Contents of file sleep.php:

\n
<?php sleep($_GET['n']);\n
\n

Now call it with:

\n
sleep(5);\n
\n

Using an existing server implementation

\n

If you don't have your own application server (for the above PHP script), you could use some online service instead. For instance:

\n

\r\n
\r\n
function sleep(n) { \n    var request = new XMLHttpRequest();\n    request.open('GET', 'http://httpstat.us/200?sleep=' + n, false);\n    request.send(null);\n};\n\nsleep(1000);\nconsole.log(\"one second delay completed.\");
\r\n
\r\n
\r\n

\n

Support

\n

About passing false for the asynchronous parameter, mdn notes:

\n
\n

Synchronous requests on the main thread can be easily disruptive to the user experience and should be avoided; in fact, many browsers have deprecated synchronous XHR support on the main thread entirely. Synchronous requests are permitted in Workers.

\n
\n

The actual delay

\n

The number of milliseconds that is passed as argument will be the time that the server waits between receiving the request and sending the response. The delay incurred by transmission and server load will be added to that.

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259d082fcc3049e91e2a", + "creator": "Vahid Amiri", + "createdAt": 1463059099000, + "text": "This is actually a pretty good idea IMO. Although I don't think the sleep API (Request URL) should be public as it can be abused.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90751", + "creator": "1j01", + "createdAt": 1420346477000, + "text": "

If you want less clunky functions than setTimeout and setInterval, you can wrap them in functions that just reverse the order of the arguments and give them nice names:

\n
function after(ms, fn){ setTimeout(fn, ms); }\nfunction every(ms, fn){ setInterval(fn, ms); }\n
\n

CoffeeScript versions:

\n
after = (ms, fn)-> setTimeout fn, ms\nevery = (ms, fn)-> setInterval fn, ms\n
\n

You can then use them nicely with anonymous functions:

\n
after(1000, function(){\n    console.log("it's been a second");\n    after(1000, function(){\n        console.log("it's been another second");\n    });\n});\n
\n

Now it reads easily as "after N milliseconds, ..." (or "every N milliseconds, ...")

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90752", + "creator": "Rodrigo", + "createdAt": 1420841868000, + "text": "

In case you really need a sleep() just to test something. But be aware that it'll crash the browser most of the times while debugging - probably that's why you need it anyway. In production mode I'll comment out this function.

\n
function pauseBrowser(millis) {\n    var date = Date.now();\n    var curDate = null;\n    do {\n        curDate = Date.now();\n    } while (curDate-date < millis);\n}\n
\n

Don't use new Date() in the loop, unless you want to waste memory, processing power, battery and possibly the lifetime of your device.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259d082fcc3049e91e2c", + "creator": "Matt Gibson", + "createdAt": 1420842361000, + "text": "Your code is virtually the same as the code in the question, which asks if there's a better way of doing this.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90753", + "creator": "Xeridea", + "createdAt": 1421950762000, + "text": "

I know the question is about sleep, and clearly the answer is that it isn't possible. I think a common want for sleep is to handle asynchronous tasks in order; I know I have had to deal with it for sure.

\n

Many cases it may be able to use promises (Ajax requests common use). They let you do asynchronous things in a synchronous manner. There is also handling for success/failure, and they can be chained.

\n

They part of ECMAScript 6, so browser support isn't all there yet, mainly, Internet Explorer does not support them. There is also library called Q for doing promises.

\n

References:

\n\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259d082fcc3049e91e2f", + "creator": "Aadit M Shah", + "createdAt": 1421950818000, + "text": "This should really be a comment, not an answer.", + "upvotes": 181, + "upvoterUsernames": [], + "downvotes": 181, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90754", + "creator": "tomekwi", + "createdAt": 1423220138000, + "text": "

If you're on Node.js, you can have a look at fibers – a native C extension to node, a kind of-multi-threading simulation.

\n

It allows you to do a real sleep in a way which is blocking execution in a fiber, but it's non-blocking in the main thread and other fibers.

\n

Here's an example fresh from their own readme:

\n
// sleep.js\n\nvar Fiber = require('fibers');\n\nfunction sleep(ms) {\n    var fiber = Fiber.current;\n    setTimeout(function() {\n        fiber.run();\n    }, ms);\n    Fiber.yield();\n}\n\nFiber(function() {\n    console.log('wait... ' + new Date);\n    sleep(1000);\n    console.log('ok... ' + new Date);\n}).run();\nconsole.log('back in main');\n
\n

– and the results are:

\n
$ node sleep.js\nwait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)\nback in main\nok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90756", + "creator": "Elo", + "createdAt": 1436450361000, + "text": "

I would encapsulate setTimeOut in a Promise for code consistency with other asynchronous tasks: Demo in Fiddle

\n
function sleep(ms)\n{\n    return(new Promise(function(resolve, reject) {\n        setTimeout(function() { resolve(); }, ms);\n    }));\n}\n
\n

It is used like this:

\n
sleep(2000).then(function() {\n   // Do something\n});\n
\n

It is easy to remember the syntax if you are used to using Promises.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259d082fcc3049e91e33", + "creator": "JSideris", + "createdAt": 1457518536000, + "text": "Why is this any better than just using setTimeout(function(){/*do something*/}, 2000);?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90755", + "creator": "Ronald Davis", + "createdAt": 1428628153000, + "text": "

Here is a way to sleep in a .hta script, such that when the script wakes up it executes the next command in sequence, as is necessary in a loop. This is a real sleep; it does not keep a processor busy during the sleep. For example, the processor is able to download and render pages during the sleep.

\n

Just once, near the beginning of the code, go

\n
var WSHShell = new ActiveXObject ("WScript.Shell");\n
\n

For a sleep of e.g. 1 second = 1000 milliseconds, execute the statement

\n
WSHShell.Run('Sleep.js 1000', 3, true);\n
\n

In the same directory as the script is the file Sleep.js, which contains the following one line:

\n
WScript.Sleep(WScript.Arguments (0));\n
\n

(Beware; 0 is in parentheses, not brackets.) The latter is the line that actually performs the sleep. The argument true in the preceding snippet makes the call synchronous. The 3 in the preceding argument seems not to have any effect, but you need some argument so that true is the third argument.

\n

Microsoft says "The WScript object ... never needs to be instantiated before invoking its properties and methods, and it is always available from any script file.", but that's not true. It is available in a free-standing .js file such as the above, but apparently not in a .js file used by a .hta file, so that is why it must be in a separate file, invoked as above.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90758", + "creator": "JSideris", + "createdAt": 1457518003000, + "text": "

There's a new library, Sequencr.js, that neatly chains functions together with timeouts so you can avoid callback hell.

\n

It turns this:

\n
setTimeout(function(timeout){\n    function1();\n    setTimeout(function(timeout){\n        function2();\n        setTimeout(function(timeout){\n            function3();\n        }, timeout, timeout)\n    }, timeout, timeout)\n}, 10, 10);\n
\n

into this:

\n
Sequencr.chain([function1, function2, function3], 10);\n
\n

And has built-in support for loops that "sleep" between each iteration.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90757", + "creator": "Miscreant", + "createdAt": 1441082079000, + "text": "

To summarize (like it has been said in previous answers):

\n

There is no built-in sleep function in JavaScript. You should use setTimeout or setInterval to achieve a similar effect.

\n

If you really wanted to, you could simulate sleep functionality with a for loop such as the one shown in the original question, but that would make your CPU work like crazy. Inside a Web Worker an alternative solution would be to make a synchronous XMLHttpRequest to a non-responsive IP address and set a proper timeout. This would avoid the CPU utilization problem. Here's a code example:

\n

\r\n
\r\n
// Works only inside a web worker\n\nfunction sleep(milliseconds) {\n    var req = new XMLHttpRequest();\n    req.open(\"GET\", \"http://192.0.2.0/\", false);\n    req.timeout = milliseconds;\n    try {\n        req.send();\n    } catch (ex) {\n\n    }\n}\n\nconsole.log('Sleeping for 1 second...');\nsleep(1000);\nconsole.log('Slept!');\n\nconsole.log('Sleeping for 5 seconds...')\nsleep(5000);\nconsole.log('Slept!');
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9075a", + "creator": "Caty Lawren", + "createdAt": 1459455035000, + "text": "

If you really want to pause a script, you can do this:

\n
var milliseconds;\nvar pretime;\nvar stage;\n\nfunction step(time){\n  switch(stage){\n    case 0:\n      //Code before the pause\n\n      pretime=time;\n      milliseconds=XXX;\n      stage=1;\n      break;\n    case 1:\n      //Code that is looped through while paused\n\n      if(time-pretime >= milliseconds){\n        //Code after the pause\n\n        pretime=time;\n        milliseconds=XXX;\n        stage=2;\n      }\n      break;\n    case 2:\n      //Code that is looped through while paused\n\n      if(time-pretime >= milliseconds){\n        //Code after the pause\n\n        pretime=time;\n        milliseconds=XXX;\n        stage=3;\n      }\n      break;\n    case 3:\n      //Etc...\n  }\n\n  Window.requestAnimationFrame(step)\n}\n\nstep();\n
\n

This is probably exactly what you want if you use a loop anyway, and you can change it in ways so that you have pseudo-multi-threading, where you have some functions waiting a while and others running normally. I use this all the time for pure JavaScript games.

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90759", + "creator": "Aistis", + "createdAt": 1458742632000, + "text": "

Another possible way is:

\n\n
var _timer;\nclearTimeout(_timer);\n_timer = setTimeout(function() {\n    // Your code\n}, 1000); // Delay for 1 s.\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259e082fcc3049e91e39", + "creator": "Peter Mortensen", + "createdAt": 1625609724000, + "text": "An explanation would be in order. E.g., how is it different from previous answers?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9075b", + "creator": "ceremcem", + "createdAt": 1459974481000, + "text": "

In LiveScript (which compiles to JavaScript), you can do like the following:

\n
sleep = (ms, func) -> set-timeout func, ms\n\nconsole.log "hello-1"\n<- sleep 2000ms\nconsole.log "hello-2"\n<- sleep 2000ms\nconsole.log "hello-3"\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3259e082fcc3049e91e3b", + "creator": "fmsf", + "createdAt": 1460380774000, + "text": "that is just wrapping setTimeout in a function called sleep :p", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f3259e082fcc3049e91e3d", + "creator": "fmsf", + "createdAt": 1460467468000, + "text": "No it doesn't. I am very much against languages that just mangle JS sintax without any added value (i.e.: coffescript)", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90763", + "creator": "God Ѻ", + "createdAt": 1491318080000, + "text": "

I have had this question for a long time and the answer I needed was not exactly what has been provided here. This wait function causes a synchronous wait that does not tie up the CPU.

\n

Function waitForIt makes an Ajax request to anywhere and sets the async flag to false. Function waitF does the same with a frame and function waitD does the same with a div. Ajax takes about 100 ms, frame is about 25, and div is about 1.

\n

The wait function leverages all of these depending on how much time you give it. If it didn't wait long enough then do it again.

\n

I need this when dealing with multiple asynchronous loading elements. Basically for 'wait until this element exists'. You can play with it at https://jsfiddle.net/h2vm29ue/. It just leverages the things that the browser naturally waits for. The longer version, https://jsfiddle.net/5cov1p0z/32/, is more precise.

\n
 function waitForIt() {\n     var start = new Date();\n     var xhttp = new XMLHttpRequest();\n     xhttp.onreadystatechange = function() {\n         if (this.readyState == 4 && this.status == 200) {\n            // Doesn't matter\n         }\n     };\n     xhttp.open("GET", "WaitForIt", false);\n     xhttp.send();\n     var end = new Date();\n }\n //\n\n function waitF() {\n     var start = new Date();\n     var ifram = document.createElement('iframe');\n     ifram.id = 'ifram';\n     ifram.src = '';\n     var div = document.createElement('div');\n     div.id = 'timer';\n     document.body.appendChild(div);\n     document.getElementById('timer').appendChild(ifram);\n     document.getElementById('timer').removeChild(ifram);\n     document.body.removeChild(div);\n     var end = new Date();\n     return (end - start);\n }\n\n\n function waitD() {\n     var start = new Date();\n     var div = document.createElement('div');\n     div.id = 'timer';\n     document.body.appendChild(div);\n     div.click();\n     document.body.removeChild(div);\n     var end = new Date();\n     return (end - start);\n }\n\n function wait(time) {\n     var start = new Date();\n     var end = new Date();\n     while ((end - start < time)) {\n\n         if ((time - (end - start)) >= 200) {\n             waitForIt();\n         } else {\n             if ((time - (end - start)) >= 50) {\n                 waitF();\n             } else {\n                 waitD();\n             }\n         }\n         end = new Date();\n     }\n     return (end - start);\n }\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90764", + "creator": "Ivan", + "createdAt": 1491575976000, + "text": "

A function to sleep, using a synchronous call to let the OS do it. Use any OS sleep command you like. It is not busy waiting in the sense of using CPU time.

\n

I chose ping on a non-existent address.

\n
const cp = require('child_process');\n\nfunction sleep(ms)\n{\n    try{cp.execSync('ping 192.0.2.0 -n 1 -w '+ms);}\n    catch(err){}\n}\n
\n

A test to verify it works

\n
console.log(Date.now());\nconsole.log(Date.now());\nsleep(10000);\nconsole.log(Date.now());\nconsole.log(Date.now());\n
\n

And some test results.

\n
1491575275136\n1491575275157\n
\n

(and after 10 seconds)

\n
1491575285075\n1491575285076\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325db082fcc3049e91e40", + "creator": "Peter Mortensen", + "createdAt": 1625612225000, + "text": "But it doesn't work in a web browser context(?). What context does it work in?", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90766", + "creator": "Paul Stettler", + "createdAt": 1499715820000, + "text": "

JavaScript functions doesn't allow any suspension. With synchronous JavaScript, procedures are implemented. Procedures await I/O operations and sleep timeouts. It is available for JavaScript 1.7.

\n

Demos:

\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90765", + "creator": "CarstenP", + "createdAt": 1498565904000, + "text": "

Each and every solution I've read so far would be like "let's get to sleep and look what happened, tomorrow".

\n

setInterval(callback, time) would wait time long and then call the callback, while blocking the runtime. The current implementation of "setInterval" is far from being thread-save, not even to mind concurrency.

\n

While the sparse solutions mentioned look like, guess what, C# (laughs), they still don't work like in C#/.NET. They still work like in C.

\n

JavaScript currently does not provide an architecture to accomplish real multi-threading. The best approach would be TypeScript, but still this lacks so much of a real solution that it... hurts. JavaScript, and jQuery, and AJAX, and jNode, and even TypeScript are just a bunch of wannabes relying on the goods and bads of the moods of the implementers. Fact. Full stop.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325db082fcc3049e91e43", + "creator": "Shadow", + "createdAt": 1560484710000, + "text": "setInterval does not block - and more importantly, it repeats every time. This isn't even close to what is being asked.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90768", + "creator": "Stitch", + "createdAt": 1513701270000, + "text": "

It is now also possible to use the native module util to promisify regular sync functions.

\n\n
const { promisify } = require('util')\nconst sleep = promisify(setTimeout)\n\nmodule.exports = () => {\n  await someAsyncFunction()\n  await sleep(2000)\n  console.log('2 seconds later...')\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325db082fcc3049e91e46", + "creator": "chharvey", + "createdAt": 1526398063000, + "text": "you cannot use await inside a non-async function (in your example, the exported function)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90767", + "creator": "pomber", + "createdAt": 1506548078000, + "text": "

To keep the main thread busy for some milliseconds:

\n\n
function wait(ms) {\n  const start = performance.now();\n  while(performance.now() - start < ms);\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325db082fcc3049e91e49", + "creator": "Peter Mortensen", + "createdAt": 1625612747000, + "text": "Busy wait? Isn't it already covered by a previous answer?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90769", + "creator": "Harry", + "createdAt": 1530396410000, + "text": "

Since Node.js 7.6, you can combine the promisify function from the utils module with setTimeout.

\n
const sleep = require('util').promisify(setTimeout)\n
\n

General Usage

\n
async function main() {\n    console.time("Slept for")\n    await sleep(3000)\n    console.timeEnd("Slept for")\n}\n\nmain()\n
\n

Question Usage

\n
async function asyncGenerator() {\n    while (goOn) {\n      var fileList = await listFiles(nextPageToken);\n      await sleep(3000)\n      var parents = await requestParents(fileList);\n    }\n  }\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9076a", + "creator": "Franz Kiermaier", + "createdAt": 1537345704000, + "text": "

I had a similar problem, having to wait for control existence and checking in intervals.\nSince there is no real sleep, wait or pause in JavaScript and using await / async is not supported properly in Internet Explorer, I made a solution using setTimeOut and injecting the function in case of successfully finding the element.\nHere is the complete sample code, so everyone can reproduce and use it for their own project:

\n\n
<html>\n<head>\n    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n    <script type=\"text/javascript\">\n        var ElementSearchStatus = {\n            None: 0,\n            Found: 1,\n            NotFound: 2,\n            Timeout: 3\n        };\n\n        var maxTimeout = 5;\n        var timeoutMiliseconds = 1000;\n\n        function waitForElement(elementId, count, timeout, onSuccessFunction) {\n            ++count;\n            var elementSearchStatus = existsElement(elementId, count, timeout);\n            if (elementSearchStatus == ElementSearchStatus.None) {\n                window.setTimeout(waitForElement, timeoutMiliseconds, elementId, count, timeout, onSuccessFunction);\n            }\n            else {\n                if (elementSearchStatus == ElementSearchStatus.Found) {\n                    onSuccessFunction();\n                }\n            }\n        }\n\n        function existsElement(elementId, count, timeout) {\n            var foundElements = $(\"#\" + elementId);\n            if (foundElements.length > 0 || count > timeout) {\n                if (foundElements.length > 0) {\n                    console.log(elementId + \" found\");\n                    return ElementSearchStatus.Found;\n                }\n                else {\n                    console.log(\"Search for \" + elementId + \" timed out after \" + count + \" tries.\");\n                    return ElementSearchStatus.Timeout;\n                }\n            }\n            else {\n                console.log(\"waiting for \" + elementId + \" after \" + count + \" of \" + timeout);\n                return ElementSearchStatus.None;\n            }\n        }\n\n        function main() {\n            waitForElement(\"StartButton\", 0, maxTimeout, function () {\n                console.log(\"found StartButton!\");\n                DoOtherStuff(\"StartButton2\")\n            });\n        }\n\n        function DoOtherStuff(elementId) {\n            waitForElement(elementId, 0, maxTimeout, function () {\n                console.log(\"found \" + elementId);\n                DoOtherStuff(\"StartButton3\");\n            });\n        }\n    </script>\n</head>\n<body>\n    <button type=\"button\" id=\"StartButton\" onclick=\"main();\">Start Test</button>\n    <button type=\"button\" id=\"StartButton2\" onclick=\"alert('Hey ya Start Button 2');\">Show alert</button>\n</body>\n</html>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9076b", + "creator": "MUY Belgium", + "createdAt": 1538748717000, + "text": "

A very simple way to do do sleep, that will be compatible with anything that runs JavaScript... This code has been tested with something like 500 entries, and CPU and memory usage is still not visible on my web browsers.

\n

Here is one function that waits until the node becomes visible...

\n

This function creates a new context function () {} to avoid recursion. We placed code that does the same as the caller code inside this new context. We use the function Timeout to call our function after a few time second.

\n
var get_hyper = function(node, maxcount, only_relation) {\n    if (node.offsetParent === null) {\n        // node is hidden\n        setTimeout(function () { get_hyper(node, maxcount, only_relation)}, \n                   1000);\n        return;\n    };\n\n    // Enter the code here that waits for that node becoming visible\n    // before getting executed.\n\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9076c", + "creator": "Riccardo Persiani", + "createdAt": 1547204423000, + "text": "

I prefer this functional style one liner sleep function:

\n
const sleep = (ms) => new Promise((res) => setTimeout(res, ms, ms));\n\n// usage\nasync function main() {\n  console.log("before");\n  const t = await sleep(10_000); /* 10 sec */\n  console.log("after " + t);\n}\nmain();\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dc082fcc3049e91e4d", + "creator": "Zeel Shah", + "createdAt": 1550127989000, + "text": "This is not sleep funciton. If you call sleep function, it will not wait until time is lapsed, it will execute next instructions immediately.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9076e", + "creator": "Shankar", + "createdAt": 1553764788000, + "text": "

The problem with using an actual sleep function is that JavaScript is single-threaded and a sleep function will pretty much make your browser tab hang for that duration.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dc082fcc3049e91e50", + "creator": "Peter Mortensen", + "createdAt": 1625613500000, + "text": "That was already said in a previous answer.", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9076d", + "creator": "ByteMe", + "createdAt": 1551156537000, + "text": "

I got Promise is not a constructor using the top answer. If you import bluebird you can do this. It is the simplest solution, in my opinion.

\n
import * as Promise from 'bluebird';\n\nawait Promise.delay(5000)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9076f", + "creator": "Kapytanhook", + "createdAt": 1559386895000, + "text": "

2019 Update using Atomics.wait

\n

It should work in Node.js 9.3 or higher.

\n

I needed a pretty accurate timer in Node.js and it works great for that.

\n

However, it seems like there is extremely limited support in browsers.

\n

\r\n
\r\n
let ms = 10000;\nAtomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
\r\n
\r\n
\r\n

\n

Ran a few 10 second timer benchmarks.

\n

With setTimeout I get a error of up to 7000 microseconds (7 ms).

\n

With Atomics, my error seems to stay under 600 microseconds (0.6 ms)

\n

2020 Update: In Summary

\n
function sleep(millis){ // Need help of a server-side page\n  let netMillis = Math.max(millis-5, 0); // Assuming 5 ms overhead\n  let xhr = new XMLHttpRequest();\n  xhr.open('GET', '/sleep.jsp?millis=' + netMillis + '&rand=' + Math.random(), false);\n  try{\n    xhr.send();\n  }catch(e){\n  }\n}\n\nfunction sleepAsync(millis){ // Use only in async function\n  let netMillis = Math.max(millis-1, 0); // Assuming 1 ms overhead\n  return new Promise((resolve) => {\n    setTimeout(resolve, netMillis);\n  });\n}\nfunction sleepSync(millis){ // Use only in worker thread, currently Chrome-only\n  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, millis);\n}\n\nfunction sleepTest(){\n  console.time('sleep');\n  sleep(1000);\n  console.timeEnd('sleep');\n}\n\nasync function sleepAsyncTest(){\n  console.time('sleepAsync');\n  await sleepAsync(1000);\n  console.timeEnd('sleepAsync');\n}\n\nfunction sleepSyncTest(){\n  let source = `${sleepSync.toString()}\n    console.time('sleepSync');\n    sleepSync(1000);\n    console.timeEnd('sleepSync');`;\n  let src = 'data:text/javascript,' + encodeURIComponent(source);\n  console.log(src);\n  var worker = new Worker(src);\n}\n
\n

of which the server-side page, e.g. sleep.jsp, looks like:

\n
<%\ntry{\n  Thread.sleep(Long.parseLong(request.getParameter("millis")));\n}catch(InterruptedException e){}\n%>\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dc082fcc3049e91e53", + "creator": "GroovyDotCom", + "createdAt": 1564914507000, + "text": "In my opinion better than the accepted solution which can't be implemented as a simple function without async/await in the caller.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f325dc082fcc3049e91e55", + "creator": "Kapytanhook", + "createdAt": 1565081006000, + "text": "yeah, as long as you are aware that this is blocking and that usually is not a good thing", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325dc082fcc3049e91e57", + "creator": "vitiral", + "createdAt": 1612730757000, + "text": "This was the answer I was looking for! I had no access to async functions :D", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90770", + "creator": "Ahmed Mohammedali", + "createdAt": 1561548432000, + "text": "

Use:

\n

\r\n
\r\n
  await new Promise(resolve => setTimeout(resolve, 2000));
\r\n
\r\n
\r\n

\n

Make sure your calling function is async. This is verified and is working fine.

\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dc082fcc3049e91e59", + "creator": "Philipp Ludwig", + "createdAt": 1656686243000, + "text": "This just yields ReferenceError: wait is not defined.", + "upvotes": 167, + "upvoterUsernames": [], + "downvotes": 167, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90771", + "creator": "k06a", + "createdAt": 1574170476000, + "text": "

The shortest solution without any dependencies:

\n\n
await new Promise(resolve => setTimeout(resolve, 5000));\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dc082fcc3049e91e5c", + "creator": "DDphp", + "createdAt": 1579679331000, + "text": "It doesn't work in IE11. We get syntax error for arrow function.", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + }, + { + "_id": "62f325dc082fcc3049e91e5e", + "creator": "k06a", + "createdAt": 1579693438000, + "text": "@Java-DK use await new Promise(function(resolve) { setTimeout(resolve, 5000); });", + "upvotes": 5184, + "upvoterUsernames": [], + "downvotes": 5184, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90772", + "creator": "sfscs", + "createdAt": 1582249103000, + "text": "

If you really want to block the main thread altogether and keep the event loop from pulling from the event queue, here's a nice way to do that without creating any functions, new Date objects or leaking any variables. I know there's a million answers to this silly question already, but I didn't see anyone using this exact solution. This is for modern browsers only.

\n

Warning: This is not something you would ever put into production. It is just helpful for understanding the browser event loop. It is probably not even useful for any testing. It is not like a normal system sleep function because the JavaScript runtime is still doing work every cycle.

\n
for (let e = performance.now() + 2000; performance.now() < e; ) {}\n
\n

Used here, the setTimeout callback won't be called until at least two seconds later even though it enters the event queue almost instantly:

\n
setTimeout(function() {\n  console.log("timeout finished");\n}, 0);\n\nfor (let e = performance.now() + 2000; performance.now() < e; ) {}\nconsole.log("haha wait for me first");\n
\n

You will experience a approximate two second pause and then see:

\n
haha wait for me first\ntimeout finished\n
\n

The benefit of using performance.now() over Date.now() is that that the Date object is

\n
\n

subject to both clock skew and adjustment of the system clock. The\nvalue of time may not always be monotonically increasing and\nsubsequent values may either decrease or remain the same.\n*

\n
\n

In general, performance.now() is more suited to measuring differences in time at high accuracy.

\n

Using a for loop has the benefit of letting you set variables local to the block before running. This allows you to do the addition math outside the loop while still being a 'one-liner'. This should hopefully minimize the CPU load of this hot cycle burn.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90773", + "creator": "Jared Dykstra", + "createdAt": 1583340703000, + "text": "

Using TypeScript:

\n

Here's a quick sleep() implementation that can be awaited. This is as similar as possible to the top answer. It's functionally equivalent, except ms is typed as number for TypeScript.

\n
const sleep = (ms: number) =>\n  new Promise((resolve) => setTimeout(resolve, ms));\n\nasync function demo() {\n  console.log('Taking a break for 2s (2000ms)...');\n  await sleep(2000);\n  console.log('Two seconds later');\n}\n\ndemo();\n
\n

This is it. await sleep(<duration>).

\n

Note that,

\n
    \n
  1. await can only be executed in functions prefixed with the async keyword, or at the top level of your script in some environments (e.g., the Chrome DevTools console, or Runkit).
  2. \n
  3. await only pauses the current async function.
  4. \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90774", + "creator": "Ludolfyn", + "createdAt": 1588347475000, + "text": "

You could do something like this. A sleep method that all functions can inherit:

\n

\r\n
\r\n
Function.prototype.sleep = function(delay, ...args) {\n    setTimeout(() => this(...args), delay)\n}\n\nconsole.log.sleep(2000, 'Hello, World!!')
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dd082fcc3049e91e62", + "creator": "pasha", + "createdAt": 1592396038000, + "text": "My favorite so far", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90776", + "creator": "nkitku", + "createdAt": 1604975545000, + "text": "

One-liner using Promises

\n
const sleep = t => new Promise(s => setTimeout(s, t));\n
\n

Demo

\n

\r\n
\r\n
const sleep = t => new Promise(s => setTimeout(s, t));\n// Usage\nasync function demo() {\n    // Count down\n    let i = 6;\n    while (i--) {\n        await sleep(1000);\n        console.log(i);\n    }\n    // Sum of numbers 0 to 5 using by delay of 1 second\n    const sum = await [...Array(6).keys()].reduce(async (a, b) => {\n        a = await a;\n        await sleep(1000);\n        const result = a + b;\n        console.log(`${a} + ${b} = ${result}`);\n        return result;\n    }, Promise.resolve(0));\n    console.log(\"sum\", sum);\n}\ndemo();
\r\n
\r\n
\r\n

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dd082fcc3049e91e65", + "creator": "T.Woody", + "createdAt": 1611267408000, + "text": "The copy/pasta is real", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90775", + "creator": "Tawfik Nasser", + "createdAt": 1601060788000, + "text": "

In the sleep method you can return any then-able object. And not necessarily a new promise.

\n

Example:

\n

\r\n
\r\n
const sleep = (t) =>  ({ then: (r) => setTimeout(r, t) })\n\nconst someMethod = async () => {\n\n    console.log(\"hi\");\n    await sleep(5000)\n    console.log(\"bye\");\n}\n\nsomeMethod()
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90777", + "creator": "HoldOffHunger", + "createdAt": 1612909157000, + "text": "

If you want code that is usable on all browsers, then use setTimeout() and clearTimeout(). If you're reading this far into answers, you'll probably notice that the accepted answer breaks all JavaScript compilation in Internet Explorer 11, and after using this solution, it seems that 5% of users approximately still use this actively-developed browser and require support.

\n

This has broken almost everything. There are known reports of arrow functions breaking Internet Explorer 11 functionality for the software of Drupal, WordPress, Amazon AWS, IBM, and there's even a dedicated discussion on it on Stack Overflow.

\n

Just check it out...

\n

\"Browser

\n

\"Browser

\n

Use setTimeout() and clearTimeout(), and that will do the trick for all browsers...

\n

Working JSBin Demo

\n

\r\n
\r\n
var timeout;\n\nfunction sleep(delay) {\n    if(timeout) {\n        clearTimeout(timeout);\n    }\n    timeout = setTimeout(function() {\n        myFunction();\n    }, delay);\n}\n\nconsole.log(\"sleep for 1 second\");\nsleep(1000);\n\nfunction myFunction() {\n    console.log(\"slept for 1 second!\");\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90778", + "creator": "Mir-Ismaili", + "createdAt": 1618970754000, + "text": "

Since April 2021 (Node.js 16+), a new promisified version of setTimeout() is available:

\n
import { setTimeout } from 'timers/promises'\n\nconst res = await setTimeout(2000, 'result')\n\nconsole.log(res);  // Prints 'result'\n
\n
\n

Thank @kigiri. See the official documentation:\nhttps://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9077a", + "creator": "Henke", + "createdAt": 1621960129000, + "text": "
\n

What is the JavaScript version of sleep()?

\n
\n

This has already been answered in the currently accepted answer:

\n
await new Promise(r => setTimeout(r, 1000));\n
\n

Two asynchronous functions running simultaneously

\n

It is a good idea to put it inside a function sleep(), and then\nawait sleep().
\nTo use it, a bit of context is needed:

\n

\r\n
\r\n
function sleep (ms) { return new Promise(r => setTimeout(r, ms)); }\n\n(async function slowDemo () {\n  console.log('Starting slowDemo ...');\n  await sleep(2000);\n  console.log('slowDemo: TWO seconds later ...');\n})();\n\n(async function fastDemo () {\n  console.log('Starting fastDemo ...');\n  await sleep(500);\n  for (let i = 1; i < 6; i++) {\n    console.log('fastDemo: ' + (i * 0.5) + ' seconds later ...');\n    await sleep(500);\n  }\n})();
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
\r\n
\r\n

\n

Two asynchronous calls running in sequence – one after the other

\n

But suppose slowDemo produces some result that fastDemo\ndepends upon.
\nIn such a case, slowDemo must run to completion before fastDemo starts:

\n

\r\n
\r\n
function sleep (ms) { return new Promise(r => setTimeout(r, ms)); }\n\n(async () => {\n  await (async function slowDemo () {\n    console.log('Starting slowDemo ...');\n    await sleep(2000);\n    console.log('slowDemo: TWO seconds later ... completed!');\n  })();\n\n  (async function fastDemo () {\n    console.log('Starting fastDemo ...');\n    await sleep(500);\n    let i = -2;\n    for (i = 1; i < 5; i++) {\n      console.log('fastDemo: ' + (i * 0.5) + ' seconds later ...');\n      await sleep(500);\n    }\n    console.log('fastDemo: ' + (i * 0.5) + ' seconds later. Completed!');\n  })();\n})();
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90779", + "creator": "quicVO", + "createdAt": 1621438410000, + "text": "

2021+ Update

\n

If you are looking for an alternative to:

\n
let sleep = ms => new Promise(res=>setTimeout(res,ms));\n
\n

Then use this:

\n
let sleep = async ms => void await Atomics.waitAsync(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms).value;\n
\n

Note, that as of when this question is posted, it is a Stage 3 proposal. Also, it may require your site to be cross-origin isolated. To see if it works in your browser, (on Stack Overflow) try this:

\n

\r\n
\r\n
let sleep = async ms => void await Atomics.waitAsync(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms).value;\n\nvoid async function() {\n  console.log(1);\n  await sleep(2000);\n  console.log(2);\n}()
\r\n
\r\n
\r\n

\n", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dd082fcc3049e91e6a", + "creator": "mousetail", + "createdAt": 1624017805000, + "text": "What is the advantage of this method over setTimeout?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f325dd082fcc3049e91e6c", + "creator": "Ray Foss", + "createdAt": 1655905002000, + "text": "@mousetail it's pricise to less than a millisecond. In browsers setTimeout can be off my 100ms, specially Firefox with high privacy settings.", + "upvotes": 199, + "upvoterUsernames": [], + "downvotes": 199, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9077c", + "creator": "Nalin Ranjan", + "createdAt": 1625587718000, + "text": "

JavaScript is a single-threaded execution environment. Every function out there is getting executed by just one thread. Asking for a "sleep" means you want to halt the whole VM instance for those many (milli)seconds, which I don't think is a very realistic ask in the JavaScript world, as not only that particular function, but the rest of all executions suffer if the executing thread is made to sleep.

\n

First of all, it is not possible to actually sleep that thread. You can only try to keep that thread busy by putting into some work doing no thing. However, if you want to only add a delay in some computation(s), put their executions inside a single function context, and let the continuation happen inside a setTimeout (or a Promise, if you prefer) from within that function. If by sleep, you are looking to delay something, otherwise, it's not possible.

\n", + "upvotes": 590, + "upvoterUsernames": [], + "downvotes": 590, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9077d", + "creator": "Aleksandr Chernyi", + "createdAt": 1634134105000, + "text": "
require('deasync').sleep(100);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dd082fcc3049e91e70", + "creator": "Hovercraft Full Of Eels", + "createdAt": 1642959421000, + "text": "@OhadCohen: not the down-voter, but how do you propose to identify those who you feel should be "blacklisted" since voting is anonymous?", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9077e", + "creator": "Jacob", + "createdAt": 1638123554000, + "text": "

The currently accepted solution of using async/await and setTimeout is perfect if you need to really wait that many seconds. If you are using it for on-screen animations, however, you should really be using requestAnimationFrame(). This functions very similarly to setTimeout, but the callback is only called when the animation is visible to the user. That means that if you're running an animation on your site and the user switches tabs, the animation will pause and save battery life.

\n

Here's an implementation of the wait method using requestAnimationFrame. It takes in a number of frames and resolves once they have all passed:

\n

\r\n
\r\n
const animationWait = (frames) => \n  new Promise((resolve) => {\n    let framesPassed = 0;\n    requestAnimationFrame(function loop() {\n      if (++framesPassed >= frames) return resolve();\n      requestAnimationFrame(loop);\n    });\n  });\n  \n// typewriter effect for demonstration\nconst content = document.querySelector(\".content\");\n\nasync function typeWriter(endText, wait) {\n  content.textContent = \"\";\n  for (const letter of endText) {\n    content.textContent += letter;\n    await animationWait(wait);\n  }\n}\n\ntypeWriter(\"Okay. This simple typewriter effect is an example of requestAnimationFrame.\", 8);
\r\n
<p>\n  The animation will play below; Try switching tabs and see that   \n  the animation pauses.\n</p>\n<code class=\"content\"></code>
\r\n
\r\n
\r\n

\n

Read more about requestAnimationFrame

\n

Browser support (IE10+)

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9077b", + "creator": "Ravinder Payal", + "createdAt": 1622311571000, + "text": "

An inliner:

\n
(async () => await new Promise(resolve => setTimeout(resolve, 500)))();\n
\n

500 here is the time in milliseconds for which VM will wait before moving to the next line of code.

\n

Bit of tldr;

\n

Basically, when you create a promise, it returns an observable while at creation giving a reference of resolve in a callback meant for handing over data/response once it's available. Here, resolve is called via setTimeOut after 500ms, and till resolve is not executed the outside scope is waiting to proceed further, hence, creating a fake blocking. It's totally different than the non-blocking(or call non-thread-reserving sleep available in other languages), as the thread and most probably the UI and any other ongoing tasks of webpage/node-application will be blocked and the main thread will be exclusively used for awaiting the promise resolution.

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325dd082fcc3049e91e73", + "creator": "sureshvv", + "createdAt": 1625039483000, + "text": "I am unable to grok the part where function resolve is defined as calling itself. How does this make sense?", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 123, + "downvoterUsernames": [] + }, + { + "_id": "62f325dd082fcc3049e91e75", + "creator": "Ravinder Payal", + "createdAt": 1633409641000, + "text": "@sureshvv check update.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f325dd082fcc3049e91e77", + "creator": "Matias Haeussler", + "createdAt": 1643729233000, + "text": "If want to put in the middle of an async function just use await new Promise(resolve => setTimeout(resolve, sleepMiliseconds))", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90780", + "creator": "S. Hesam", + "createdAt": 1651933896000, + "text": "

UPDATE 2022

\n

Just use this code snippet.

\n
await new Promise(resolve => setTimeout(resolve, 2000));\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9077f", + "creator": "ForeverLearner", + "createdAt": 1638629992000, + "text": "

This is a blocking version of sleep. Found it easier to follow during testing activities where you need sequential execution. It can be called like sleep(2000) to sleep the thread for 2 seconds.

\n
function sleep(ms) {\n    const now = Date.now();\n    const limit = now + ms;\n    let execute = true;\n    while (execute) {\n        if (limit < Date.now()) {\n            execute = false;\n        }\n    }\n    return;\n  }\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90782", + "creator": "Ran Turner", + "createdAt": 1654453460000, + "text": "

The setTimeout is part of the JavaScript asynchronous methods (methods that are starting to execute and their result will return sometime in the future to a component called the callback queue, later to be executed)

\n

What you probably want to do is to wrap that setTimeout function within a Promise.

\n

promise example:

\n

\r\n
\r\n
const sleep = time => new Promise(res => setTimeout(res, time, \"done sleeping\"));\n\n// using native promises\nsleep(2000).then(msg => console.log(msg));
\r\n
\r\n
\r\n

\n

async/await example:

\n

\r\n
\r\n
const sleep = time => new Promise(res => setTimeout(res, time, \"done sleeping\"));\n\n// using async/await in top level\n(async function(){\n  const msg = await sleep(2000);\n  console.log(msg);\n})();
\r\n
\r\n
\r\n

\n

More about setTimeout can be found here

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90781", + "creator": "Arjun G Perambra", + "createdAt": 1653669620000, + "text": "

I suggest this method for former python developers

\n
const sleep = (time) => {\n   return new Promise((resolve) => setTimeout(resolve, Math.ceil(time * 1000)));\n};\n
\n

Usage:

\n
await sleep(10) // for 10 seconds\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90783", + "creator": "Ray Foss", + "createdAt": 1655616556000, + "text": "

A SAFER async sleep with better DX

\n

I use sleep for debugging... and I've forgotten to use await a dozen too many times. Leaving me scratching my head. I forgot to use await while writing the live snippet below... NO MORE!

\n

The sleep function below alerts you if you run it twice in 1 ms. It also supports passing a quite parameter, if you're sure you used await. It does not throw so it should be safe to use as a replacement for sleep(s). Enjoy.

\n

Yes there is a JavaScript version in the live snippet.

\n
// Sleep, with protection against accidentally forgetting to use await\nexport const sleep = (s: number, quiet?: boolean) => {\n  const sleepId = 'SLEEP_' + Date.now()\n  const G = globalThis as any\n  if (G[sleepId] === true && !quiet) {\n    console.error('Did you call sleep without await? use quiet to hide msg.')\n  } else {\n    G[sleepId] = true\n  }\n  return new Promise((resolve) => {\n    !quiet && setTimeout(() => {\n      delete G[sleepId]\n    }, 1)\n    setTimeout(resolve, (s * 1000) | 0)\n  })\n}\n\n
\n

\r\n
\r\n
// Sleep, with protection agains accidentally forgetting to use await\nconst sleep = (s, quiet) => {\n  const sleepId = 'SLEEP_' + Date.now()\n  const G = globalThis\n  if (G[sleepId] === true && !quiet) {\n    console.error('Did you call sleep without await? use quiet to hide msg.')\n  } else {\n    G[sleepId] = true\n  }\n  return new Promise((resolve) => {\n    !quiet && setTimeout(() => {\n      delete G[sleepId]\n    }, 1)\n    setTimeout(resolve, (s * 1000) | 0)\n  })\n}\n\nconst main = async() => {\n  console.log('Sleeping for 1 second...')\n  await sleep(1)\n  console.log('forgetting to use await...')\n  sleep(99)\n  sleep(99)\n  await sleep(1, true)\n  console.log('Happy developer!')\n}\nmain()
\r\n
\r\n
\r\n

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9075d", + "creator": "Paul Sweatte", + "createdAt": 1467419129000, + "text": "

Use three functions:

\n
    \n
  1. A function which calls setInterval to start the loop
  2. \n
  3. A function which calls clearInterval to stop the loop, then calls setTimeout to sleep, and finally calls to within the setTimeout as the callback to restart the loop
  4. \n
  5. A loop which tracks the number of iterations, sets a sleep number and a maximum number, calls the sleep function once the sleep number has been reached, and calls clearInterval after the maximum number has been reached
  6. \n
\n

\r\n
\r\n
var foo = {};\n\nfunction main()\n{\n  'use strict';\n\n  /* Initialize global state */\n  foo.bar = foo.bar || 0;\n\n  /* Initialize timer */\n  foo.bop = setInterval(foo.baz, 1000);\n}\n\nsleep =\n  function(timer)\n  {\n    'use strict';\n    clearInterval(timer);\n    timer = setTimeout(function(){main()}, 5000);\n  };\n\nfoo.baz =\n  function()\n  {\n    'use strict';\n\n    /* Update state */\n    foo.bar = Number(foo.bar + 1) || 0;\n\n    /* Log state */\n    console.log(foo.bar);\n\n    /* Check state and stop at 10 */\n    (foo.bar === 5) && sleep(foo.bop);\n    (foo.bar === 10) && clearInterval(foo.bop);\n  };\n\nmain();
\r\n
\r\n
\r\n

\n

\"Event

\n

References

\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9075c", + "creator": "TheComputerGuy", + "createdAt": 1464656473000, + "text": "

This might work. It worked for me in C and JavaScript.

\n
function sleep(time) {\n  var x = 0;\n  for(x = 0;x < time;x++) {/* Do nothing */}\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e45", + "creator": "Alwin Kesler", + "createdAt": 1464879327000, + "text": "The use of a for/while/foreach should be avoid in javascript. Consider setTimeout() or setInverval() instead", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e9075f", + "creator": "O_Z", + "createdAt": 1476369064000, + "text": "

The problem with most solutions here is that they rewind the stack. This can be a big problem in some cases. In this example I show how to use iterators in a different way to simulate real sleep.

\n

In this example the generator is calling its own next(), so once it's going, it's on its own.

\n
var h = a();\nh.next().value.r = h; // That's how you run it. It is the best I came up with\n\n// Sleep without breaking the stack!!!\nfunction *a(){\n    var obj = {};\n\n    console.log("going to sleep....2s")\n\n    setTimeout(function(){obj.r.next();}, 2000)\n    yield obj;\n\n    console.log("woke up");\n    console.log("going to sleep no 2....2s")\n    setTimeout(function(){obj.r.next();}, 2000)\n    yield obj;\n\n    console.log("woke up");\n    console.log("going to sleep no 3....2s")\n\n    setTimeout(function(){obj.r.next();}, 2000)\n    yield obj;\n\n    console.log("done");\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9075e", + "creator": "Guang-De Lin", + "createdAt": 1472466413000, + "text": "

With await support and bluebird promise:

\n
await bluebird.delay(1000);\n
\n

This will work like a synchronous sleep(1) of the C language. My favorite solution.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e48", + "creator": "Dan Dascalescu", + "createdAt": 1475833734000, + "text": "If you have await support, you don't need Bluebird.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90760", + "creator": "bitifet", + "createdAt": 1476425548000, + "text": "

At server side, you can use the deasync sleep() method, which is natively implemented in C so it can effectively implement a wait effect without blocking the event loop or putting your CPU at 100% of load.

\n

Example:

\n
#!/usr/bin/env node\n\n// Requires `npm install --save deasync`\nvar sleep = require("deasync").sleep;\n\nsleep(5000);\n\nconsole.log ("Hello World!!");\n
\n

But, if you need a pure JavaScript function (for example, to run it at client-side by a browser), I'm sorry to say that I think your pausecomp() function is the only way to approach it and, more than that:

\n
    \n
  1. That pauses not only your function but the whole event loop. So no other events will be attended.

    \n
  2. \n
  3. It puts your CPU at 100% load.

    \n
  4. \n
\n

So, if you need it for a browser script and doesn't want those terrible effects, I must say you should rethink your function in a way:

\n

a). You can recall it (or call a do_the_rest() function) at a timeout. The easier way if you are not expecting any result from your function.

\n

b). Or, if you need to wait for a result, then you should move to using promises (or a callback hell, of course ;-)).

\n

No result expected example:

\n
function myFunc() {\n\n    console.log ("Do some things");\n\n    setTimeout(function doTheRest(){\n        console.log ("Do more things...");\n    }, 5000);\n\n    // Returns undefined.\n};\n\nmyFunc();\n
\n

An example returning a promise (notice it alters your function usage):

\n
function myFunc(someString) {\n\n    return new Promise(function(resolve, reject) {\n\n        var result = [someString];\n        result.push("Do some things");\n\n        setTimeout(function(){\n            result.push("Do more things...");\n            resolve(result.join("\\n"));\n        }, 5000);\n    });\n};\n\n\n// But notice that this approach affect to the function usage...\n// (It returns a promise, not actual data):\nmyFunc("Hello!!").then(function(data){\n    console.log(data);\n}).catch(function(err){\n    console.error(err);\n});\n
\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90762", + "creator": "Taulant", + "createdAt": 1482400307000, + "text": "

This will do you the trick.

\n\n
var reloadAfter = 10; //seconds\nvar intervalId = setTimeout(function() {\n    //code you want to execute after the time waiting\n}, reloadAfter * 1000); // 60000 = 60 sec = 1 min\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e4e", + "creator": "Peter Mortensen", + "createdAt": 1625611868000, + "text": "How is this different from previous answers?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90761", + "creator": "melMass", + "createdAt": 1478980593000, + "text": "

I personally like the simple:

\n
function sleep(seconds){\n    var waitUntil = new Date().getTime() + seconds*1000;\n    while(new Date().getTime() < waitUntil) \n        true;\n}\n
\n

then:

\n
sleep(2); // Sleeps for 2 seconds\n
\n

I'm using it all the time to create fake load times while creating scripts in p5.js.

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e50", + "creator": "eaorak", + "createdAt": 1513593965000, + "text": "NEVER EVER DO THAT. Have you checked the CPU usage while this function is working? It should be close to 100% if you give enough time for it.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f329c0082fcc3049e92e51", + "creator": "melMass", + "createdAt": 1583943660000, + "text": "@noego How so ? I just tested in Node 10, I have no CPU usage change at all", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f329c0082fcc3049e92e53", + "creator": "Hardik Mandankaa", + "createdAt": 1604408771000, + "text": "It is block rendering until sleep time.", + "upvotes": 407, + "upvoterUsernames": [], + "downvotes": 407, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321c2082fcc3049e9069f", + "creator": "17 of 26", + "createdAt": 1244126822000, + "text": "This is a horrible solution - you're going to be chewing up processing cycles while doing nothing.", + "upvotes": 348, + "upvoterUsernames": [], + "downvotes": 111, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a0", + "creator": "annakata", + "createdAt": 1244127030000, + "text": "The only purpose for a sleep is polling or waiting for a callback - setInterval and setTimeout do both better than this.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3963768, + "uvac": 3963851 + } + }, + { + "_id": "62f321bb082fcc3049e8fefc", + "title": "How do I chop/slice/trim off last character in string using Javascript?", + "title-lowercase": "how do i chop/slice/trim off last character in string using javascript?", + "creator": "Phill Pafford", + "createdAt": 1244147513000, + "status": "open", + "text": "

I have a string, 12345.00, and I would like it to return 12345.0.

\n\n

I have looked at trim, but it looks like it is only trimming whitespace and slice which I don't see how this would work. Any suggestions?

\n", + "upvotes": 2437, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2178899, + "answers": 24, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90e33", + "creator": "Paolo Bergantino", + "createdAt": 1244148136000, + "text": "

For a number like your example, I would recommend doing this over substring:

\n\n

\r\n
\r\n
console.log(parseFloat('12345.00').toFixed(1));
\r\n
\r\n
\r\n

\n\n

Do note that this will actually round the number, though, which I would imagine is desired but maybe not:

\n\n

\r\n
\r\n
console.log(parseFloat('12345.46').toFixed(1));
\r\n
\r\n
\r\n

\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e9297a", + "creator": "naugtur", + "createdAt": 1354728216000, + "text": "+1 This is what OP needs, forget the false assumption that there are strings to be trimmed.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e9297b", + "creator": "Fernando", + "createdAt": 1433245946000, + "text": "It is not a false assumption, OP talks about strings. Yours is a false assumption because OP doesn't talk about rounding numbers.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e32", + "creator": "dariom", + "createdAt": 1244147891000, + "text": "

How about:

\n\n

\r\n
\r\n
let myString = \"12345.00\";\r\nconsole.log(myString.substring(0, myString.length - 1));
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e34", + "creator": "PatrikAkerstrand", + "createdAt": 1244148427000, + "text": "

If you want to do generic rounding of floats, instead of just trimming the last character:

\n\n
var float1 = 12345.00,\n    float2 = 12345.4567,\n    float3 = 12345.982;\n\nvar MoreMath = {\n    /**\n     * Rounds a value to the specified number of decimals\n     * @param float value The value to be rounded\n     * @param int nrDecimals The number of decimals to round value to\n     * @return float value rounded to nrDecimals decimals\n     */\n    round: function (value, nrDecimals) {\n        var x = nrDecimals > 0 ? 10 * parseInt(nrDecimals, 10) : 1;\n        return Math.round(value * x) / x;\n    }\n}\n\nMoreMath.round(float1, 1) => 12345.0\nMoreMath.round(float2, 1) => 12345.5\nMoreMath.round(float3, 1) => 12346.0\n
\n\n

EDIT: Seems like there exists a built in function for this, as Paolo points out. That solution is obviously much cleaner than mine. Use parseFloat followed by toFixed

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e36", + "creator": "swift", + "createdAt": 1280854358000, + "text": "
\n

@Jason S:

\n \n

You can use slice! You just have to\n make sure you know how to use it.\n Positive #s are relative to the\n beginning, negative numbers are\n relative to the end.

\n \n

js>\"12345.00\".slice(0,-1)\n 12345.0

\n
\n\n

Sorry for my graphomany but post was tagged 'jquery' earlier. So, you can't use slice() inside jQuery because slice() is jQuery method for operations with DOM elements, not substrings ... \nIn other words answer @Jon Erickson suggest really perfect solution.

\n\n

However, your method will works out of jQuery function, inside simple Javascript.\nNeed to say due to last discussion in comments, that jQuery is very much more often renewable extension of JS than his own parent most known ECMAScript.

\n\n

Here also exist two methods:

\n\n

as our:

\n\n

string.substring(from,to) as plus if 'to' index nulled returns the rest of string. so:\nstring.substring(from) positive or negative ...

\n\n

and some other - substr() - which provide range of substring and 'length' can be positive only:\nstring.substr(start,length)

\n\n

Also some maintainers suggest that last method string.substr(start,length) do not works or work with error for MSIE.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e9297e", + "creator": "naugtur", + "createdAt": 1354703902000, + "text": "What do you mean? String.prototype.slice is a native method", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e9297f", + "creator": "swift", + "createdAt": 1354730564000, + "text": "and you decided discuss it with me? then you should to know that your mythology is many more inconsistent. yes. just it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92981", + "creator": "naugtur", + "createdAt": 1354732130000, + "text": "let us continue this discussion in chat", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92982", + "creator": "reconbot", + "createdAt": 1370919519000, + "text": "I can agree with @naugtur this answer is wrong, the string's slice method is not effected by jQuery.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e35", + "creator": "Jason S", + "createdAt": 1244151702000, + "text": "

You can use slice! You just have to make sure you know how to use it. Positive #s are relative to the beginning, negative numbers are relative to the end.

\n\n
js>\"12345.00\".slice(0,-1)\n12345.0\n
\n", + "upvotes": 2637, + "upvoterUsernames": [], + "downvotes": 1161, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e92984", + "creator": "Sameer Alibhai", + "createdAt": 1341348097000, + "text": "Compared to the accepted solution, this is way more elegant and can be used even with dynamically created strings", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92986", + "creator": "pathfinder", + "createdAt": 1363328651000, + "text": "I like this way because it jives with php thinking for substr function, easier to remember and write on the fly.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92988", + "creator": "Miscreant", + "createdAt": 1436286460000, + "text": "Should be added that the first index is inclusive and the second exclusive.", + "upvotes": 189, + "upvoterUsernames": [], + "downvotes": 189, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e3a", + "creator": "w35l3y", + "createdAt": 1283137742000, + "text": "

A regular expression is what you are looking for:

\n\n

\r\n
\r\n
let str = \"foo_bar\";\r\nconsole.log(str.replace(/_bar$/, \"\"));
\r\n
\r\n
\r\n

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e92989", + "creator": "Aaron", + "createdAt": 1529346503000, + "text": "This solves a related problem of conditional removing rather than blindly truncating", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e37", + "creator": "griegs", + "createdAt": 1283137682000, + "text": "

Use substring to get everything to the left of _bar. But first you have to get the instr of _bar in the string:

\n\n
str.substring(3, 7);\n
\n\n

3 is that start and 7 is the length.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e9298b", + "creator": "Design by Adrian", + "createdAt": 1348228130000, + "text": "this only works on "foo_bar" not on "foo_foo_bar", the question was about a string with any length but a known end.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e39", + "creator": "Matthew Flaschen", + "createdAt": 1283137730000, + "text": "
if(str.substring(str.length - 4) == \"_bar\")\n{\n    str = str.substring(0, str.length - 4);\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e38", + "creator": "Alex Martelli", + "createdAt": 1283137729000, + "text": "

You can use the substring method of JavaScript string objects:

\n\n
s = s.substring(0, s.length - 4)\n
\n\n

It unconditionally removes the last four characters from string s.

\n\n

However, if you want to conditionally remove the last four characters, only if they are exactly _bar:

\n\n
var re = /_bar$/;\ns.replace(re, \"\");\n
\n", + "upvotes": 440, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d7082fcc3049e9298f", + "creator": "Tim Down", + "createdAt": 1283207413000, + "text": "slice is better here. s.slice(0, -4)", + "upvotes": 140, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92990", + "creator": "Mahn", + "createdAt": 1344643501000, + "text": "Alternatively: s.slice(0, -"_bar".length) (useful if one doesn't want to hardcode the number of characters)", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92992", + "creator": "Mike Graf", + "createdAt": 1374515004000, + "text": "I like this one because he also gives help for replacing a specified ending.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f328d7082fcc3049e92994", + "creator": "O Genthe", + "createdAt": 1623237730000, + "text": "The second example will only remove the first instance of "_bar" if there are multiple instances in the string", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e3b", + "creator": "Tim Down", + "createdAt": 1283163357000, + "text": "

The easiest method is to use the slice method of the string, which allows negative positions (corresponding to offsets from the end of the string):

\n\n
const s = \"your string\";\nconst withoutLastFourChars = s.slice(0, -4);\n
\n\n

If you needed something more general to remove everything after (and including) the last underscore, you could do the following (so long as s is guaranteed to contain at least one underscore):

\n\n

\r\n
\r\n
const s = \"your_string\";\r\nconst withoutLastChunk = s.slice(0, s.lastIndexOf(\"_\"));\r\nconsole.log(withoutLastChunk);
\r\n
\r\n
\r\n

\n", + "upvotes": 227, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e3e", + "creator": "Somwang Souksavatd", + "createdAt": 1377721903000, + "text": "

Try this:

\n\n

\r\n
\r\n
const myString = \"Hello World!\";\r\nconsole.log(myString.slice(0, -1));
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e3d", + "creator": "sites", + "createdAt": 1356669511000, + "text": "

1. (.*), captures any character multiple times:

\n

\r\n
\r\n
console.log(\"a string\".match(/(.*).$/)[1]);
\r\n
\r\n
\r\n

\n

2. ., matches last character, in this case:

\n

\r\n
\r\n
console.log(\"a string\".match(/(.*).$/));
\r\n
\r\n
\r\n

\n

3. $, matches the end of the string:

\n

\r\n
\r\n
console.log(\"a string\".match(/(.*).{2}$/)[1]);
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d8082fcc3049e92997", + "creator": "QueueHammer", + "createdAt": 1371753899000, + "text": "I like this because in spite of .splice() you found a way to use a regular expression.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f328d8082fcc3049e92999", + "creator": "Sebastian Simon", + "createdAt": 1589406333000, + "text": "Apart from not being Unicode-aware, this also doesn’t match line breaks.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e3c", + "creator": "kamal", + "createdAt": 1356617620000, + "text": "

Try this:

\n\n
<script>\n    var x=\"foo_foo_foo_bar\";\n    for (var i=0; i<=x.length; i++) {\n        if (x[i]==\"_\" && x[i+1]==\"b\") {\n            break;\n        }\n        else {\n            document.write(x[i]);\n        }\n    }\n</script>\n
\n\n

You can also try the live working example on http://jsfiddle.net/informativejavascript/F7WTn/87/.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e3f", + "creator": "Akshay Joshi", + "createdAt": 1393499666000, + "text": "

Using JavaScript's slice function:

\n\n

\r\n
\r\n
let string = 'foo_bar';\r\nstring = string.slice(0, -4); // Slice off last four characters here\r\nconsole.log(string);
\r\n
\r\n
\r\n

\n\n

This could be used to remove '_bar' at end of a string, of any length.

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e40", + "creator": "uofc", + "createdAt": 1401187630000, + "text": "
debris = string.split(\"_\") //explode string into array of strings indexed by \"_\"\n\ndebris.pop(); //pop last element off the array (which you didn't want)\n\nresult = debris.join(\"_\"); //fuse the remainng items together like the sun\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e41", + "creator": "Marc477", + "createdAt": 1438145905000, + "text": "

Use regex:

\n\n

\r\n
\r\n
let aStr = \"12345.00\";\r\naStr = aStr.replace(/.$/, '');\r\nconsole.log(aStr);
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d8082fcc3049e9299e", + "creator": "Sebastian Simon", + "createdAt": 1589406319000, + "text": "Apart from not being Unicode-aware, this also doesn’t match line breaks.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e43", + "creator": "bestafubana", + "createdAt": 1471630658000, + "text": "

In cases where you want to remove something that is close to the end of a string (in case of variable sized strings) you can combine slice() and substr().

\n\n

I had a string with markup, dynamically built, with a list of anchor tags separated by comma. The string was something like:

\n\n
var str = \"<a>text 1,</a><a>text 2,</a><a>text 2.3,</a><a>text abc,</a>\";\n
\n\n

To remove the last comma I did the following:

\n\n
str = str.slice(0, -5) + str.substr(-4);\n
\n", + "upvotes": 378, + "upvoterUsernames": [], + "downvotes": 378, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e42", + "creator": "profile_-1", + "createdAt": 1452883723000, + "text": "

https://stackoverflow.com/questions/34817546/javascript-how-to-delete-last-two-characters-in-a-string

\n
\n

Just use trim if you don't want spaces

\n
\n
"11.01 °C".slice(0,-2).trim()\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e44", + "creator": "Mark Walters", + "createdAt": 1520942588000, + "text": "

Here is an alternative that i don't think i've seen in the other answers, just for fun.

\n\n

\r\n
\r\n
var strArr = \"hello i'm a string\".split(\"\");\r\nstrArr.pop();\r\ndocument.write(strArr.join(\"\"));
\r\n
\r\n
\r\n

\n\n

Not as legible or simple as slice or substring but does allow you to play with the string using some nice array methods, so worth knowing.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e45", + "creator": "Kamil Kiełczewski", + "createdAt": 1589374143000, + "text": "

Performance

\n

Today 2020.05.13 I perform tests of chosen solutions on Chrome v81.0, Safari v13.1 and Firefox v76.0 on MacOs High Sierra v10.13.6.

\n

Conclusions

\n\n

\"enter

\n

Details

\n

I perform two tests for solutions A, B, C, D, E(ext), F, G(my)

\n\n

Solutions are presented in below snippet

\n

\r\n
\r\n
function A(str) {\n  return str.replace(/.$/, '');\n}\n\nfunction B(str) {\n  return str.match(/(.*).$/)[1];\n}\n\nfunction C(str) {\n  return str.substring(0, str.length - 1);\n}\n\nfunction D(str) {\n  return str.slice(0, -1); \n}\n\nfunction E(str) {\n  return str.substr(0, str.length - 1);\n}\n\nfunction F(str) {\n  let s= str.split(\"\");\n  s.pop();\n  return s.join(\"\");\n}\n\nfunction G(str) {\n  let s='';\n  for(let i=0; i<str.length-1; i++) s+=str[i];\n  return s;\n }\n\n\n\n// ---------\n// TEST\n// ---------\n\nlet log = (f)=>console.log(`${f.name}: ${f(\"12345.00\")}`);\n\n[A,B,C,D,E,F,G].map(f=>log(f));
\r\n
This snippet only presents soutions
\r\n
\r\n
\r\n

\n

Here are example results for Chrome for short string

\n

\"enter

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e47", + "creator": "fruitloaf", + "createdAt": 1603943249000, + "text": "

Via slice(indexStart, indexEnd) method - note, this does NOT CHANGE the existing string, it creates a copy and changes the copy.

\n
console.clear();\nlet str = "12345.00";\nlet a = str.slice(0, str.length -1)\nconsole.log(a, "<= a");\nconsole.log(str, "<= str is NOT changed");\n
\n

Via Regular Expression method - note, this does NOT CHANGE the existing string, it creates a copy and changes the copy.

\n
console.clear();\nlet regExp = /.$/g\nlet b = str.replace(regExp,"")\nconsole.log(b, "<= b");\nconsole.log(str, "<= str is NOT changed");\n
\n

Via array.splice() method -> this only works on arrays, and it CHANGES, the existing array (so careful with this one), you'll need to convert a string to an array first, then back.

\n
console.clear();\nlet str = "12345.00";\nlet strToArray = str.split("")\nconsole.log(strToArray, "<= strToArray");\nlet spliceMethod = strToArray.splice(str.length-1, 1)\nstr = strToArray.join("")\nconsole.log(str, "<= str is changed now");\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e46", + "creator": "shreyasm-dev", + "createdAt": 1599611377000, + "text": "

You can, in fact, remove the last arr.length - 2 items of an array using arr.length = 2, which if the array length was 5, would remove the last 3 items.

\n

Sadly, this does not work for strings, but we can use split() to split the string, and then join() to join the string after we've made any modifications.

\n

\r\n
\r\n
var str = 'string'\n\nString.prototype.removeLast = function(n) {\n  var string = this.split('')\n  string.length = string.length - n\n\n  return string.join('')\n}\n\nconsole.log(str.removeLast(3))
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e48", + "creator": "Ali Yaghoubi", + "createdAt": 1634148513000, + "text": "

Try to use toFixed

\n
const str = "12345.00";\nreturn (+str).toFixed(1);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e31", + "creator": "Jon Erickson", + "createdAt": 1244147748000, + "text": "

You can use the substring function:

\n\n

\r\n
\r\n
let str = \"12345.00\";\r\nstr = str.substring(0, str.length - 1);\r\nconsole.log(str);
\r\n
\r\n
\r\n

\n\n

This is the accepted answer, but as per the conversations below, the slice syntax is much clearer:

\n\n

\r\n
\r\n
let str = \"12345.00\";\r\nstr = str.slice(0, -1); \r\nconsole.log(str);
\r\n
\r\n
\r\n

\n", + "upvotes": 6048, + "upvoterUsernames": [], + "downvotes": 2297, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32a0d082fcc3049e92fcf", + "creator": "Matt Ball", + "createdAt": 1271167771000, + "text": "@Kheu - the slice notation is much cleaner to me. I was previously using the substring version. Thanks!", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd0", + "creator": "rorypicko", + "createdAt": 1448887034000, + "text": "@BenR not anymore, in my Chrome 46, slice is now 0.24% faster!", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd2", + "creator": "Black", + "createdAt": 1462364338000, + "text": "Does not work, i get logik.js:156 Uncaught TypeError: str.slice is not a function", + "upvotes": 325, + "upvoterUsernames": [], + "downvotes": 325, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd3", + "creator": "Izzy", + "createdAt": 1470388736000, + "text": "@EdwardBlack Because your str is not a string.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd5", + "creator": "Bernardo Dal Corno", + "createdAt": 1549656920000, + "text": "slife is lice... or something like that", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321cb082fcc3049e90e2f", + "creator": "RSolberg", + "createdAt": 1244148031000, + "text": "Do you care about rounding? 12345.46 = 12345.5 or 12345.4?", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + }, + { + "_id": "62f321cb082fcc3049e90e30", + "creator": "Cᴏʀʏ", + "createdAt": 1283137635000, + "text": "Do you know what the suffix is or do you want to split and remove the last word based on your underscores?", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2181338, + "uvac": 2181362 + } + }, + { + "_id": "62f321bb082fcc3049e8febe", + "title": "How do I make the first letter of a string uppercase in JavaScript?", + "title-lowercase": "how do i make the first letter of a string uppercase in javascript?", + "creator": "Robert Wills", + "createdAt": 1245659131000, + "status": "open", + "text": "

How do I make the first letter of a string uppercase, but not change the case of any of the other letters?

\n

For example:

\n\n", + "upvotes": 6441, + "upvoterUsernames": [], + "downvotes": 1714, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 3252506, + "answers": 119, + "answerItems": [ + { + "_id": "62f321bf082fcc3049e902cc", + "creator": "Steve Harrison", + "createdAt": 1245659438000, + "text": "

The basic solution is:

\n

\r\n
\r\n
function capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\nconsole.log(capitalizeFirstLetter('foo')); // Foo
\r\n
\r\n
\r\n

\n

Some other answers modify String.prototype (this answer used to as well), but I would advise against this now due to maintainability (hard to find out where the function is being added to the prototype and could cause conflicts if other code uses the same name / a browser adds a native function with that same name in future).

\n

...and then, there is so much more to this question when you consider internationalisation, as this astonishingly good answer (buried below) shows.

\n

If you want to work with Unicode code points instead of code units (for example to handle Unicode characters outside of the Basic Multilingual Plane) you can leverage the fact that String#[@iterator] works with code points, and you can use toLocaleUpperCase to get locale-correct uppercasing:

\n

\r\n
\r\n
const capitalizeFirstLetter = ([ first, ...rest ], locale = navigator.language) =>\n  first === undefined ? '' : first.toLocaleUpperCase(locale) + rest.join('')\n\nconsole.log(\n  capitalizeFirstLetter(''), // [empty string]\n  capitalizeFirstLetter('foo'), // Foo\n  capitalizeFirstLetter(\"𐐶𐐲𐑌𐐼𐐲𐑉\"), // \"𐐎𐐲𐑌𐐼𐐲𐑉\" (correct!)\n  capitalizeFirstLetter(\"italya\", 'tr') // İtalya\" (correct in Turkish Latin!)\n)
\r\n
\r\n
\r\n

\n

For even more internationalization options, please see the original answer below.

\n", + "upvotes": 11757, + "upvoterUsernames": [], + "downvotes": 4564, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902cd", + "creator": "Robert Wills", + "createdAt": 1245659613000, + "text": "

Here is a function called ucfirst()(short for "upper case first letter"):

\n
function ucfirst(str) {\n    var firstLetter = str.substr(0, 1);\n    return firstLetter.toUpperCase() + str.substr(1);\n}\n
\n

You can capitalise a string by calling ucfirst("some string") -- for example,

\n
ucfirst("this is a test") --> "This is a test"\n
\n

It works by splitting the string into two pieces. On the first line it pulls out firstLetter and then on the second line it capitalises firstLetter by calling firstLetter.toUpperCase() and joins it with the rest of the string, which is found by calling str.substr(1).

\n

You might think this would fail for an empty string, and indeed in a language like C you would have to cater for this. However in JavaScript, when you take a substring of an empty string, you just get an empty string back.

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ce", + "creator": "Steve Hansell", + "createdAt": 1279641067000, + "text": "

Here's a more object-oriented approach:

\n
Object.defineProperty(String.prototype, 'capitalize', {\n  value: function() {\n    return this.charAt(0).toUpperCase() + this.slice(1);\n  },\n  enumerable: false\n});\n
\n

You'd call the function, like this:

\n
"hello, world!".capitalize();\n
\n

With the expected output being:

\n
"Hello, world!"\n
\n", + "upvotes": 3110, + "upvoterUsernames": [], + "downvotes": 1532, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902cf", + "creator": "raphie", + "createdAt": 1282165652000, + "text": "

The ucfirst function works if you do it like this.

\n\n
function ucfirst(str) {\n    var firstLetter = str.slice(0,1);\n    return firstLetter.toUpperCase() + str.substring(1);\n}\n
\n\n

Thanks J-P for the aclaration.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e9153c", + "creator": "dr.dimitru", + "createdAt": 1448424067000, + "text": "One liner: string[0].toUpperCase() + string.substring(1)", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e9153d", + "creator": "dr.dimitru", + "createdAt": 1462558835000, + "text": "@TarranJones here is bulletproof one liner: (string[0] || '').toUpperCase() + string.substring(1)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e9153e", + "creator": "Przemek", + "createdAt": 1493058245000, + "text": "@dr.dimitru: Instead of idiomatic (string[0] || '') you could just string.charAt(0).", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91540", + "creator": "Peter Mortensen", + "createdAt": 1609976047000, + "text": "What is "aclaration"?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91541", + "creator": "mickmackusa", + "createdAt": 1623885528000, + "text": "Please do not litter this page with commented solutions @dr.d", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d0", + "creator": "Murat Kucukosman", + "createdAt": 1292408157000, + "text": "
String.prototype.capitalize = function(){\n    return this.replace(/(^|\\s)([a-z])/g, \n                        function(m, p1, p2) {\n                            return p1 + p2.toUpperCase();\n                        });\n};\n
\n

Usage:

\n
capitalizedString = someString.capitalize();\n
\n

This is a text string => This Is A Text String

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e91543", + "creator": "tomdemuyt", + "createdAt": 1358200551000, + "text": "+1, this is what I was really looking for. There is a minor bug though, it ought to be return.this.toLocaleLowerCase().replace( ...", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91545", + "creator": "Benubird", + "createdAt": 1365515892000, + "text": "+1, I found this page looking for a javascript version of phps ucfirst, which I suspect is how most people find it.", + "upvotes": 482, + "upvoterUsernames": [], + "downvotes": 482, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91547", + "creator": "Travis Webb", + "createdAt": 1375439090000, + "text": "@DanDascalescu I found this useful, so +1 utilitarianism, and -1 anal-retentiveness. He included an example, so its function is clear.", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d2", + "creator": "Jakub Januszkiewicz", + "createdAt": 1299741991000, + "text": "

If you go with one of the regex answers, remember they will only work with ASCII characters. All your unicode letters will not be uppercased. The XRegExp library and its unicode plugins solve this problem if you want to stick with regexps. So something like this would work:

\n\n
String.prototype.capitalize = function () {\n    return this.replace(XRegExp(\"^\\\\p{L}\"), function ($0) { return $0.toUpperCase(); })\n}\n
\n\n

Considering that it still doesn't cover all possibilities (combined characters, see http://www.regular-expressions.info/unicode.html) it seems easier to just use the .charAt(0).toUpperCase() approach.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902d1", + "creator": "Steve", + "createdAt": 1295370857000, + "text": "

If I may alter the code a little. I found that if I run an all caps string through this function, nothing happens. So... here is my tid bit. Force the string to lower case first.

\n\n
String.prototype.capitalize = function(){\n    return this.toLowerCase().replace( /(^|\\s)([a-z])/g , function(m, p1, p2) {\n        return p1 + p2.toUpperCase();\n    });\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902d6", + "creator": "Dan", + "createdAt": 1322673405000, + "text": "

Capitalize the first letter of all words in a string:

\n\n
function ucFirstAllWords( str )\n{\n    var pieces = str.split(\" \");\n    for ( var i = 0; i < pieces.length; i++ )\n    {\n        var j = pieces[i].charAt(0).toUpperCase();\n        pieces[i] = j + pieces[i].substr(1);\n    }\n    return pieces.join(\" \");\n}\n
\n", + "upvotes": 124, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e9154b", + "creator": "JimmyPena", + "createdAt": 1322680387000, + "text": "Re-read question: I want to capitalize the first character of a string, but not change the case of any of the other letters.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e9154d", + "creator": "Malovich", + "createdAt": 1356038173000, + "text": "I know I did. I'd add one thing, in case the entire string starts capitalized: pieces[i] = j + pieces[i].substr(1).toLowerCase();", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e9154f", + "creator": "Magico", + "createdAt": 1467801361000, + "text": "Would be better to first lowercase the whole string", + "upvotes": 652, + "upvoterUsernames": [], + "downvotes": 652, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d5", + "creator": "Łukasz Kurowski", + "createdAt": 1320698358000, + "text": "

CoffeeScript

\n
ucfirst = (str) -> str.charAt(0).toUpperCase() + str.slice(1)\n
\n

As a String prototype method:

\n
String::capitalize = -> @charAt(0).toUpperCase() + @slice(1)\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e91551", + "creator": "longda", + "createdAt": 1345053291000, + "text": "Stupid question but how would you add this to the String prototype in coffeescript?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d3", + "creator": "monokrome", + "createdAt": 1312822543000, + "text": "

If you are wanting to reformat all-caps text, you might want to modify the other examples as such:

\n\n
function capitalize (text) {\n    return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();\n}\n
\n\n

This will ensure that the following text is changed:

\n\n
TEST => Test\nThis Is A TeST => This is a test\n
\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e91554", + "creator": "monokrome", + "createdAt": 1461560903000, + "text": "Probably worth noting that this will also convert things like acronyms to lowercase, so maybe not the best idea in most cases", + "upvotes": 847, + "upvoterUsernames": [], + "downvotes": 847, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91556", + "creator": "monokrome", + "createdAt": 1472354705000, + "text": "Also,did GAMITG really make an edit just to remove a piece of whitespace from a non-code portion of the post? O_O", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e91557", + "creator": "monokrome", + "createdAt": 1552686823000, + "text": "btw, this will break uppercasing acronyms so be careful y'all <3", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d4", + "creator": "joelvh", + "createdAt": 1314572639000, + "text": "

Here is a shortened version of the popular answer that gets the first letter by treating the string as an array:

\n
function capitalize(s)\n{\n    return s[0].toUpperCase() + s.slice(1);\n}\n
\n

Update

\n

According to the comments below this doesn't work in IE 7 or below.

\n

Update 2:

\n

To avoid undefined for empty strings (see @njzk2's comment below), you can check for an empty string:

\n
function capitalize(s)\n{\n    return s && s[0].toUpperCase() + s.slice(1);\n}\n
\n

ES version

\n
const capitalize = s => s && s[0].toUpperCase() + s.slice(1)\n\n// to always return type string event when s may be falsy other than empty-string\nconst capitalize = s => (s && s[0].toUpperCase() + s.slice(1)) || ""\n
\n", + "upvotes": 618, + "upvoterUsernames": [], + "downvotes": 176, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cb082fcc3049e91558", + "creator": "Martijn Scheffer", + "createdAt": 1635257102000, + "text": "just use charAt() instead of []", + "upvotes": 8857, + "upvoterUsernames": [], + "downvotes": 8857, + "downvoterUsernames": [] + }, + { + "_id": "62f323cb082fcc3049e9155a", + "creator": "Voxlinou", + "createdAt": 1658527219000, + "text": "@MartijnScheffer what do you mean "just use", [] is simpler, easier, clearer and more popular than charAt", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d7", + "creator": "Ryan", + "createdAt": 1323390436000, + "text": "

If you're ok with capitalizing the first letter of every word, and your usecase is in HTML, you can use the following CSS:

\n
<style type="text/css">\n    p.capitalize {text-transform:capitalize;}\n</style>\n<p class="capitalize">This is some text.</p>\n
\n

This is from CSS text-transform Property (at W3Schools).

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e9155d", + "creator": "Adam Hepton", + "createdAt": 1326879160000, + "text": "@Simon It's not stated that the string is necessarily going to be output as part of a HTML document - CSS is only going to be of use if it is.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e9155f", + "creator": "DDK", + "createdAt": 1340613778000, + "text": "@Simon, the text-transform capitalizes first letter of every word and this is what Robert wants", + "upvotes": 533, + "upvoterUsernames": [], + "downvotes": 533, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91561", + "creator": "Simon East", + "createdAt": 1340668942000, + "text": "Incorrect, Dinesh. He said the first character of the string.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91563", + "creator": "SoreThumb", + "createdAt": 1352080798000, + "text": "What's funny is that Ryan didn't help answer @Robert Willis's question. but he answered mine.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91565", + "creator": "Holf", + "createdAt": 1363975318000, + "text": "The question specifies 'JavaScript'. A CSS solution doesn't provide this.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91567", + "creator": "Ali Gangji", + "createdAt": 1365843519000, + "text": "It's now javascript: $('.capitalize').css('text-transform', 'capitalize')", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91569", + "creator": "Nick Manning", + "createdAt": 1380865236000, + "text": "So just wrap the tag around the single world you want capitalized", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e9156a", + "creator": "MKN Web Solutions", + "createdAt": 1384979713000, + "text": "This capitalizes every word's first letter.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e9156c", + "creator": "ivkremer", + "createdAt": 1400190253000, + "text": "This answer is bad since it is not JavaScript.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902d8", + "creator": "Alex", + "createdAt": 1335978157000, + "text": "

Okay, so I am new to JavaScript. I wasn't able to get the above to work for me. So I started putting it together myself. Here's my idea (about the same, different and working syntax):

\n\n
String name = request.getParameter(\"name\");\nname = name.toUpperCase().charAt(0) + name.substring(1);\nout.println(name);\n
\n\n

Here I get the variable from a form (it also works manually):

\n\n
String name = \"i am a Smartypants...\";\nname = name.toUpperCase().charAt(0) + name.substring(1);\nout.println(name);\n
\n\n

Output: \"I am a Smartypants...\";

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902d9", + "creator": "sam6ber", + "createdAt": 1342533978000, + "text": "

In CSS:

\n
p::first-letter {\n    text-transform:capitalize;\n}\n
\n", + "upvotes": 1444, + "upvoterUsernames": [], + "downvotes": 561, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e9156e", + "creator": "DonMB", + "createdAt": 1443116063000, + "text": "$('#mystring_id').text(string).css('text-transform','capital‌ize');", + "upvotes": 170, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91570", + "creator": "Alesh Houdek", + "createdAt": 1638709855000, + "text": "It's not JS, but I bet this is the best answer for 99% of the people reading this. I'm certainly glad I scrolled this far 🙃", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902da", + "creator": "Pnobuts", + "createdAt": 1343895684000, + "text": "
// Uppercase first letter\nfunction ucfirst(field) {\n    field.value = field.value.substr(0, 1).toUpperCase() + field.value.substr(1);\n}\n
\n\n

Usage:

\n\n
<input type=\"text\" onKeyup=\"ucfirst(this)\" />\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902dc", + "creator": "Zaheer Ahmed", + "createdAt": 1352889278000, + "text": "

One possible solution:

\n\n
function ConvertFirstCharacterToUpperCase(text) {\n    return text.substr(0, 1).toUpperCase() + text.substr(1);    \n}\n
\n\n

Use this:

\n\n
 alert(ConvertFirstCharacterToUpperCase(\"this is string\"));\n
\n\n

Here is working JS Fiddle

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902db", + "creator": "longda", + "createdAt": 1345053961000, + "text": "

In CoffeeScript, add to the prototype for a string:

\n\n
String::capitalize = ->\n  @substr(0, 1).toUpperCase() + @substr(1)\n
\n\n

Usage would be:

\n\n
\"woobie\".capitalize()\n
\n\n

Which yields:

\n\n
\"Woobie\"\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e91575", + "creator": "Cobby", + "createdAt": 1399337645000, + "text": "This is a JavaScript question.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91577", + "creator": "longda", + "createdAt": 1399404548000, + "text": "@Cobby - And this is a coffeescript answer.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e91578", + "creator": "TaylorMac", + "createdAt": 1406751511000, + "text": "Coffeescript is a preprocessor language, not a library... A library for this would be silly", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902dd", + "creator": "Simon", + "createdAt": 1353349988000, + "text": "
yourString.replace(/^[a-z]/, function(m){ return m.toUpperCase() });\n
\n\n

(You may encapsulate it in a function or even add it to the String prototype if you use it frequently.)

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e9157a", + "creator": "Simon", + "createdAt": 1485178598000, + "text": "Regexp is overkill for this, prefer the simpler : str.charAt(0).toUpperCase() + str.slice(1)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e9157c", + "creator": "Przemek", + "createdAt": 1506194334000, + "text": "Often times, if you want to solve your problem with regex, you end up with two problems.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902de", + "creator": "yckart", + "createdAt": 1360911326000, + "text": "

We could get the first character with one of my favorite RegExp, looks like a cute smiley: /^./

\n\n
String.prototype.capitalize = function () {\n  return this.replace(/^./, function (match) {\n    return match.toUpperCase();\n  });\n};\n
\n\n

And for all coffee-junkies:

\n\n
String::capitalize = ->\n  @replace /^./, (match) ->\n    match.toUpperCase()\n
\n\n

...and for all guys who think that there's a better way of doing this, without extending native prototypes:

\n\n
var capitalize = function (input) {\n  return input.replace(/^./, function (match) {\n    return match.toUpperCase();\n  });\n};\n
\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e9157d", + "creator": "Big McLargeHuge", + "createdAt": 1373402555000, + "text": "There is a better way of doing this without modifying the String prototype.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323cc082fcc3049e9157f", + "creator": "yckart", + "createdAt": 1373407292000, + "text": "@davidkennedy85 Sure! But this is the simple way, not the best way... ;-)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902df", + "creator": "alejandro", + "createdAt": 1374257871000, + "text": "

For another case I need it to capitalize the first letter and lowercase the rest. The following cases made me change this function:

\n\n
//es5\nfunction capitalize(string) {\n    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();\n}\ncapitalize(\"alfredo\")  // => \"Alfredo\"\ncapitalize(\"Alejandro\")// => \"Alejandro\ncapitalize(\"ALBERTO\")  // => \"Alberto\"\ncapitalize(\"ArMaNdO\")  // => \"Armando\"\n\n// es6 using destructuring \nconst capitalize = ([first,...rest]) => first.toUpperCase() + rest.join('').toLowerCase();\n
\n", + "upvotes": 261, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e0", + "creator": "Berezh", + "createdAt": 1385027300000, + "text": "

The function takes two arguments:

\n

start - the start index;
\nlength - the length of substring to capitalise

\n
String.prototype.subUpper = function () {\n    var result = this.toString();\n    var start = 0;\n    var length = 1;\n    if (arguments.length > 0) {\n        start = arguments[0];\n        if (start < this.length) {\n            if (arguments.length > 1) {\n                length = arguments[1];\n            }\n            if (start + length > this.length) {\n                length = this.length - start;\n            }\n            var startRest = start + length;\n            var prefix = start > 0 ? this.substr(0, start) : String.empty;\n            var sub = this.substr(start, length);\n            var suffix = this.substr(startRest, this.length - startRest);\n            result = prefix + sub.toUpperCase() + suffix;\n        }\n    }\n    return result;\n};\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cc082fcc3049e91580", + "creator": "Craicerjack", + "createdAt": 1434025233000, + "text": "Why have such a huge solution when others are so much more elegant and much simpler?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902e1", + "creator": "Abdennour TOUMI", + "createdAt": 1385755304000, + "text": "
String.prototype.capitalize = function(allWords) {\n   return (allWords) ? // If all words\n      this.split(' ').map(word => word.capitalize()).join(' ') : // Break down the phrase to words and then recursive\n                                                                 // calls until capitalizing all words\n      this.charAt(0).toUpperCase() + this.slice(1); // If allWords is undefined, capitalize only the first word,\n                                                    // meaning the first character of the whole string\n}\n
\n

And then:

\n
 "capitalize just the first word".capitalize(); ==> "Capitalize just the first word"\n "capitalize all words".capitalize(true); ==> "Capitalize All Words"\n
\n

Update November 2016 (ES6), just for fun:

\n
const capitalize = (string = '') => [...string].map(    // Convert to array with each item is a char of\n                                                        // string by using spread operator (...)\n    (char, index) => index ? char : char.toUpperCase()  // Index true means not equal 0, so (!index) is\n                                                        // the first character which is capitalized by\n                                                        // the `toUpperCase()` method\n ).join('')                                             // Return back to string\n
\n

then capitalize("hello") // Hello

\n", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cd082fcc3049e91583", + "creator": "Dan Dascalescu", + "createdAt": 1561182617000, + "text": "The other ES6 answer is simpler: const capitalize = ([first,...rest]) => first.toUpperCase() + rest.join('').toLowerCase();.", + "upvotes": 1189, + "upvoterUsernames": [], + "downvotes": 1189, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902e2", + "creator": "user28864", + "createdAt": 1387479351000, + "text": "

I have been trying to do same (that is; capitalize the first letter in a string while it is being typed) using jQuery. I searched all through the web for the answer but couldn't find it. However I was able to get a work around using the on() function in jQuery like so:

\n\n
$(\"#FirstNameField\").on(\"keydown\",function(e){\n    var str = $(\"#FirstNameField\").val();\n    if(str.substring()===str.substring(0,1)){\n        $(\"#FirstNameField\").val(str.substring(0,1).toUpperCase());\n    } \n});\n
\n\n

This function actually capitalizes the first letter while the data entrant is typing continuously.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e3", + "creator": "Gabriel Hautclocq", + "createdAt": 1391695576000, + "text": "

Here is my attempt to make a universal function that can capitalize only the first letter, or the first letter of each word, including words separated by a dash (like some first names in French).

\n

By default, the function capitalizes only the first letter and leave the rest untouched.

\n

Parameters:

\n\n

 

\n
if( typeof String.prototype.capitalize !== "function" ) {\n    String.prototype.capitalize = function( lc, all ) {\n        if( all ) {\n            return this.split( " " )\n                       .map( currentValue => currentValue.capitalize( lc ), this )\n                       .join( " " )\n                       .split( "-" )\n                       .map( currentValue => currentValue.capitalize( false ), this )\n                       .join( "-" );\n        } else {\n            return lc\n            ? this.charAt( 0 ).toUpperCase() + this.slice( 1 ).toLowerCase()\n            : this.charAt( 0 ).toUpperCase() + this.slice( 1 );\n        }\n    }\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e4", + "creator": "andersh", + "createdAt": 1397559154000, + "text": "

Or you could use Sugar.js capitalize()

\n\n

Example:

\n\n
'hello'.capitalize()           -> 'Hello'\n'hello kitty'.capitalize()     -> 'Hello kitty'\n'hello kitty'.capitalize(true) -> 'Hello Kitty'\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e5", + "creator": "MaxEcho", + "createdAt": 1399353301000, + "text": "
var str = \"test string\";\nstr = str.substring(0,1).toUpperCase() + str.substring(1);\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e6", + "creator": "andersh", + "createdAt": 1402560023000, + "text": "

If you use Underscore.js or Lodash, the underscore.string library provides string extensions, including capitalize:

\n
\n

_.capitalize(string) Converts first letter of the string to\nuppercase.

\n
\n

Example:

\n
_.capitalize("foo bar") == "Foo bar"\n
\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cd082fcc3049e91588", + "creator": "Igor Loskutov", + "createdAt": 1455352408000, + "text": "From version 4*, Lodash also lowercase() every other letter, be careful!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902e7", + "creator": "a8m", + "createdAt": 1405922525000, + "text": "

Here's my version. I think it's easy to understand and elegant too.

\n
var str = "foo bar baz";\n\n// Capitalize\nstr.split(' ')\n    .map(w => w[0].toUpperCase() + w.substr(1).toLowerCase())\n    .join(' ')\n// Returns "Foo Bar Baz"\n\n// Capitalize the first letter\nstr.charAt(0).toUpperCase() + str.slice(1)\n// Returns "Foo bar baz"\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902e9", + "creator": "AMIC MING", + "createdAt": 1411661628000, + "text": "

Use:

\n\n

\r\n
\r\n
var str = \"ruby java\";\r\n\r\nconsole.log(str.charAt(0).toUpperCase() + str.substring(1));
\r\n
\r\n
\r\n

\n\n

It will output \"Ruby java\" to the console.

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cd082fcc3049e9158b", + "creator": "Ahmad Sharif", + "createdAt": 1584545634000, + "text": "One line solution.", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902e8", + "creator": "kaleazy", + "createdAt": 1407407501000, + "text": "

This is what I use religiously:

\n
function capitalizeMe(str, force){\n    str = force ? str.toLowerCase() : str;\n    return str.replace(/(\\b)([a-zA-Z])/g,\n        function(firstLetter){\n            return firstLetter.toUpperCase();\n        });\n}\n\n\nvar firstName = capitalizeMe($firstName.val());\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ea", + "creator": "mfq", + "createdAt": 1419437515000, + "text": "

Use this module of Node.js, the http://stringjs.com/ package, to capitalize your string:

\n\n
var S = require('string');\nS('jon').capitalize().s; //'Jon'\nS('JP').capitalize().s; //'Jp'\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cd082fcc3049e9158f", + "creator": "Craicerjack", + "createdAt": 1434025275000, + "text": "So you need to install node and a package to capitalise a string. Bit of overkill.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902eb", + "creator": "Asad Fida", + "createdAt": 1421907936000, + "text": "
var capitalizeMe = \"string not starting with capital\"\n
\n\n

Capitalize with substr

\n\n
var capitalized = capitalizeMe.substr(0, 1).toUpperCase() + capitalizeMe.substr(1);\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ec", + "creator": "Mr. X", + "createdAt": 1425743875000, + "text": "

I use something along these lines in my development environment, especially when working with APIs like HTTP:

\n\n

Suppose you have an HTTP header in which you'd like to capitalize every initial letter in its name and add the hyphen between its constituent words. You may achieve something like that using this basic and simple routine:

\n\n
'access control allow origin'\n    .replace(/\\b\\w/g, function (match) {\n        return match.toUpperCase();\n    })\n    .split(' ')\n    .join('-');\n\n// Output: 'Access-Control-Allow-Origin'\n
\n\n

It is not maybe the most elegant and attractive function definition out there, but it certainly gets the job done.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ed", + "creator": "Nicolò", + "createdAt": 1425925376000, + "text": "
function capitalize(string) {\n    return string.replace(/^./, Function.call.bind(\"\".toUpperCase));\n}\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e91593", + "creator": "henhen", + "createdAt": 1529953756000, + "text": "this capitalizes the whole string", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323ce082fcc3049e91595", + "creator": "Martin Burch", + "createdAt": 1530204684000, + "text": "@henhen no, the regex character ^ asserts position at start. then . matches a single character", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ee", + "creator": "pizzamonster", + "createdAt": 1428523782000, + "text": "

The currently voted answer is right, but it doesn't trim or check the length of the string before capitalising the first character.

\n\n
String.prototype.ucfirst = function(notrim) {\n    s = notrim ? this : this.replace(/(?:(?:^|\\n)\\s+|\\s+(?:$|\\n))/g,'').replace(/\\s+/g,' ');\n    return s.length > 0 ? s.charAt(0).toUpperCase() + s.slice(1) : s;\n}\n
\n\n

Set the notrim argument to prevent trimming the string first:

\n\n
'pizza'.ucfirst()         => 'Pizza'\n'   pizza'.ucfirst()      => 'Pizza'\n'   pizza'.ucfirst(true)  => '   pizza'\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e91598", + "creator": "Peter Mortensen", + "createdAt": 1560467689000, + "text": "What do you mean by "The currently voted answer"? Do you mean "The currently highest voted answer"?", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902ef", + "creator": "ilter", + "createdAt": 1431086072000, + "text": "

Posting an edit of @salim's answer to include locale letter transformation.

\n\n
var str = \"test string\";\nstr = str.substring(0,1).toLocaleUpperCase() + str.substring(1);\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e9159a", + "creator": "YakovL", + "createdAt": 1514479349000, + "text": "Man, this should be at the first page at very least", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f323ce082fcc3049e9159c", + "creator": "YakovL", + "createdAt": 1514493084000, + "text": "I would go str = str.charAt(0).toLocaleUpperCase() + str.substr(1);, though, to make this shorter", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f0", + "creator": "maudulus", + "createdAt": 1434566062000, + "text": "

If you want to capitalize every first letter in a string, for example hello to the worldbecomes Hello To The World you can use the following (repurposed from Steve Harrison):

\n\n
function capitalizeEveryFirstLetter(string) {\n    var splitStr = string.split(' ')\n    var fullStr = '';\n\n    $.each(splitStr,function(index){\n        var currentSplit = splitStr[index].charAt(0).toUpperCase() + splitStr[index].slice(1);\n        fullStr += currentSplit + \" \"\n    });\n\n    return fullStr;\n}\n
\n\n

Which you can call by using the following:

\n\n
capitalizeFirstLetter(\"hello to the world\");\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902f1", + "creator": "Fredrik A.", + "createdAt": 1436819640000, + "text": "
function capitalize(s) {\n    // returns the first letter capitalized + the string from index 1 and out aka. the rest of the string\n    return s[0].toUpperCase() + s.substr(1);\n}\n\n\n// examples\ncapitalize('this is a test');\n=> 'This is a test'\n\ncapitalize('the Eiffel Tower');\n=> 'The Eiffel Tower'\n\ncapitalize('/index.html');\n=> '/index.html'\n
\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e9159f", + "creator": "Fredrik A.", + "createdAt": 1437657287000, + "text": "Done @Ram. Also included examples.", + "upvotes": 314, + "upvoterUsernames": [], + "downvotes": 314, + "downvoterUsernames": [] + }, + { + "_id": "62f323ce082fcc3049e915a1", + "creator": "Dan Dascalescu", + "createdAt": 1548627210000, + "text": "How is this any better than the 2009 answer?.", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 130, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f2", + "creator": "Raju Bera", + "createdAt": 1440334260000, + "text": "

Check out this solution:

\n
var stringVal = 'master';\nstringVal.replace(/^./, stringVal[0].toUpperCase()); // Returns Master\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e915a2", + "creator": "Alfredo Delgado", + "createdAt": 1444937427000, + "text": "Save some keystrokes ;) stringVal.replace(/^./, stringVal[0].toUpperCase());", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f3", + "creator": "ISFO", + "createdAt": 1445514362000, + "text": "

Like it:

\n\n
function capitalize(string,a) {\n    var tempstr = string.toLowerCase();\n    if (a == false || a == undefined)\n        return tempstr.replace(tempstr[0], tempstr[0].toUpperCase());\n    else {\n        return tempstr.split(\" \").map(function (i) { return i[0].toUpperCase() + i.substring(1) }).join(\" \");\n    }\n}\n\n\ncapitalize('stack overflow yeah!',true)); //Stack Overflow Yeah!\n\ncapitalize('stack stack stack stack overflow yeah!'));//Stack overflow yeah!\n
\n\n

https://jsfiddle.net/dgmLgv7b/

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902f4", + "creator": "Josh Crozier", + "createdAt": 1447471574000, + "text": "

If you're interested in the performance of a few different methods posted:

\n

Here are the fastest methods based on this jsperf test (ordered from fastest to slowest).

\n

As you can see, the first two methods are essentially comparable in terms of performance, whereas altering the String.prototype is by far the slowest in terms of performance.

\n
// 10,889,187 operations/sec\nfunction capitalizeFirstLetter(string) {\n    return string[0].toUpperCase() + string.slice(1);\n}\n\n// 10,875,535 operations/sec\nfunction capitalizeFirstLetter(string) {\n    return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\n// 4,632,536 operations/sec\nfunction capitalizeFirstLetter(string) {\n    return string.replace(/^./, string[0].toUpperCase());\n}\n\n// 1,977,828 operations/sec\nString.prototype.capitalizeFirstLetter = function() {\n    return this.charAt(0).toUpperCase() + this.slice(1);\n}\n
\n

\"enter

\n", + "upvotes": 366, + "upvoterUsernames": [], + "downvotes": 99, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323ce082fcc3049e915a6", + "creator": "Martijn Scheffer", + "createdAt": 1635257208000, + "text": "i wonder why that last method is so slow, do you attach the function to the prototype every iteration ? that would be unfair", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f5", + "creator": "chovy", + "createdAt": 1448784835000, + "text": "

If you're already (or considering) using Lodash, the solution is easy:

\n
_.upperFirst('fred');\n// => 'Fred'\n\n_.upperFirst('FRED');\n// => 'FRED'\n\n_.capitalize('fred') //=> 'Fred'\n
\n

See their documentation: https://lodash.com/docs#capitalize

\n

_.camelCase('Foo Bar'); //=> 'fooBar'

\n

https://lodash.com/docs/4.15.0#camelCase

\n
_.lowerFirst('Fred');\n// => 'fred'\n\n_.lowerFirst('FRED');\n// => 'fRED'\n\n_.snakeCase('Foo Bar');\n// => 'foo_bar'\n
\n

Vanilla JavaScript for first upper case:

\n
function upperCaseFirst(str){\n    return str.charAt(0).toUpperCase() + str.substring(1);\n}\n
\n", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cf082fcc3049e915a8", + "creator": "GGG", + "createdAt": 1449445760000, + "text": "I think the preference should be for vanilla Js as most people will not download an entire framework only to capitalize a string.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f323cf082fcc3049e915aa", + "creator": "Hemerson Varela", + "createdAt": 1624541513000, + "text": "@GGG In all my projects so far I've used lodash", + "upvotes": 194, + "upvoterUsernames": [], + "downvotes": 194, + "downvoterUsernames": [] + }, + { + "_id": "62f323cf082fcc3049e915ac", + "creator": "chovy", + "createdAt": 1624690748000, + "text": "Vanilla js is better than lodash. Nobody uses it anymore.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f6", + "creator": "szanata", + "createdAt": 1450354433000, + "text": "

A one-liner:

\n\n

\r\n
\r\n
'string'.replace(/(^[a-z])/,function (p) { return p.toUpperCase(); } )
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cf082fcc3049e915ae", + "creator": "Harry Svensson", + "createdAt": 1454620091000, + "text": "Oneliners? Sure, extremely inefficient? No, regex is not cheap.", + "upvotes": 1575, + "upvoterUsernames": [], + "downvotes": 1575, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f8", + "creator": "Bekim Bacaj", + "createdAt": 1462245124000, + "text": "

This one will tolerate possible leading whitespaces and will not miss the target of the first letter in a string. Therefore, it might improve already good solutions available on the thread.

\n\n
str = \"   the Eifel Tower\";\nstr.replace(/\\w/, str.match(/\\w/)[0].toUpperCase());\n>> \"   The Eifel Tower\";\n
\n\n

!But, will cause a 'soft' error if executed against a blank string.\nTo avoid this possible error or unnecessary processing of a blank string or a number, a ternary conditional guarding can be used:

\n\n
+str!=+str ?  str.replace(/\\w/, str.match(/\\w/)[0].toUpperCase()) : str;\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cf082fcc3049e915b1", + "creator": "Kaiido", + "createdAt": 1462246106000, + "text": "it's Eiffel with two "f"", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f7", + "creator": "Irfan Syed", + "createdAt": 1459193579000, + "text": "

This does the same action:

\n\n
var newStr = string.slice(0,1).toUpperCase() + string.slice(1);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cf082fcc3049e915b3", + "creator": "Irfan Syed", + "createdAt": 1459350612000, + "text": "Yes @AndrewMyers i made the edit which wouldn't change the case of the other letters", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902f9", + "creator": "TarranJones", + "createdAt": 1462554716000, + "text": "

Firstly, I just wanted to clear up what capitalize means in this context.\n"This String Is Capitalized" Reliable source

\n

You can see from the example provided this is not what the OP is looking for. What it should say is "How do I make the first letter of a string uppercase" (Not capitalize string)

\n
function ucfirst (str) {\n    return typeof str != "undefined" ? (str += '', str[0].toUpperCase() + str.substr(1)) : '';\n}\n
\n

Explained

\n
typeof str != "undefined" // Is str set\n? // true\nstr += '' // Turns the string variable into a string\nstr[0].toUpperCase() // Get the first character and make it upper case\n+ // Add\nstr.substr(1) // String starting from the index 1 (starts at 0)\n: // false\n''; // Returns an empty string\n
\n

This will work with any argument or no argument at all.

\n
undefined         === ""\n""                === ""\n"my string"       === "My string"\nnull              === "Null"\nundefined         === "";\nfalse             === "False"\n0                 === "0"\ntrue              === "True"\n[]                === ""\n[true,0,"",false] === "True,0,,false"\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902fa", + "creator": "Hadnazzar", + "createdAt": 1466362792000, + "text": "

For just capitalizing the first letter and make the rest of the string lower case:

\n\n
function capitalize(str) {\n     var splittedEnter = str.split(\" \");\n     var capitalized;\n     var capitalizedResult;\n     for (var i = 0 ; i < splittedEnter.length ; i++){\n         capitalized = splittedEnter[i].charAt(0).toUpperCase();\n         splittedEnter[i] = capitalized + splittedEnter[i].substr(1).toLowerCase();\n    }\n    return splittedEnter.join(\" \");\n}\n\ncapitalize(\"tHiS wiLL be alL CapiTaLiZED.\");\n
\n\n

The result will be:

\n\n
\n

This Will Be All Capitalized.

\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902fc", + "creator": "zianwar", + "createdAt": 1472128151000, + "text": "
var capitalized = yourstring[0].toUpperCase() + yourstring.substr(1);\n
\n", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902fb", + "creator": "Eduardo Cuomo", + "createdAt": 1468849219000, + "text": "

\r\n
\r\n
function capitalizeEachWord(str) {\r\n    return str.replace(/\\w\\S*/g, function(txt) {\r\n        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();\r\n    });\r\n}\r\n\r\ndocument.write(capitalizeEachWord('foo BAR God bAD'));
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323cf082fcc3049e915b8", + "creator": "Avatar", + "createdAt": 1640162254000, + "text": "Exactly what I was looking for. Thanks a lot. Should be upvoted more!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e902fe", + "creator": "medik", + "createdAt": 1473980669000, + "text": "

If there's Lodash in your project, use upperFirst.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902fd", + "creator": "Timur Usmanov", + "createdAt": 1472542105000, + "text": "

A small improvement - every word in titlecase.

\n
String.prototype.toTitleCase = function(){\n    return this.replace(/\\b(\\w+)/g, function(m,p){ return p[0].toUpperCase() + p.substr(1).toLowerCase() });\n}\n\nvar s = 'heLLo, wOrLD!';\nconsole.log(s.toTitleCase()); // Hello, World!\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e902ff", + "creator": "SalmanAA", + "createdAt": 1479892672000, + "text": "
function cap(input) {\n    return input.replace(/[\\.\\r\\n\\t\\:\\;\\?\\!]\\W*(\\w)/g, function(match, capture) {\n         // For other sentences in the text\n         return match.toUpperCase();\n    }).replace(/^\\W*\\w/, function(match, capture) {\n        // For the first sentence in the text\n        return match.toUpperCase();\n    });;\n}\n\nvar a = "hi, dear user. it is a simple test. see you later!\\r\\nbye";\nconsole.log(cap(a));\n// Output: Hi, dear user. It is a simple test. See you later!\n// Bye\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90300", + "creator": "daronwolff", + "createdAt": 1489083392000, + "text": "

Using prototypes

\n\n
String.prototype.capitalize = function () {\n    return this.charAt(0) + this.slice(1).toLowerCase();\n  }\n
\n\n

or Using functions

\n\n
function capitalize(str) {\nreturn str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90302", + "creator": "Alireza", + "createdAt": 1494749542000, + "text": "

It's always better to handle these kinds of stuff using CSS first, in general, if you can solve something using CSS, go for that first, then try JavaScript to solve your problems, so in this case try using :first-letter in CSS and apply text-transform:capitalize;

\n

So try creating a class for that, so you can use it globally, for example: .first-letter-uppercase and add something like below in your CSS:

\n
.first-letter-uppercase:first-letter {\n    text-transform:capitalize;\n}\n
\n

Also the alternative option is JavaScript, so the best gonna be something like this:

\n
function capitalizeTxt(txt) {\n  return txt.charAt(0).toUpperCase() + txt.slice(1); //or if you want lowercase the rest txt.slice(1).toLowerCase();\n}\n
\n

and call it like:

\n
capitalizeTxt('this is a test'); // return 'This is a test'\ncapitalizeTxt('the Eiffel Tower'); // return 'The Eiffel Tower'\ncapitalizeTxt('/index.html');  // return '/index.html'\ncapitalizeTxt('alireza');  // return 'Alireza'\ncapitalizeTxt('dezfoolian');  // return 'Dezfoolian'\n
\n

If you want to reuse it over and over, it's better attach it to javascript native String, so something like below:

\n
String.prototype.capitalizeTxt = String.prototype.capitalizeTxt || function() {\n    return this.charAt(0).toUpperCase() + this.slice(1);\n}\n
\n

and call it as below:

\n
'this is a test'.capitalizeTxt(); // return 'This is a test'\n'the Eiffel Tower'.capitalizeTxt(); // return 'The Eiffel Tower'\n'/index.html'.capitalizeTxt();  // return '/index.html'\n'alireza'.capitalizeTxt();  // return 'Alireza'\n
\n", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90303", + "creator": "BILAL AHMAD", + "createdAt": 1504905472000, + "text": "

Easy peasy:

\n

// OK, agreed so here is the edited version. I can't go simple beyond this.

\n

\r\n
\r\n
function FirstUpperCase(inputString){\n  return inputString.replace(inputString[0],inputString[0].toUpperCase());\n};
\r\n
\r\n
\r\n

\n

Input: hello student\nOutput: Hello student

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90304", + "creator": "BILAL AHMAD", + "createdAt": 1505339454000, + "text": "

This solution might be new and probably the simplest.

\n

\r\n
\r\n
function firstUpperCase(input)\n{\n    return input[0].toUpperCase() + input.substr(1);\n}\n\nconsole.log(firstUpperCase(\"capitalize first letter\"));
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90301", + "creator": "Przemek", + "createdAt": 1491412896000, + "text": "

CSS only

\n

If the transformation is needed only for displaying on a web page:

\n
p::first-letter {\n  text-transform: uppercase;\n}\n
\n\n

ES2015 one-liner

\n
const capitalizeFirstChar = str => str.charAt(0).toUpperCase() + str.substring(1);\n
\n

Remarks

\n\n

Benchmark between substring() and slice()

\n

The difference is rather minuscule nowadays (run the test yourself):

\n\n

\"Solutions'

\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90305", + "creator": "user8903269", + "createdAt": 1513435633000, + "text": "

Try this code:

\n
alert("hello".substr(0, 1).toUpperCase() + "hello".substr(1));\n
\n

It is taking the first character in "hello", capitalizing it and adding the rest of it on.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90307", + "creator": "puiu", + "createdAt": 1516644588000, + "text": "

Just because you can, doesn't mean you should, however. It requires ECMAScript 6 as the code uses array destructuring.

\n
const capitalizeFirstLetter = s => {\n  const type = typeof s;\n  if (type !== "string") {\n    throw new Error(`Expected string, instead received ${type}`);\n  }\n\n  const [firstChar, ...remainingChars] = s;\n\n  return [firstChar.toUpperCase(), ...remainingChars].join("");\n};\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90306", + "creator": "Abhishek Maurya", + "createdAt": 1514270387000, + "text": "

Another way using RamdaJs, the functional programming way:

\n
firstCapital(str){\n    const fn = p => R.toUpper(R.head(p)) + R.tail(p);\n    return fn(str);\n}\n
\n

With multiple words in a string:

\n
firstCapitalAllWords(str){\n    const fn = p => R.toUpper(R.head(p)) + R.tail(p);\n    return R.map(fn,R.split(' ', str)).join(' ');\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90308", + "creator": "Qwerty", + "createdAt": 1516645630000, + "text": "

You can do it in one line like this

\n\n
string[0].toUpperCase() + string.substring(1)\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d0082fcc3049e915c2", + "creator": "Dan Dascalescu", + "createdAt": 1548627088000, + "text": "This answer was already given in 2009.", + "upvotes": 1464, + "upvoterUsernames": [], + "downvotes": 1464, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90309", + "creator": "Aditya Joshi", + "createdAt": 1517340074000, + "text": "

One liner (\"inputString can be set to any string\"):

\n\n
inputString.replace(/.{1}/, inputString.charAt(0).toUpperCase())\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d1082fcc3049e915c4", + "creator": "Aditya Joshi", + "createdAt": 1517350916000, + "text": "@noahnu there was typo and there are many way to solve the same thing", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9030a", + "creator": "Rambabu Bommisetti", + "createdAt": 1518609801000, + "text": "
var a = \"this is a test\"\nconsole.log(a.replace(/^[a-z]/g, txt => txt.toUpperCase()));\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9030c", + "creator": "Shivam Gupta", + "createdAt": 1525108357000, + "text": "

Here is the nice and cleaner version;

\n\n
var str = '';\nreturn str.replace(new RegExp('^'+str[0]+''), str[0].toUpperCase());\n
\n\n

Results:

\n\n

this is a test --> This is a test

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9030b", + "creator": "Sterling Bourne", + "createdAt": 1521473544000, + "text": "

This is the 2018 ECMAScript 6+ Solution:

\n\n

\r\n
\r\n
const str = 'the Eiffel Tower';\r\nconst newStr = `${str[0].toUpperCase()}${str.slice(1)}`;\r\nconsole.log('Original String:', str); // the Eiffel Tower\r\nconsole.log('New String:', newStr); // The Eiffel Tower
\r\n
\r\n
\r\n

\n", + "upvotes": 171, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9030e", + "creator": "Ilyas karim", + "createdAt": 1530445988000, + "text": "

You can do something like this:

\n\n
mode =  \"string\";\nstring = mode.charAt(0).toUpperCase() + mode.substr(1,mode.length).toLowerCase();\nconsole.log(string);\n
\n\n

This will print

\n\n

String

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9030d", + "creator": "victorhazbun", + "createdAt": 1528082908000, + "text": "

This one is simple

\n\n
const upper = lower.replace(/^\\w/, c => c.toUpperCase());\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9030f", + "creator": "Wolf", + "createdAt": 1531597237000, + "text": "
yourString.replace(/\\w/, c => c.toUpperCase())\n
\n

I found this arrow function easiest. Replace matches the first letter character (\\w) of your string and converts it to uppercase. Nothing fancier is necessary.

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90310", + "creator": "bajran", + "createdAt": 1531637574000, + "text": "

To make the first letter of a string capital

\n

First solution

\n

"this is a test" → "This is a test"

\n
var word = "this is a test"\nword[0].toUpperCase();\n
\n

It will give: "This is a test"

\n

Second solution to make first word of string capital

\n

"this is a test" → "This Is A Test"

\n
function capitalize(str) {\n\n    const word = [];\n\n    for(let char of str.split(' ')){\n        word.push(char[0].toUpperCase() + char.slice(1))\n    }\n\n    return word.join(' ');\n\n}\n\n capitalize("this is a test");\n
\n

It will give: "This Is A Test"

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d1082fcc3049e915cc", + "creator": "Melih Yıldız'", + "createdAt": 1536581109000, + "text": "In JS, strings are immutable.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90311", + "creator": "Thielicious", + "createdAt": 1533660841000, + "text": "

a.slice(0,1).toUpperCase()+a.slice(1)

\n\n

\r\n
\r\n
let a = 'hello',\r\n    fix = a.slice(0,1).toUpperCase()+a.slice(1)\r\n    \r\nconsole.log(fix)
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90312", + "creator": "Benny Powers", + "createdAt": 1533908571000, + "text": "

A functional approach

\n\n
const capitalize = ([s, ...tring]) =>\n  [s.toUpperCase(), ...tring]\n    .join('');\n
\n\n

Then you could

\n\n
const titleCase = str => \n  str\n    .split(' ')\n    .map(capitalize)\n    .join(' ')\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d1082fcc3049e915d0", + "creator": "Aaron Tribou", + "createdAt": 1536059955000, + "text": "Don't forget toLowerCase() the remainder of the word. Passing a word in all caps to this current solution would keep it in all caps.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90314", + "creator": "Little Roys", + "createdAt": 1541665886000, + "text": "

There is a very simple way to implement it by replace. For ECMAScript 6:

\n\n
'foo'.replace(/^./, str => str.toUpperCase())\n
\n\n

Result:

\n\n
'Foo'\n
\n", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d2082fcc3049e915d3", + "creator": "Wade Hatler", + "createdAt": 1547165684000, + "text": "Best answer by far, and extra points for showing the regex lambda syntax. I especially like this one as it can be a fluent cut-and-paste anywhere.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f323d2082fcc3049e915d5", + "creator": "codemonkey", + "createdAt": 1596341967000, + "text": "Very clever indeed!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f323d2082fcc3049e915d7", + "creator": "msdos", + "createdAt": 1631402405000, + "text": "@CodeManiac there are so many languages and letters except [a-z]", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90313", + "creator": "Kamil Kiełczewski", + "createdAt": 1537338093000, + "text": "

SHORTEST 3 solutions, 1 and 2 handle cases when s string is \"\", null and undefined:

\n\n
 s&&s[0].toUpperCase()+s.slice(1)        // 32 char\n\n s&&s.replace(/./,s[0].toUpperCase())    // 36 char - using regexp\n\n'foo'.replace(/./,x=>x.toUpperCase())    // 31 char - direct on string, ES6\n
\n\n

\r\n
\r\n
let s='foo bar';\r\n\r\nconsole.log( s&&s[0].toUpperCase()+s.slice(1) );\r\n\r\nconsole.log( s&&s.replace(/./,s[0].toUpperCase()) );\r\n\r\nconsole.log( 'foo bar'.replace(/./,x=>x.toUpperCase()) );
\r\n
\r\n
\r\n

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90316", + "creator": "Mradul Pandey", + "createdAt": 1547480334000, + "text": "

There are multiple ways of doing this try some below

\n\n
var lower = 'the Eiffel Tower';\nvar upper = lower.charAt(0).toUpperCase() + lower.substr(1);\n
\n\n

And if you are comfortable with regular expressions, you do things this way:

\n\n
var upper = lower.replace(/^\\w/, function (chr) {\n  return chr.toUpperCase();\n});\n
\n\n

And you can even take it one step further by using more modern syntax:

\n\n
const upper = lower.replace(/^\\w/, c => c.toUpperCase());\n
\n\n

Also this will take care of negative scenarios as mentioned in example like words starting with special characters like !@#$%^&*()}{{[];':\",.<>/? .

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90315", + "creator": "Akitha_MJ", + "createdAt": 1545532532000, + "text": "

For TypeScript

\n\n
  capitalizeFirstLetter(string) {\n    return string.charAt(0).toUpperCase() + string.slice(1);\n  }\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90318", + "creator": "Jackkobec", + "createdAt": 1550912521000, + "text": "

The simplest solution is:

\n\n
let yourSentence = 'it needs first letter upper case';\n\nyourSentence.charAt(0).toUpperCase() + yourSentence.substr(1);\n
\n\n

or:

\n\n
yourSentence.charAt(0).toUpperCase() + yourSentence.slice(1);\n
\n\n

or:

\n\n
yourSentence.substr(0, 1).toUpperCase() + yourSentence.substr(1);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90317", + "creator": "alejoko", + "createdAt": 1550139653000, + "text": "

I prefer use a solution oriented to a functional way (mapping array for example):

\n\n
Array.from(str).map((letter, i) => i === 0 ? letter.toUpperCase() : letter ).join('');\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90319", + "creator": "Jaydeep Galani", + "createdAt": 1553572520000, + "text": "

Well, all the answers will crash if the method is passed with some unexpected type of data such as Object or function.

\n\n

So to ensure that it will not crash in any conditions we'll need to check for types.

\n\n

\r\n
\r\n
capitalizeFirstLetter = string => {\r\n  if (typeof string == \"string\") {\r\n      console.log(\"passed\");\r\n    return string.charAt(0).toUpperCase() + string.slice(1);\r\n  } else {\r\n    console.log(\"error\");\r\n    return string;\r\n  }\r\n};\r\n\r\n//type function\r\nconsole.log(\r\n  capitalizeFirstLetter(() => {\r\n    return true;\r\n  })\r\n);\r\n// error\r\n//  () => { return true; }\r\n\r\n//type object\r\nconsole.log(capitalizeFirstLetter({ x: 1 }));\r\n// error\r\n// Object { x: 1 }\r\n\r\n//type boolean\r\nconsole.log(capitalizeFirstLetter(true));\r\n// error\r\n// true\r\n\r\n//type undefined\r\nconsole.log(capitalizeFirstLetter(undefined));\r\n// error\r\n// undefined\r\n\r\n//type null\r\nconsole.log(capitalizeFirstLetter(null));\r\n// error\r\n// null\r\n\r\n//type NaN\r\nconsole.log(capitalizeFirstLetter(NaN));\r\n// error\r\n// NaN\r\n\r\n//type number\r\nconsole.log(capitalizeFirstLetter(2));\r\n// error\r\n// 2\r\n\r\n//type any for e.g. class\r\nclass Jaydeep {}\r\nconsole.log(capitalizeFirstLetter(new Jaydeep()));\r\n// error\r\n// Object {}\r\n\r\n//type string\r\nconsole.log(capitalizeFirstLetter(\"1\"));\r\nconsole.log(capitalizeFirstLetter(\"a\"));\r\nconsole.log(capitalizeFirstLetter(\"@\"));\r\nconsole.log(capitalizeFirstLetter(\"\"));\r\n// 1\r\n// A\r\n// @\r\n//  :empty string
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9031a", + "creator": "justcant", + "createdAt": 1556651782000, + "text": "
string = string.replace(string.charAt(0), string.charAt(0).toUpperCase());\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d2082fcc3049e915de", + "creator": "Fappaz", + "createdAt": 1559476028000, + "text": "This does the trick, why was it downvoted with no explanation at all?", + "upvotes": 173, + "upvoterUsernames": [], + "downvotes": 173, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9031b", + "creator": "Mohamed Ben Hartouz", + "createdAt": 1563465590000, + "text": "

The method will take a value and then split it to have an array of string.

\n\n
const firstLetterToUpperCase = value => {\n return value.replace(\n    value.split(\"\")[\"0\"], // Split stirng and get the first letter \n    value\n        .split(\"\")\n        [\"0\"].toString()\n        .toUpperCase() // Split string and get the first letter to replace it with an uppercase value\n  );\n};\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d2082fcc3049e915e1", + "creator": "Tyson Phalp", + "createdAt": 1563470068000, + "text": "Could you add details about what the above code does? That would be really useful, and would increase the quality of your answer.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9031c", + "creator": "Sapphire_Brick", + "createdAt": 1568158646000, + "text": "
/*\n * As terse as possible, assuming you're using ES version 6+\n */\nvar upLetter1=s=>s.replace(/./,m=>m.toUpperCase());\n\nconsole.log(upLetter1(\"the quick brown fox jumped over the lazy dog.\"));\n//\\\\ The quick brown fox jumped over the lazy dog. //\\\\\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9031d", + "creator": "Rúnar Berg", + "createdAt": 1572397965000, + "text": "

Unicode and Locale Aware

\n\n

Using current language features:

\n\n

\r\n
\r\n
function capitalize([firstLetter, ...rest]) {\r\n  return [firstLetter.toLocaleUpperCase(), ...rest].join('');\r\n}\r\n\r\nconsole.log(capitalize('foo bar'));\r\nconsole.log(capitalize('ѷҥӕ'))\r\nconsole.log(capitalize('🎁❄💊🎸⭐'));\r\n\r\n// Title Case\r\nconsole.log(\r\n  'Title Case:',\r\n  'foo bar'\r\n    .split(/\\s+/)\r\n    .map(capitalize)\r\n    .join(' '),\r\n);
\r\n
\r\n
\r\n

\n\n

We accept a destructured string as the only parameter [firstLetter, ...rest], assigning the first character to the variable firstLetter and get an array for the rest of the characters (...rest) bound to the rest variable. E.g. for the string lorem ipsum this should look like:

\n\n
capitalize('lorem ipsum');\n// firstLetter = 'l'\n// rest = ['o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm'];\n
\n\n

Now all we need to do is prepend an uppercased version of the first letter firstLetter.toLocaleUpperCase() to the rest array—using the spread operator—and join the resulting array into a string using .join('')

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d3082fcc3049e915e5", + "creator": "Amit Jamwal", + "createdAt": 1574246064000, + "text": "can you explain your function capitalize.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9031f", + "creator": "chickens", + "createdAt": 1584018720000, + "text": "

Capitalize First Word: Shortest

\n\n
text.replace(/(^.)/, m => m.toUpperCase())\n
\n\n
\n\n

Capitalize Each Word: Shortest

\n\n
text.replace(/(^\\w|\\s\\w)/g, m => m.toUpperCase());\n
\n\n

If you want to make sure the rest is in lowercase:

\n\n
text.replace(/(^\\w|\\s\\w)(\\S*)/g, (_,m1,m2) => m1.toUpperCase()+m2.toLowerCase())\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9031e", + "creator": "Tim", + "createdAt": 1579500728000, + "text": "

Using the JS replace string method & a regular expression w/ a word boundary seems simple.

\n\n

Capitalize the first words' first character: \"the eiffel tower\" --> \"The eiffel tower\"

\n\n
str.replace(/\\b\\w/, v => v.toUpperCase())\n
\n\n

Capitalize all words' first character: \"the eiffel tower\" --> \"The Eiffel Tower\"

\n\n
str.replace(/\\b\\w/g, v => v.toUpperCase())\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90320", + "creator": "Chukwu3meka", + "createdAt": 1584577080000, + "text": "

1. We'll be using CSS to achieve this. It can also be set from an external CSS.

\n
<span text-transform="capitalize ">The first letter of each word becomes an upper case</span>\n
\n

2. Using vanilla JavaScript, we could do:

\n
let string = "test case"\n\nstring = string[0].toUpperCase() + string.substring(1)\n//return "Test case"\n
\n

Explanation</b/>:

\n

string[0].toUpperCase(): converts the first letter in the string to upper case

\n

string.substring(1): deletes the first letter in the string and returns the remaining characters

\n

text-transform="capitalize": make the first letter of each word in this tag upper case. If you use 'uppercase' as the value of text-transform, every letter in the tag will be a capital letter

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90321", + "creator": "Omkesh Sajjanwar", + "createdAt": 1584595979000, + "text": "

Any type of string can convert --

\n

YoUrStRiNg → Yourstring

\n
var str = yOuRsTrING.toLowerCase(); // Output: yourstring\nstr.charAt(0).toUpperCase() + str.slice(1); // Output: Y + ourstring = Yourstring\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90322", + "creator": "Christian Matthew", + "createdAt": 1586239840000, + "text": "

Only because this is really a one-liner I will include this answer. It's an ES6-based interpolated string one-liner.

\n
let setStringName = 'the Eiffel Tower';\nsetStringName = `${setStringName[0].toUpperCase()}${setStringName.substring(1)}`;\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90324", + "creator": "Abdulmoiz Ahmer", + "createdAt": 1593003506000, + "text": "

I would just use a regular expression:

\n
myString = '    the quick green alligator...';\nmyString.trim().replace(/^\\w/, (c) => c.toUpperCase());\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90323", + "creator": "Omar bakhsh", + "createdAt": 1591611144000, + "text": "

The first character of every string is capitalized.

\n

\r\n
\r\n
function capitalize(word){\n    return word[0].toUpperCase() + word.slice(1).toLowerCase();\n}\n\nconsole.log(capitalize(\"john\")); //John\nconsole.log(capitalize(\"BRAVO\")); //Bravo\nconsole.log(capitalize(\"BLAne\")); //Blane
\r\n
\r\n
\r\n

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d3082fcc3049e915ec", + "creator": "Omar bakhsh", + "createdAt": 1595121051000, + "text": "first char of word to upper case = word[0].toUpperCase() skipe first char and lower rest = slice(1).toLowerCase();", + "upvotes": 465, + "upvoterUsernames": [], + "downvotes": 465, + "downvoterUsernames": [] + }, + { + "_id": "62f323d3082fcc3049e915ed", + "creator": "Omar bakhsh", + "createdAt": 1598141869000, + "text": "you can ignore the other extra step of code : +word.slice(1).toLowerCase();", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90326", + "creator": "whereisanddy", + "createdAt": 1599069793000, + "text": "

Try with the following function:

\n
function capitalize (string) {\n  return [].map.call(string, (char, i) => i ? char : char.toUpperCase()).join('')\n}\n
\n

Usage:

\n
capitalize('hello, world!')\n
\n

Result:

\n
Hello, world!\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90325", + "creator": "Ruslan Korkin", + "createdAt": 1595329278000, + "text": "

If you need to have all words starting with a capital letter, you can use the following function:

\n
const capitalLetters = (s) => {\n    return s.trim().split(" ").map(i => i[0].toUpperCase() + i.substr(1)).reduce((ac, i) => `${ac} ${i}`);\n}\n
\n

Example:

\n
console.log(`result: ${capitalLetters("this is a test")}`)\n// Result: "This Is A Test"\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d3082fcc3049e915ef", + "creator": "Sean", + "createdAt": 1651246734000, + "text": "This is not what the question asks.", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90327", + "creator": "Deen John", + "createdAt": 1601302368000, + "text": "

There are already so many good answers, but you can also use a simple CSS transform:

\n
text-transform: capitalize;\n
\n

\r\n
\r\n
div.c {\n  text-transform: capitalize;\n}
\r\n
<h2>text-transform: capitalize:</h2>\n<div class=\"c\">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d4082fcc3049e915f1", + "creator": "stackoverflow", + "createdAt": 1605373059000, + "text": "this will uppercase all words in a string", + "upvotes": 1093, + "upvoterUsernames": [], + "downvotes": 1093, + "downvoterUsernames": [] + }, + { + "_id": "62f323d4082fcc3049e915f3", + "creator": "Deen John", + "createdAt": 1609986916000, + "text": "i specifically mentioned that besides javascript you can use css too...not everyone is probably looking for an interview question answer.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f323d4082fcc3049e915f5", + "creator": "Sean", + "createdAt": 1651246714000, + "text": "This duplicates other prior answers.", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e90328", + "creator": "Tahseen Alaa", + "createdAt": 1603481186000, + "text": "

You can use a regular expression as below:

\n
return string1.toLowerCase().replace(/^[a-zA-z]|\\s(.)/ig, L => L.toUpperCase());\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9032b", + "creator": "giovaniZanetti", + "createdAt": 1605718154000, + "text": "

Using an arrow function:

\n
const capitalize = string => string[0].toUpperCase() + string.slice(1)\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9032a", + "creator": "DiaMaBo", + "createdAt": 1605071710000, + "text": "

Just install and load Lodash:

\n
import { capitalize } from "lodash";\n\ncapitalize('test') // Test\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e90329", + "creator": "Dory Daniel", + "createdAt": 1604409406000, + "text": "

You can do str.replace(str[0], str[0].toUpperCase()).

\n

Check this example:

\n

\r\n
\r\n
let str = \"hello, WORLD!\"\nlet newStr = str.replace(str[0], str[0].toUpperCase())\n\nconsole.log(\"str: \", str)\nconsole.log(\"newStr: \", newStr)
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f323d4082fcc3049e915f9", + "creator": "Dory Daniel", + "createdAt": 1610058608000, + "text": "@PeterMortensen it's working perfectly even before you edited it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bf082fcc3049e9032c", + "creator": "Satheez", + "createdAt": 1606995336000, + "text": "

Capitalizing the first letter with validation

\n
function capitalizeFirstLetter(str) {\n    return (str && typeof str === 'string') ? (str.charAt(0).toUpperCase() + str.slice(1)) : "";\n}\n
\n

Testing

\n
console.log(capitalizeFirstLetter(0)); // Output: ""\nconsole.log(capitalizeFirstLetter(null)); // Output: ""\nconsole.log(capitalizeFirstLetter("test")); // Output: "Test"\nconsole.log(capitalizeFirstLetter({})); // Output: ""\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bf082fcc3049e9032d", + "creator": "sun sreng", + "createdAt": 1608621238000, + "text": "

Here is the function I use:

\n
capitalCase(text: string = 'NA') {\n    return text\n      .trim()\n      .toLowerCase()\n      .replace(/\\w\\S*/g, (w) => w.replace(/^\\w/, (c) => c.toUpperCase()));\n  }\n\nconsole.log('this cApitalize TEXt');\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92df3", + "creator": "Samuel Kiroko N", + "createdAt": 1615207699000, + "text": "
const capitalizeName = function (name) { \n    const names = name.split(' '); \n    const namesUpper = [];\n    for (const n of names) {  \n        namesUpper.push(n.replace(n[0], n[0].toUpperCase()));\n    } \n    console.log(namesUpper.join(' '));\n }; \ncapitalizeName('the Eiffel Tower')\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92df4", + "creator": "Manuel del Pozo", + "createdAt": 1618229816000, + "text": "

Elegant

\n
const capitalize = ([firstChar, ...rest]) => `${firstChar.toUpperCase()}${rest.join('')}`;\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92f86", + "creator": "Thorvald", + "createdAt": 1622464589000, + "text": "Doesn't look very elegant to me, not very readable. Typical example of overcomplicating something simple.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92df5", + "creator": "Teymur", + "createdAt": 1618460987000, + "text": "

You should do like that:

\n
let text = "lower case";\ntext = text.charAt(0).toUpperCase() + text.substring(1, text.length);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92df6", + "creator": "Faisal Nadeem", + "createdAt": 1624618565000, + "text": "

I know this is an old question with a lot of answers but here's my quick snippet.

\n
const capitalize = (str) => str?.split('').map( (e, i) => i === 0 ? e.toUpperCase() : e ).join('')\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92f89", + "creator": "Sai Ranjit", + "createdAt": 1625659643000, + "text": "this is a better way as boundary conditions are also handled", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92df8", + "creator": "Sreehari Ballampalli", + "createdAt": 1628693729000, + "text": "

I tried with different approach

\n
function myFun(val) {\n var combain='';\n  for (let i = 0; i < val.length; i++) {\n     combain  +=  val[i].charAt(0).toUpperCase() + val[i].substring(1, val[i].length)+'-';\n  }\n  return  combain.replaceAll('-',' ');\n}\nvar str = 'sreehari_bsn_alli'.replaceAll('_', ' ');\nstr = str.split(' ');\n\nlet op = myFun(str);\n
\n

console.log(op);

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92df7", + "creator": "German", + "createdAt": 1626697088000, + "text": "

When we say capital it means first letter in each word should be in uppercase and the succeeding character is in lowercase.

\n

There are two functions below the first function will make first letter of a string into uppercase the succeeding is in lowercase. The second function will make a string into title case which means each first letter in every word will be in capital

\n

\r\n
\r\n
// Will make will first letter of a sentence or word uppercase\n\nfunction capital(word){\n  word = word.toLowerCase()\n  return word[0].toUpperCase() + word.substring(1);\n}\n\n\n// Will make first letter in each words capital\n\nfunction titleCase(title) {\n  title = title.toLowerCase();\n  const words = title.split(' ');\n  const titleCaseWords = words.map((word) => word[0].toUpperCase() + word.substring(1));\n  return titleCaseWords.join(' ');\n}\n\nconst title = titleCase('the QUICK brown fox') \nconst caps = capital('the QUICK brown fox') \n\nconsole.log(title); // The Quick Brown Fox\nconsole.log(caps); // The quick brown fox
\r\n
\r\n
\r\n

\n", + "upvotes": 405, + "upvoterUsernames": [], + "downvotes": 405, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92f8f", + "creator": "German", + "createdAt": 1626746483000, + "text": "@EricAya yeah, you right, so I edit my answer and add a function that will turn first letter of a string into uppercase.", + "upvotes": 1276, + "upvoterUsernames": [], + "downvotes": 1276, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92df9", + "creator": "Devendra Kumbhkar", + "createdAt": 1630662699000, + "text": "

This code will also handle extra spaces at the start & end of the string.

\n

\r\n
\r\n
let val = '  this is test ';\nval = val.trim();\nval = val.charAt(0).toUpperCase() + val.slice(1);\nconsole.log(\"Value => \", val);
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92dfa", + "creator": "Ahmad Moghazi", + "createdAt": 1631892157000, + "text": "

with arrow function

\n
let fLCapital = s => s.replace(/./, c => c.toUpperCase())\nfLCapital('this is a test') // "This is a test"\n
\n

with arrow function, another solution

\n
let fLCapital = s => s = s.charAt(0).toUpperCase() + s.slice(1);\nfLCapital('this is a test') // "This is a test"\n
\n

with array and map()

\n
let namesCapital = names => names.map(name => name.replace(/./, c => c.toUpperCase()))\nnamesCapital(['james', 'robert', 'mary']) // ["James", "Robert", "Mary"]\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92dfb", + "creator": "H.Mustafa", + "createdAt": 1631893590000, + "text": "

You can use regex approach :

\n
str.replace(/(^|\\s)\\S/g, letter => letter.toUpperCase());\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92dfd", + "creator": "Konstantin XFlash Stratigenas", + "createdAt": 1644269731000, + "text": "

Capitalize and Uncapitalize first Char of a String.

\n

Functions to include:

\n
/** First Character uppercase */\nfunction capitalize(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/** First Character lowercase */\nfunction uncapitalize(str) {\n    return str.charAt(0).toLowerCase() + str.slice(1);\n}\n
\n

Example1 "First Character uppercase":

\n
alert(capitalize("hello world"));\n
\n

Result: Hello world

\n

Example 2 "First Character lowercase":

\n
alert(uncapitalize("Hello World, today is sunny"));\n
\n

Result: hello World, today is sunny

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92dfc", + "creator": "Cava", + "createdAt": 1642431174000, + "text": "

Capitalize the first letter of all words in a string:

\n
function capitalize(str) {\n  return str.split(' ').map(word => word.charAt(0).toUpperCase() + word.toLowerCase().slice(1)).join(' ');\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92fb6", + "creator": "Sean", + "createdAt": 1651246331000, + "text": "The question is not about capitalizing all words in a string.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32a0b082fcc3049e92fb7", + "creator": "Saad Zahoor", + "createdAt": 1656302709000, + "text": "Thanks you. This is what I was looking for. This code capitalizing the first letter of all words.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92dfe", + "creator": "Mohcine BAADI", + "createdAt": 1645570875000, + "text": "

I needed a similar feature in a project lately, and this is how I implemented it:

\n

\r\n
\r\n
function capitlizeFirst(str) {\n  // checks for null, undefined and empty string\n  if (!str) return;\n  return str.match(\"^[a-z]\") ? str.charAt(0).toUpperCase() + str.substring(1) : str;\n}\n\nconsole.log(capitlizeFirst(\"\"));\nconsole.log(capitlizeFirst(null));\nconsole.log(capitlizeFirst(undefined));\nconsole.log(capitlizeFirst(\"hello world\"));\nconsole.log(capitlizeFirst(\"/index.html\"));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92dff", + "creator": "Amir", + "createdAt": 1647113684000, + "text": "

I needed to make a full name capitalized like amir diafi => Amir Diafi\nso I split the string to get an array of these names and capitalize the first letter of each one of them like so...

\n

\r\n
\r\n
const value = 'amir diafi karim mohammed'\nconst splited_names = value.split(' ')\nlet capitalizedValue = ''\n    for (const iterator of splited_names) {\n    capitalizedValue += \n    ` ${iterator.charAt(0).toUpperCase()}${iterator.slice(1)}`\n}\n    \n capitalizedValue.trim()\n console.log(capitalizedValue)\n//amir diafi karim => Amir Diafi Karim
\r\n
\r\n
\r\n

\n", + "upvotes": 1769, + "upvoterUsernames": [], + "downvotes": 1769, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92fbb", + "creator": "Sean", + "createdAt": 1651246247000, + "text": "This is not related to the question.", + "upvotes": 2402, + "upvoterUsernames": [], + "downvotes": 2402, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92e00", + "creator": "davidchoo12", + "createdAt": 1648576375000, + "text": "

If you want to capitalize each word in a string, can use this:

\n
'all_lowercase Capitalized lower_then_Upper a'.replace(/(?<=\\b)[a-z](?=\\w*)/g, c => c.toUpperCase())\n// prints "All_lowercase Capitalized Lower_then_Upper A"\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92fbd", + "creator": "Sean", + "createdAt": 1651246216000, + "text": "The question does not ask to capitalize each word in a string.", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92e01", + "creator": "user1075296", + "createdAt": 1649342542000, + "text": "

EDIT :\nI like this one :

\n
yourString.replace(/(^[a-z])/i, (str, firstLetter) => firstLetter.toUpperCase())\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0b082fcc3049e92fbf", + "creator": "Sean", + "createdAt": 1651246181000, + "text": "This would fail the last test in the question.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92e02", + "creator": "Mr.Sabin ", + "createdAt": 1649781736000, + "text": "
var nameP = prompt("please enter your name");\nvar nameQ = nameP.slice(0,1);\nvar nameR = nameP.slice(1,100);\nnameQ = nameQ.toUpperCase();\nnameP = nameQ + nameR;\nconsole.log("Hello! " + nameP);\n
\n

Output:

\n
Hello! Alex\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92e03", + "creator": "Arsonik", + "createdAt": 1650012471000, + "text": "

The reduce/Typescript approach with separator option !

\n
declare global {\n    interface String {\n        toCapitalizeWords(separators?: string[]): string;\n    }\n}\nString.prototype.toCapitalizeWords = function (separators = [' ', '-']) {\n    return separators.reduce(\n        (str, sep) =>\n            str\n                .split(sep)\n                .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n                .join(sep),\n        this.toString()\n    );\n};\n\n// exemple\n"blanc-dupont-michel".toCapitalizeWords()\n// or\n"BLANC:DUPONT:MICHEL".toLowerCase().toCapitalizeWords(':')\n
\n", + "upvotes": 284, + "upvoterUsernames": [], + "downvotes": 284, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0c082fcc3049e92fc6", + "creator": "Sean", + "createdAt": 1651246068000, + "text": "The question is not about capitalizing each word.", + "upvotes": 139, + "upvoterUsernames": [], + "downvotes": 139, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f329bc082fcc3049e92e04", + "creator": "Dawood F.M Kaundama", + "createdAt": 1650538431000, + "text": "

There might be an answer above that has addressed this, but I was unable to locate it.\nThe following code capitalizes the whole given string.

\n

\r\n
\r\n
capitalize = (str) => {\n    if(!str) throw 'Cannot capilatize undefined';\n    let strings = str.split(' ');\n    return strings.map(string => string.charAt(0).toLocaleUpperCase()+string.slice(1)).join(' ');\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92e05", + "creator": "DiaMaBo", + "createdAt": 1654554529000, + "text": "

Please use lodash

\n
import { capitalize } from 'lodash';\n/** call it */\ncapitalize('word') //Word\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92e06", + "creator": "Manbir Judge", + "createdAt": 1654854471000, + "text": "

A simple, compact function that will do your job:

\n
const capitalize = str => str.split(' ').map(sub => sub.charAt(0).toUpperCase() + sub.slice(1)).join(' ');\n
\n

"foo" > "Foo"\n
\n"foo bar" > "Foo Bar"

\n", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 312, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f329bc082fcc3049e92e07", + "creator": "Maher Aldous", + "createdAt": 1655584117000, + "text": "

This code might work good in some cases:

\n

\r\n
\r\n
function capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\nconsole.log(capitalizeFirstLetter('foo')); // Foo\n// But if we had like this it won't work well\nconsole.log(capitalizeFirstLetter('fOo')); // FOo
\r\n
\r\n
\r\n

\n

But if you really want to make sure, that there is only the first letter capitalized and the rest is built out of lowercase letters, you could adjust the code like this:

\n

\r\n
\r\n
function capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();\n}\n    \nconsole.log(capitalizeFirstLetter('fOo')); // Foo
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 7, + "commentItems": [ + { + "_id": "62f321be082fcc3049e90243", + "creator": "Muhammad Umer", + "createdAt": 1416597937000, + "text": "what about: return str.replace(/(\\b\\w)/gi,function(m){return m.toUpperCase();});", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90244", + "creator": "dr.dimitru", + "createdAt": 1448424025000, + "text": "Simpler: string[0].toUpperCase() + string.substring(1)", + "upvotes": 264, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90245", + "creator": "eaorak", + "createdAt": 1572726004000, + "text": "`${s[0].toUpperCase()}${s.slice(1)}`", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90246", + "creator": "Константин Ван", + "createdAt": 1578293380000, + "text": "([initial, ...rest]) => [initial.toUpperCase(), ...rest].join("")", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90247", + "creator": "Navruzbek Noraliev", + "createdAt": 1583320640000, + "text": "str.toLowerCase().replace(/\\b(\\w)/g, s => s.toUpperCase())", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90248", + "creator": "Rolando Niubó", + "createdAt": 1639196818000, + "text": "string.replace(/./, c => c.toUpperCase()) Cant get simplier than this folks", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321be082fcc3049e90249", + "creator": "Pashan", + "createdAt": 1657389914000, + "text": "string.trim().split(" ").map(s => s[0].toUpperCase() + s.substring(1)).join(" ") This should handle sentences too", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3258954, + "uvac": 3259073 + } + }, + { + "_id": "62f321bb082fcc3049e8fece", + "title": "Get the current URL with JavaScript?", + "title-lowercase": "get the current url with javascript?", + "creator": "dougoftheabaci", + "createdAt": 1245785205000, + "status": "open", + "text": "

All I want is to get the website URL. Not the URL as taken from a link. On the page loading I need to be able to grab the full, current URL of the website and set it as a variable to do with as I please.

\n", + "upvotes": 6460, + "upvoterUsernames": [], + "downvotes": 2949, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3397366, + "answers": 26, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e9056c", + "creator": "Rohan Patil", + "createdAt": 1387457938000, + "text": "
var currentPageUrlIs = \"\";\nif (typeof this.href != \"undefined\") {\n       currentPageUrlIs = this.href.toString().toLowerCase(); \n}else{ \n       currentPageUrlIs = document.location.toString().toLowerCase();\n}\n
\n\n

The above code can also help someone

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9056b", + "creator": "Christoph", + "createdAt": 1245785735000, + "text": "

Use window.location for read and write access to the location object associated with the current frame. If you just want to get the address as a read-only string, you may use document.URL, which should contain the same value as window.location.href.

\n", + "upvotes": 449, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90569", + "creator": "Zanoni", + "createdAt": 1245785323000, + "text": "

Gets the current page URL:

\n\n
window.location.href\n
\n", + "upvotes": 306, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b9082fcc3049e919d8", + "creator": "Gumbo", + "createdAt": 1245785547000, + "text": "Note that that’s the window’s location not the document’s.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324b9082fcc3049e919d9", + "creator": "Zanoni", + "createdAt": 1245785671000, + "text": "It's the same thing. Full current URL refers to the document path (external address).", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f324b9082fcc3049e919db", + "creator": "chendral", + "createdAt": 1245786435000, + "text": "Is it standardized like document.url? (I mean something like a w3c document)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9056a", + "creator": "VolkerK", + "createdAt": 1245785340000, + "text": "

Use:

\n
window.location.href\n
\n

As noted in the comments, the line below works, but it is bugged for Firefox.

\n
document.URL\n
\n

See URL of type DOMString, readonly.

\n", + "upvotes": 7334, + "upvoterUsernames": [], + "downvotes": 3090, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f324b9082fcc3049e919dd", + "creator": "GabrielBB", + "createdAt": 1405081908000, + "text": ""window.location.href" for the win", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f324b9082fcc3049e919df", + "creator": "wranvaud", + "createdAt": 1572357787000, + "text": "@aliyouhannaei you could also do window.location.pathname to get just the path", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324b9082fcc3049e919e1", + "creator": "Timo", + "createdAt": 1615577810000, + "text": "@AliU toString() should not be necessary as the former is a string.", + "upvotes": 3124, + "upvoterUsernames": [], + "downvotes": 3124, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9056d", + "creator": "Nikhil Agrawal", + "createdAt": 1387812798000, + "text": "

URL Info Access

\n

JavaScript provides you with many methods to retrieve and change the current URL, which is displayed in the browser's address bar. All these methods use the Location object, which is a property of the Window object. You can read the current Location object by reading window.location:

\n
var currentLocation = window.location;\n
\n

Basic URL Structure

\n
<protocol>//<hostname>:<port>/<pathname><search><hash>\n
\n\n

With these Location object properties you can access all of these URL components and what they can set or return:

\n\n

I hope you got your answer..

\n", + "upvotes": 1524, + "upvoterUsernames": [], + "downvotes": 700, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ba082fcc3049e919e3", + "creator": "AJcodez", + "createdAt": 1496674511000, + "text": "Keep in mind ie9 pathname does not have a leading slash, so it could be index.html.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324ba082fcc3049e919e5", + "creator": "Apollo Data", + "createdAt": 1577466080000, + "text": "It's called 'search' not 'query'", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324ba082fcc3049e919e7", + "creator": "madaimartin", + "createdAt": 1603272335000, + "text": "Your answer is very detailed. Thank you for sharing you knowledge with us.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324ba082fcc3049e919e9", + "creator": "Radmation", + "createdAt": 1603481003000, + "text": "I appreciate this answer, straight forward and to the point. Thanks for sharing!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9056f", + "creator": "Syed Nasir Abbas", + "createdAt": 1409958442000, + "text": "

For complete URL with query strings:

\n
document.location.toString()\n
\n

For host URL:

\n
window.location\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9056e", + "creator": "Dorian", + "createdAt": 1398442468000, + "text": "

Use: window.location.href.

\n\n

As noted above, document.URL doesn't update when updating window.location. See MDN.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90570", + "creator": "kishore", + "createdAt": 1427350211000, + "text": "\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324ba082fcc3049e919ee", + "creator": "OMGPOP", + "createdAt": 1435564394000, + "text": "window.location.pathname does not include query and hash fragment", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90572", + "creator": "Josip Ivic", + "createdAt": 1443535553000, + "text": "

The way to get the current location object is window.location.

\n\n

Compare this to document.location, which originally only returned the current URL as a string. Probably to avoid confusion, document.location was replaced with document.URL.

\n\n

And, all modern browsers map document.location to window.location.

\n\n

In reality, for cross-browser safety, you should use window.location rather than document.location.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90571", + "creator": "Sangeet Shah", + "createdAt": 1441867865000, + "text": "

To get the path, you can use:

\n\n

\r\n
\r\n
console.log('document.location', document.location.href);\r\nconsole.log('location.pathname',  window.location.pathname); // Returns path only\r\nconsole.log('location.href', window.location.href); // Returns full URL
\r\n
\r\n
\r\n

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90573", + "creator": "Maleen Abewardana", + "createdAt": 1460473587000, + "text": "

In jstl we can access the current URL path using pageContext.request.contextPath. If you want to do an Ajax call, use the following URL.

\n\n
url = \"${pageContext.request.contextPath}\" + \"/controller/path\"\n
\n\n

Example: For the page http://stackoverflow.com/posts/36577223 this will give http://stackoverflow.com/controller/path.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90574", + "creator": "Bhaskar Bhatt", + "createdAt": 1475940243000, + "text": "

You can get the current URL location with a hash tag by using:

\n\n

JavaScript:

\n\n
 // Using href\n var URL = window.location.href;\n\n // Using path\n var URL = window.location.pathname;\n
\n\n

jQuery:

\n\n
$(location).attr('href');\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90576", + "creator": "زياد", + "createdAt": 1491479246000, + "text": "
location.origin+location.pathname+location.search+location.hash;\n
\n

and

\n
location.href\n
\n

does the same.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90575", + "creator": "Khaled Harby", + "createdAt": 1489497238000, + "text": "

You can get the full link of the current page through location.href\nand to get the link of the current controller, use:

\n\n
location.href.substring(0, location.href.lastIndexOf('/'));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90577", + "creator": "Tmh", + "createdAt": 1497000761000, + "text": "

Open Developer Tools, type in the following in the console and press Enter.

\n\n
window.location\n
\n\n

Ex: Below is the screenshot of the result on the current page.

\n\n

\"enter

\n\n

Grab what you need from here. :)

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90579", + "creator": "Hitesh Sahu", + "createdAt": 1509717156000, + "text": "

Adding result for quick reference

\n\n
\n

window.location;

\n
\n\n
 Location {href: \"https://stackoverflow.com/questions/1034621/get-the-current-url-with-javascript\",\n ancestorOrigins: DOMStringList,\n origin: \"https://stackoverflow.com\",\n replace: ƒ, assign: ƒ, …}\n
\n\n
\n

document.location

\n
\n\n
  Location {href: \"https://stackoverflow.com/questions/1034621/get-the-current-url-with-javascript\", \nancestorOrigins: DOMStringList,\n origin: \"https://stackoverflow.com\",\n replace: ƒ, assign: ƒ\n, …}\n
\n\n
\n

window.location.pathname

\n
\n\n
\"/questions/1034621/get-the-current-url-with-javascript\"\n
\n\n
\n

window.location.href

\n
\n\n
\"https://stackoverflow.com/questions/1034621/get-the-current-url-with-javascript\"\n
\n\n
\n

location.hostname

\n
\n\n
\"stackoverflow.com\"\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90578", + "creator": "Alireza", + "createdAt": 1507343506000, + "text": "

OK, getting the full URL of the current page is easy using pure JavaScript. For example, try this code on this page:

\n\n
window.location.href;\n// use it in the console of this page will return\n// http://stackoverflow.com/questions/1034621/get-current-url-in-web-browser\"\n
\n\n
\n

The window.location.href property returns the URL of the current page.

\n
\n\n

\r\n
\r\n
document.getElementById(\"root\").innerHTML = \"The full URL of this page is:<br>\" + window.location.href;
\r\n
<!DOCTYPE html>\r\n<html>\r\n\r\n<body>\r\n  <h2>JavaScript</h2>\r\n  <h3>The window.location.href</h3>\r\n  <p id=\"root\"></p>\r\n</body>\r\n\r\n</html>
\r\n
\r\n
\r\n

\n\n

Just not bad to mention these as well:

\n\n\n\n

So window.location.href handles all in once... basically:

\n\n
window.location.protocol + '//' + window.location.hostname + window.location.pathname + window.location.hash === window.location.href;\n    //true\n
\n\n

Also using window is not needed if already in window scope...

\n\n

So, in that case, you can use:

\n\n
location.protocol\n\nlocation.hostname\n\nlocation.pathname\n\nlocation.hash\n\nlocation.href\n
\n\n

\"Get

\n", + "upvotes": 180, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057a", + "creator": "Hasib Kamal Chowdhury", + "createdAt": 1514261302000, + "text": "

Getting the current URL with JavaScript :

\n\n
\n \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057c", + "creator": "Ashish Kamble", + "createdAt": 1539085965000, + "text": "

Firstly check for page is loaded completely in

\n\n
browser,window.location.toString();\n\nwindow.location.href\n
\n\n

then call a function which takes url, URL variable and prints on console,

\n\n
$(window).load(function(){\n   var url = window.location.href.toString();\n   var URL = document.URL;\n   var wayThreeUsingJQuery = $(location).attr('href');\n   console.log(url);\n   console.log(URL);\n   console.log(wayThreeUsingJQuery );\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057b", + "creator": "curiosity", + "createdAt": 1530354360000, + "text": "

if you are referring to a specific link that has an id this code can help you.

\n\n
$(\".disapprove\").click(function(){\n    var id = $(this).attr(\"id\");\n\n    $.ajax({\n        url: \"<?php echo base_url('index.php/sample/page/\"+id+\"')?>\",\n        type: \"post\",\n        success:function()\n        {\n            alert(\"The Request has been Disapproved\");\n            window.location.replace(\"http://localhost/sample/page/\"+id+\"\");\n        }\n    });\n});\n
\n\n

I am using ajax here to submit an id and redirect the page using window.location.replace. just add an attribute id=\"\" as stated.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057d", + "creator": "Seph Reed", + "createdAt": 1569797488000, + "text": "

For those who want an actual URL object, potentially for a utility which takes URLs as an argument:

\n\n
const url = new URL(window.location.href)\n
\n\n

https://developer.mozilla.org/en-US/docs/Web/API/URL

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057e", + "creator": "Apollo Data", + "createdAt": 1577466346000, + "text": "

Nikhil Agrawal's answer is great, just adding a little example here you can do in the console to see the different components in action:

\n\n

\"enter

\n\n

If you want the base URL without path or query parameter (for example to do AJAX requests against to work on both development/staging AND production servers), window.location.origin is best as it keeps the protocol as well as optional port (in Django development, you sometimes have a non-standard port which breaks it if you just use hostname etc.)

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90580", + "creator": "Programmer", + "createdAt": 1609502148000, + "text": "

You have multiple ways to do this.

\n

1:

\n
location.href;\n
\n

2:

\n
document.URL;\n
\n

3:

\n
document.documentURI;\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90581", + "creator": "Giacomo Casadei", + "createdAt": 1612951492000, + "text": "

Use this:

\n

\r\n
\r\n
var url = window.location.href;\n\nconsole.log(url);
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9057f", + "creator": "Kamil Kiełczewski", + "createdAt": 1577918268000, + "text": "

Short

\n
location+''\n
\n

\r\n
\r\n
let url = location+'';\n\nconsole.log(url);
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90582", + "creator": "Weilory", + "createdAt": 1639815449000, + "text": "
\n// http://127.0.0.1:8000/projects/page/2?name=jake&age=34\nlet url = new URL(window.location.href);\n/*\nhash: ""\n\nhost: "127.0.0.1:8000"\n\nhostname: "127.0.0.1"\n\nhref: "http://127.0.0.1:8000/projects/page/2?username=jake&age=34"\n\norigin: "http://127.0.0.1:8000"\n\npassword: ""\n\npathname: "/projects/page/2"\n\nport: "8000"\n\nprotocol: "http:"\n\nsearch: "?name=jake&age=34"\n\nusername: ""\n*/\n\nurl.searchParams.get('name')\n// jake\n\nurl.searchParams.get('age')\n// 34\n\nurl.searchParams.get('gender')\n// null\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3403826, + "uvac": 3403852 + } + }, + { + "_id": "62f321bb082fcc3049e8ff08", + "title": "Get selected value in dropdown list using JavaScript", + "title-lowercase": "get selected value in dropdown list using javascript", + "creator": "Fire Hand", + "createdAt": 1246865106000, + "status": "open", + "text": "

How do I get the selected value from a dropdown list using JavaScript?

\n

\r\n
\r\n
<form>\n  <select id=\"ddlViewBy\">\n    <option value=\"1\">test1</option>\n    <option value=\"2\" selected=\"selected\">test2</option>\n    <option value=\"3\">test3</option>\n  </select>\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 4276, + "upvoterUsernames": [], + "downvotes": 2060, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 5184076, + "answers": 32, + "answerItems": [ + { + "_id": "62f3220b082fcc3049e90f8d", + "creator": "Greg", + "createdAt": 1246865396000, + "text": "
var strUser = e.options[e.selectedIndex].value;\n
\n\n

This is correct and should give you the value.\nIs it the text you're after?

\n\n
var strUser = e.options[e.selectedIndex].text;\n
\n\n

So you're clear on the terminology:

\n\n
<select>\n    <option value=\"hello\">Hello World</option>\n</select>\n
\n\n

This option has:

\n\n\n", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296b082fcc3049e92bf6", + "creator": "Andrew Koper", + "createdAt": 1377615618000, + "text": "Yep - make the value of the option the same as what it is. Simpler - the guy above needs to write more code to make up for his initial vagueness.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92bf8", + "creator": "OZZIE", + "createdAt": 1546427419000, + "text": "should be e.target.options[e.target.selectedIndex].text don't know why it's wrong in all answers here..", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f8c", + "creator": "Paolo Bergantino", + "createdAt": 1246865383000, + "text": "

Given a select element that looks like this:

\n
<select id="ddlViewBy">\n  <option value="1">test1</option>\n  <option value="2" selected="selected">test2</option>\n  <option value="3">test3</option>\n</select>\n
\n

Running this code:

\n
var e = document.getElementById("ddlViewBy");\nvar value = e.value;\nvar text = e.options[e.selectedIndex].text;\n
\n

Results in:

\n
value == 2\ntext == "test2"\n
\n
\n

Interactive example:

\n

\r\n
\r\n
var e = document.getElementById(\"ddlViewBy\");\nfunction onChange() {\n  var value = e.value;\n  var text = e.options[e.selectedIndex].text;\n  console.log(value, text);\n}\ne.onchange = onChange;\nonChange();
\r\n
<form>\n  <select id=\"ddlViewBy\">\n    <option value=\"1\">test1</option>\n    <option value=\"2\" selected=\"selected\">test2</option>\n    <option value=\"3\">test3</option>\n  </select>\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 6715, + "upvoterUsernames": [], + "downvotes": 3208, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3296b082fcc3049e92bf9", + "creator": "The Red Pea", + "createdAt": 1419984101000, + "text": "var strUser = e.options[e.selectedIndex].value; why not just var strUser = e.value ?", + "upvotes": 296, + "upvoterUsernames": [], + "downvotes": 130, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92bfb", + "creator": "Fthr", + "createdAt": 1477412201000, + "text": "I used like this: var e = document.getElementById("ddlViewBy").value;", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92bfd", + "creator": "Massimiliano Kraus", + "createdAt": 1514544153000, + "text": "@RishiDua you can iterate the options of the select: for(var o of e.options) { if (o.selected) { /* ... */ } }", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92bff", + "creator": "Harold_Finch", + "createdAt": 1528982823000, + "text": "Good answer but this doesn't work well for a multi select. Can someone assist?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92c01", + "creator": "Rishi Dua", + "createdAt": 1529016784000, + "text": "@Harold_Finch for(var o of e.options) { if (o.selected) { /* ... */ } } solution should work, although I dont remember what I did (2yr old comment).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92c03", + "creator": "OZZIE", + "createdAt": 1546427401000, + "text": "should be e.target.options[e.target.selectedIndex].text don't know why it's wrong in all answers here..", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92c04", + "creator": "Salam", + "createdAt": 1570686369000, + "text": "e.val() was enough for me", + "upvotes": 944, + "upvoterUsernames": [], + "downvotes": 944, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92c06", + "creator": "Fabien Haddadi", + "createdAt": 1574770341000, + "text": "Purists would even write const e = document.getElementById("ddlViewBy"); since it will not change (it's a DOM assumption)", + "upvotes": 220, + "upvoterUsernames": [], + "downvotes": 220, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92c08", + "creator": "ygoe", + "createdAt": 1642520568000, + "text": "@Salam That's jQuery, not plain JavaScript.", + "upvotes": 824, + "upvoterUsernames": [], + "downvotes": 824, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f8e", + "creator": "Ben Greenaway", + "createdAt": 1299641522000, + "text": "

Beginners are likely to want to access values from a select with the NAME attribute rather than ID attribute. We know all form elements need names, even before they get ids.

\n

So, I'm adding the getElementsByName() solution just for new developers to see too.

\n

NB. names for form elements will need to be unique for your form to be usable once posted, but the DOM can allow a name be shared by more than one element. For that reason consider adding IDs to forms if you can, or be explicit with form element names my_nth_select_named_x and my_nth_text_input_named_y.

\n

Example using getElementsByName:

\n
var e = document.getElementsByName("my_select_with_name_ddlViewBy")[0];\nvar strUser = e.options[e.selectedIndex].value;\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296c082fcc3049e92c0a", + "creator": "zeuf", + "createdAt": 1512841442000, + "text": "Doesn't work if my_select_with_name_ddlViewBy is an array like my_select_with_name_ddlViewBy[]", + "upvotes": 397, + "upvoterUsernames": [], + "downvotes": 397, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f8f", + "creator": "Vitalii Fedorenko", + "createdAt": 1324174096000, + "text": "

Plain JavaScript:

\n
var e = document.getElementById("elementId");\nvar value = e.options[e.selectedIndex].value;\nvar text = e.options[e.selectedIndex].text;\n
\n

jQuery:

\n
$("#elementId :selected").text(); // The text content of the selected option\n$("#elementId").val(); // The value of the selected option\n
\n

AngularJS: (http://jsfiddle.net/qk5wwyct):

\n
// HTML\n<select ng-model="selectItem" ng-options="item as item.text for item in items">\n</select>\n<p>Text: {{selectItem.text}}</p>\n<p>Value: {{selectItem.value}}</p>\n\n// JavaScript\n$scope.items = [{\n  value: 'item_1_id',\n  text: 'Item 1'\n}, {\n  value: 'item_2_id',\n  text: 'Item 2'\n}];\n
\n", + "upvotes": 702, + "upvoterUsernames": [], + "downvotes": 255, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296c082fcc3049e92c0d", + "creator": "Kevin", + "createdAt": 1370553488000, + "text": "I must be doing something wrong because when I try this I get back the text of every option in the drop down.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296c082fcc3049e92c0e", + "creator": "Ruwantha", + "createdAt": 1376467674000, + "text": "This did worked for me in different way. $("#ddlViewBy :selected").val() not without selected", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3296c082fcc3049e92c10", + "creator": "Christopher", + "createdAt": 1427469520000, + "text": "element.options[e.selectedIndex].value must be element.options[element.selectedIndex].value", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296c082fcc3049e92c12", + "creator": "Cindy Meister", + "createdAt": 1519928510000, + "text": "Still useful - thank you for writing out the variations / language! Now if I only knew the equivalent for Office JS API Dropdown...", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296c082fcc3049e92c14", + "creator": "OZZIE", + "createdAt": 1546427411000, + "text": "should be e.target.options[e.target.selectedIndex].text don't know why it's wrong in all answers here..", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f3296c082fcc3049e92c15", + "creator": "Elias", + "createdAt": 1615937361000, + "text": "It also worked for me in jQuery by simply putting $("#elementId").val() with no need to mention the selected", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f90", + "creator": "Carl Onager", + "createdAt": 1340033308000, + "text": "

If you ever run across code written purely for Internet Explorer you might see this:

\n\n
var e = document.getElementById(\"ddlViewBy\");\nvar strUser = e.options(e.selectedIndex).value;\n
\n\n

Running the above in Firefox et al will give you an 'is not a function' error, because Internet Explorer allows you to get away with using () instead of []:

\n\n
var e = document.getElementById(\"ddlViewBy\");\nvar strUser = e.options[e.selectedIndex].value;\n
\n\n

The correct way is to use square brackets.

\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f91", + "creator": "Code Spy", + "createdAt": 1354517420000, + "text": "

The following code exhibits various examples related to getting/putting of values from input/select fields using JavaScript.

\n\n

Source Link

\n\n

Working Javascript & jQuery Demo

\n\n

\"enter

\n\n

\"enter

\n\n
 <select id=\"Ultra\" onchange=\"run()\">  <!--Call run() function-->\n     <option value=\"0\">Select</option>\n     <option value=\"8\">text1</option>\n     <option value=\"5\">text2</option>\n     <option value=\"4\">text3</option>\n</select><br><br>\nTextBox1<br>\n<input type=\"text\" id=\"srt\" placeholder=\"get value on option select\"><br>\nTextBox2<br>\n<input type=\"text\" id=\"rtt\"  placeholder=\"Write Something !\" onkeyup=\"up()\">\n
\n\n

The following script is getting the value of the selected option and putting it in text box 1

\n\n
<script>\n    function run() {\n        document.getElementById(\"srt\").value = document.getElementById(\"Ultra\").value;\n    }\n</script>\n
\n\n

The following script is getting a value from a text box 2 and alerting with its value

\n\n
<script>\n    function up() {\n        //if (document.getElementById(\"srt\").value != \"\") {\n            var dop = document.getElementById(\"srt\").value;\n        //}\n        alert(dop);\n    }\n</script>\n
\n\n

The following script is calling a function from a function

\n\n
<script>\n    function up() {\n        var dop = document.getElementById(\"srt\").value;\n        pop(dop); // Calling function pop\n    }\n\n    function pop(val) {\n        alert(val);\n    }?\n</script>\n
\n", + "upvotes": 129, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296c082fcc3049e92c18", + "creator": "Berci", + "createdAt": 1579910009000, + "text": "onchange=run(this.value) or (this.text) can be more benefficial.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f92", + "creator": "Mohammad Faisal", + "createdAt": 1382707633000, + "text": "
var selectedValue = document.getElementById(\"ddlViewBy\").value;\n
\n", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f95", + "creator": "boateng", + "createdAt": 1429019109000, + "text": "
<select id=\"Ultra\" onchange=\"alert(this.value)\"> \n <option value=\"0\">Select</option>\n <option value=\"8\">text1</option>\n <option value=\"5\">text2</option>\n <option value=\"4\">text3</option>\n</select>\n
\n\n

Any input/form field can use a “this” keyword when you are accessing it from inside the element. This eliminates the need for locating a form in the dom tree and then locating this element inside the form.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f93", + "creator": "Marvil Joy", + "createdAt": 1392721316000, + "text": "

Just use

\n\n\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296c082fcc3049e92c1c", + "creator": "David Meza", + "createdAt": 1459188587000, + "text": "This uses jQuery, which doesn't answer the OP's question.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f94", + "creator": "zackify", + "createdAt": 1397681560000, + "text": "

Here's an easy way to do it in an onchange function:

\n\n

event.target.options[event.target.selectedIndex].dataset.name

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296c082fcc3049e92c1f", + "creator": "Christian Læirbag", + "createdAt": 1455143578000, + "text": "Talking about simplicity, I was thinking about this instead of event.target", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f96", + "creator": "Frank Conijn - Support Ukraine", + "createdAt": 1432262555000, + "text": "

The previous answers still leave room for improvement because of the possibilities, the intuitiveness of the code, and the use of id versus name. One can get a read-out of three data of a selected option -- its index number, its value and its text. This simple, cross-browser code does all three:

\n\n
<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>Demo GetSelectOptionData</title>\n</head>\n<body>\n    <form name=\"demoForm\">\n        <select name=\"demoSelect\" onchange=\"showData()\">\n            <option value=\"zilch\">Select:</option>\n            <option value=\"A\">Option 1</option>\n            <option value=\"B\">Option 2</option>\n            <option value=\"C\">Option 3</option>\n        </select>\n    </form>\n\n    <p id=\"firstP\">&nbsp;</p>\n    <p id=\"secondP\">&nbsp;</p>\n    <p id=\"thirdP\">&nbsp;</p>\n\n    <script>\n    function showData() {\n        var theSelect = demoForm.demoSelect;\n        var firstP = document.getElementById('firstP');\n        var secondP = document.getElementById('secondP');\n        var thirdP = document.getElementById('thirdP');\n        firstP.innerHTML = ('This option\\'s index number is: ' + theSelect.selectedIndex + ' (Javascript index numbers start at 0)');\n        secondP.innerHTML = ('Its value is: ' + theSelect[theSelect.selectedIndex].value);\n        thirdP.innerHTML = ('Its text is: ' + theSelect[theSelect.selectedIndex].text);\n    }\n     </script>\n</body>\n</html>\n
\n\n

Live demo: http://jsbin.com/jiwena/1/edit?html,output .

\n\n

id should be used for make-up purposes. For functional form purposes, name is still valid, also in HTML5, and should still be used. Lastly, mind the use of square versus round brackets in certain places. As was explained before, only (older versions of) Internet Explorer will accept round ones in all places.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f97", + "creator": "Hector Llorens", + "createdAt": 1445553494000, + "text": "

In 2015, in Firefox, the following also works.

\n\n
\n

e.options.selectedIndex

\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f98", + "creator": "user5846985", + "createdAt": 1464201738000, + "text": "

Using jQuery:

\n\n
$('select').val();\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f99", + "creator": "Belgacem Ksiksi", + "createdAt": 1488229495000, + "text": "

Here is a JavaScript code line:

\n\n
var x = document.form1.list.value;\n
\n\n

Assuming that the dropdown menu named list name=\"list\" and included in a form with name attribute name=\"form1\".

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f9a", + "creator": "Genko", + "createdAt": 1494606332000, + "text": "

To go along with the previous answers, this is how I do it as a one-liner. This is for getting the actual text of the selected option. There are good examples for getting the index number already. (And for the text, I just wanted to show this way)

\n\n
let selText = document.getElementById('elementId').options[document.getElementById('elementId').selectedIndex].text\n
\n\n

In some rare instances you may need to use parentheses, but this would be very rare.

\n\n
let selText = (document.getElementById('elementId')).options[(document.getElementById('elementId')).selectedIndex].text;\n
\n\n

I doubt this processes any faster than the two line version. I simply like to consolidate my code as much as possible.

\n\n

Unfortunately this still fetches the element twice, which is not ideal. A method that only grabs the element once would be more useful, but I have not figured that out yet, in regards to doing this with one line of code.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f9b", + "creator": "Ani Menon", + "createdAt": 1501679941000, + "text": "

Running example of how it works:

\n\n

\r\n
\r\n
var e = document.getElementById(\"ddlViewBy\");\r\nvar val1 = e.options[e.selectedIndex].value;\r\nvar txt = e.options[e.selectedIndex].text;\r\n\r\ndocument.write(\"<br />Selected option Value: \"+ val1);\r\ndocument.write(\"<br />Selected option Text: \"+ txt);
\r\n
<select id=\"ddlViewBy\">\r\n  <option value=\"1\">test1</option>\r\n  <option value=\"2\">test2</option>\r\n  <option value=\"3\"  selected=\"selected\">test3</option>\r\n</select>
\r\n
\r\n
\r\n

\n\n

Note: The values don't change as the dropdown is changed, if you require that functionality then an onClick change is to be implemented.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296d082fcc3049e92c27", + "creator": "ReinstateMonica3167040", + "createdAt": 1531784199000, + "text": "Good answer for showing how the code needs to be refreshed after used!", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f9c", + "creator": "Rounin - Standing with Ukraine", + "createdAt": 1515105407000, + "text": "

You can use querySelector.

\n\n

E.g.

\n\n
var myElement = document.getElementById('ddlViewBy');\n\nvar myValue = myElement.querySelector('[selected]').value;\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f9d", + "creator": "Javad Kargar", + "createdAt": 1531939741000, + "text": "

Another solution is:

\n\n
document.getElementById('elementId').selectedOptions[0].value\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296d082fcc3049e92c2b", + "creator": "Bharata", + "createdAt": 1595544233000, + "text": "It is not cross-browser compatible.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f9e", + "creator": "Du-Lacoste", + "createdAt": 1532351925000, + "text": "

There are two ways to get this done either using JavaScript or jQuery.

\n\n

JavaScript:

\n\n
var getValue = document.getElementById('ddlViewBy').selectedOptions[0].value;\n\nalert (getValue); // This will output the value selected.\n
\n\n

OR

\n\n
var ddlViewBy = document.getElementById('ddlViewBy');\n\nvar value = ddlViewBy.options[ddlViewBy.selectedIndex].value;\n\nvar text = ddlViewBy.options[ddlViewBy.selectedIndex].text;\n\nalert (value); // This will output the value selected\n\nalert (text); // This will output the text of the value selected\n
\n\n

jQuery:

\n\n
$(\"#ddlViewBy:selected\").text(); // Text of the selected value\n\n$(\"#ddlViewBy\").val(); // Outputs the value of the ID in 'ddlViewBy'\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296d082fcc3049e92c2e", + "creator": "Geshan Ravindu", + "createdAt": 1650180263000, + "text": "Worked well. Thnaks a lot.", + "upvotes": 360, + "upvoterUsernames": [], + "downvotes": 360, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f9f", + "creator": "ThomAce", + "createdAt": 1539344343000, + "text": "

I have a bit different view of how to achieve this. I'm usually doing this with the following approach (it is an easier way and works with every browser as far as I know):

\n\n
<select onChange=\"functionToCall(this.value);\" id=\"ddlViewBy\">\n  <option value=\"value1\">Text one</option>\n  <option value=\"value2\">Text two</option>\n  <option value=\"value3\">Text three</option>\n  <option value=\"valueN\">Text N</option>\n</select>\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa0", + "creator": "Knautiluz", + "createdAt": 1545490985000, + "text": "

Just do: document.getElementById('idselect').options.selectedIndex

\n\n

Then you i'll get select index value, starting in 0.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa1", + "creator": "Kamil Kiełczewski", + "createdAt": 1576700364000, + "text": "

Try

\n\n
ddlViewBy.value                      // value\n\nddlViewBy.selectedOptions[0].text    // label\n
\n\n

\r\n
\r\n
console.log( ddlViewBy.value );\r\n\r\nconsole.log( ddlViewBy.selectedOptions[0].text );
\r\n
<select id=\"ddlViewBy\">\r\n  <option value=\"1\">Happy</option>\r\n  <option value=\"2\">Tree</option>\r\n  <option value=\"3\"  selected=\"selected\">Friends</option>\r\n</select>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa2", + "creator": "Pal Singh", + "createdAt": 1579217951000, + "text": "

You should be using querySelector to achieve this. This also standardize the way of getting value from form elements.

\n\n

var dropDownValue = document.querySelector('#ddlViewBy').value;

\n\n

Fiddle: https://jsfiddle.net/3t80pubr/

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa3", + "creator": "Olawale Oladiran", + "createdAt": 1580304186000, + "text": "

I don't know if I'm the one that doesn't get the question right, but this just worked for me:\nUse an onchange() event in your HTML, eg.

\n
<select id="numberToSelect" onchange="selectNum()">\n    <option value="1">One</option>\n    <option value="2">Two</option>\n    <option value="3">Three</option>\n</select>\n
\n

//javascript

\n
function selectNum(){\n    var strUser = document.getElementById("numberToSelect").value;\n}\n
\n

This will give you whatever value is on the select dropdown per click

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa4", + "creator": "Clairton Luz", + "createdAt": 1585744444000, + "text": "

The simplest way to do this is:

\n\n
var value = document.getElementById(\"selectId\").value;\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296e082fcc3049e92c34", + "creator": "Thanasis", + "createdAt": 1587388678000, + "text": "He wants not the value but the shown text of the select box", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f3296e082fcc3049e92c36", + "creator": "Zack Plauché", + "createdAt": 1593871256000, + "text": "I think this is the actual answer most people are looking for.", + "upvotes": 2617, + "upvoterUsernames": [], + "downvotes": 2617, + "downvoterUsernames": [] + }, + { + "_id": "62f3296e082fcc3049e92c38", + "creator": "Dilip Agheda", + "createdAt": 1598658615000, + "text": "Good answer! It was useful", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90fa5", + "creator": "Dangerousgame", + "createdAt": 1589964545000, + "text": "

Make a drop-down menu with several options (As many as you want!)

\n\n
<select>\n  <option value=\"giveItAName\">Give it a name\n  <option value=\"bananaShark\">Ridiculous animal\n  <ooption value=\"Unknown\">Give more options!\n</select>\n
\n\n

I made a bit hilarious.\nHere's the code snippet:

\n\n

\r\n
\r\n
<select>\r\n  <option value=\"RidiculousObject\">Banana Shark\r\n  <option value=\"SuperDuperCoding\">select tag and option tag!\r\n  <option value=\"Unknown\">Add more tags to add more options!\r\n</select>\r\n<h1>Only 1 option (Useless)</h1>\r\n<select>\r\n  <option value=\"Single\">Single Option\r\n</select>  
\r\n
\r\n
\r\n

\n\n

yay the snippet worked

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa7", + "creator": "Emmanuel Onah", + "createdAt": 1595275563000, + "text": "

I think you can attach an event listener to the select tag itself e.g:

\n
<script>\n  document.addEventListener("DOMContentLoaded", (_) => {\n    document.querySelector("select").addEventListener("change", (e) => {\n      console.log(e.target.value);\n    });\n  });\n</script>\n
\n

In this scenario, you should make sure you have a value attribute for all of your options, and they are not null.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296e082fcc3049e92c3b", + "creator": "Dominic Smith", + "createdAt": 1608736457000, + "text": "Great answer, thank you! In my case, I have two selectors, but I was able to access the selector id with 'e.srcElement.id'", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90fa6", + "creator": "Heretic Monkey", + "createdAt": 1591649435000, + "text": "

In more modern browsers, querySelector allows us to retrieve the selected option in one statement, using the :checked pseudo-class. From the selected option, we can gather whatever information we need:

\n\n

\r\n
\r\n
const opt = document.querySelector('#ddlViewBy option:checked');\r\n// opt is now the selected option, so\r\nconsole.log(opt.value, 'is the selected value');\r\nconsole.log(opt.text, \"is the selected option's text\");
\r\n
<select id=\"ddlViewBy\">\r\n  <option value=\"1\">test1</option>\r\n  <option value=\"2\" selected=\"selected\">test2</option>\r\n  <option value=\"3\">test3</option>\r\n</select>
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa8", + "creator": "emptywalls", + "createdAt": 1619063459000, + "text": "

event.target.value inside the onChange callback did the trick for me.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fa9", + "creator": "Dhairya Lakhera", + "createdAt": 1650969865000, + "text": "
<select name="test" id="test" >\n    <option value="1" full-name="Apple">A</option>\n    <option value="2" full-name="Ball">B</option>\n    <option value="3" full-name="Cat" selected>C</option>\n</select>\n\nvar obj  = document.getElementById('test');\nobj.options[obj.selectedIndex].value;  //3\nobj.options[obj.selectedIndex].text;   //C\nobj.options[obj.selectedIndex].getAttribute('full-name'); //Cat\nobj.options[obj.selectedIndex].selected; //true\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90faa", + "creator": "Ezra Siton", + "createdAt": 1651931461000, + "text": "

Most answer here get the value of "this" select menu onchange by plain text JavaScript selector.

\n

For example:

\n
document.getElementById("ddlViewBy").value;\n
\n

This is not DRY approach.

\n

DRY (3 lines of code):

\n
function handleChange(e) {\n  let innerText = e.target[e.target.options.selectedIndex].innerText;\n  let value = e.target.value;\n  /* Do something with these values */\n}\n
\n

Get the first select option:

\n
console.log(e.target[0]); /*output: <option value="value_hello">Hello innerText</option>*/\n
\n

With this idea in mind we return dynamically "this" select option item (By selectedIndex):

\n
e.target[e.target.options.selectedIndex].innerText;\n
\n

DEMO

\n

\r\n
\r\n
let log = document.getElementById('log');\n\nfunction handleChange(e) {\n  let innerText = e.target[e.target.options.selectedIndex].innerText;\n  let value = e.target.value;\n  \n  log.innerHTML = `<table>\n    <tr><th>value</th><th>innerText</th></tr>\n    <tr><td>${value}</td><td>${innerText}</td></tr>\n  </table>`;\n\n}
\r\n
<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.css\">\n\n<select id=\"greet\" onchange=\"handleChange(event)\">\n  <option value=\"value_hello\">Hello innerText</option>\n  <option value=\"value_goodbye\">Goodbye innerText</option>\n  <option value=\"value_seeYou\">See you... innerText</option>\n</select>\n\n<select id=\"other_select_menu\" onchange=\"handleChange(event)\">\n  <option value=\"value_paris\">Paris innerText</option>\n  <option value=\"value_ny\">New York innerText</option>\n</select>\n\n<div id=\"log\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90fab", + "creator": "questionto42standswithUkraine", + "createdAt": 1658177875000, + "text": "

There is a workaround, using the EasyUI framework with all of its plugins.

\n

You only need to add some EasyUI object that can read from an input as a "bridge" to the drop-down menu.

\n

Example: easyui-searchbox

\n

To the left, the drop-down, to the right, the easyui-searchbox:

\n

\"enter

\n
...\n<input id="ss" class="easyui-searchbox" style="width:300px"\n        data-options="  searcher:my_function,\n                        prompt:'Enter value',\n                        menu:'#mm'">\n<div id="mm" style="width:200px">\n    <div data-options="name:'1'">test1</div>\n    <div data-options="name:'2'">test2</div>\n</div>\n...\n
\n
...\n<script type="text/javascript">\n    function my_js_function(triggeredByButton = false){\n        // normal text of the searchbox (what you entered)\n        var value = $("#ss").searchbox("getValue");\n        // what you chose from the drop-down menu\n        var name = $("#ss").searchbox("getName");\n...\n
\n

Mind: the var name is the '1' or '2', that is, the "value of the drop-down", while var value is the value that was entered in the easyui-searchbox instead and not relevant if you only want to know the value of the drop-down.

\n

I checked how EasyUI fetches that #mm name, and I could not find out how to get that name without the help of EasyUI. The jQuery behind getName:

\n
getName:function(jq){\nreturn $.data(jq[0],"searchbox").searchbox.find("input.textbox-value").attr("name");\n}\n
\n

Mind that the return of this function is not the value of the easyui-searchbox, but the name of the #mm drop-down that was used as the menu parameter of the easyui-searchbox. Somehow EasyUI must get that other value, therefore it must be possible.

\n

If you do not want any plugin to be seen, make it as tiny as possible? Or find perhaps a plugin that does not need a form at all in the link above, I just did not take the time.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 5188352, + "uvac": 5188384 + } + }, + { + "_id": "62f321bb082fcc3049e8fec9", + "title": "Checking if a key exists in a JavaScript object?", + "title-lowercase": "checking if a key exists in a javascript object?", + "creator": "Adam Ernst", + "createdAt": 1247059292000, + "status": "open", + "text": "

How do I check if a particular key exists in a JavaScript object or array?

\n\n

If a key doesn't exist, and I try to access it, will it return false? Or throw an error?

\n", + "upvotes": 4383, + "upvoterUsernames": [], + "downvotes": 614, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3009723, + "answers": 29, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e90538", + "creator": "Eli Courtwright", + "createdAt": 1247059488000, + "text": "

It will return undefined.

\n\n

\r\n
\r\n
var aa = {hello: \"world\"};\r\nalert( aa[\"hello\"] );      // popup box with \"world\"\r\nalert( aa[\"goodbye\"] );    // popup box with \"undefined\"
\r\n
\r\n
\r\n

\n\n

undefined is a special constant value. So you can say, e.g.

\n\n
// note the three equal signs so that null won't be equal to undefined\nif( aa[\"goodbye\"] === undefined ) {\n    // do something\n}\n
\n\n

This is probably the best way to check for missing keys. However, as is pointed out in a comment below, it's theoretically possible that you'd want to have the actual value be undefined. I've never needed to do this and can't think of a reason offhand why I'd ever want to, but just for the sake of completeness, you can use the in operator

\n\n
// this works even if you have {\"goodbye\": undefined}\nif( \"goodbye\" in aa ) {\n    // do something\n}\n
\n", + "upvotes": 298, + "upvoterUsernames": [], + "downvotes": 118, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b6082fcc3049e919b3", + "creator": "Nosredna", + "createdAt": 1247059845000, + "text": "Yes. It returns undefined whether it is created as an object or an array.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919b5", + "creator": "Ates Goral", + "createdAt": 1247068329000, + "text": "What if the key exists but the value is actually undefined?", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919b6", + "creator": "Matthew Crumley", + "createdAt": 1247068586000, + "text": "You should use === instead of == when comparing to undefined, otherwise null will compare equal to undefined.", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9053a", + "creator": "user2320522", + "createdAt": 1366904734000, + "text": "
\"key\" in obj\n
\n\n

Is likely testing only object attribute values that are very different from array keys

\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90539", + "creator": "Ates Goral", + "createdAt": 1247068270000, + "text": "

Checking for undefined-ness is not an accurate way of testing whether a key exists. What if the key exists but the value is actually undefined?

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(obj[\"key\"] !== undefined); // false, but the key exists!
\r\n
\r\n
\r\n

\n

You should instead use the in operator:

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(\"key\" in obj); // true, regardless of the actual value
\r\n
\r\n
\r\n

\n

If you want to check if a key doesn't exist, remember to use parenthesis:

\n

\r\n
\r\n
var obj = { not_key: undefined };\nconsole.log(!(\"key\" in obj)); // true if \"key\" doesn't exist in object\nconsole.log(!\"key\" in obj);   // Do not do this! It is equivalent to \"false in obj\"
\r\n
\r\n
\r\n

\n

Or, if you want to particularly test for properties of the object instance (and not inherited properties), use hasOwnProperty:

\n

\r\n
\r\n
var obj = { key: undefined };\nconsole.log(obj.hasOwnProperty(\"key\")); // true
\r\n
\r\n
\r\n

\n

For performance comparison between the methods that are in, hasOwnProperty and key is undefined, see this benchmark:

\n

\"Benchmark

\n", + "upvotes": 8221, + "upvoterUsernames": [], + "downvotes": 3043, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f324b6082fcc3049e919b9", + "creator": "Ates Goral", + "createdAt": 1247069542000, + "text": "I'm convinced that there are use cases for having properties intentionally set to undefined.", + "upvotes": 387, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9053b", + "creator": "Lavi Avigdor", + "createdAt": 1384247988000, + "text": "

Three ways to check if a property is present in a javascript object:

\n\n
    \n
  1. !!obj.theProperty
    \nWill convert value to bool. returns true for all but the false value
  2. \n
  3. 'theProperty' in obj
    \nWill return true if the property exists, no matter its value (even empty)
  4. \n
  5. obj.hasOwnProperty('theProperty')
    \nDoes not check the prototype chain. (since all objects have the toString method, 1 and 2 will return true on it, while 3 can return false on it.)
  6. \n
\n\n

Reference:

\n\n
\n

http://book.mixu.net/node/ch5.html

\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b7082fcc3049e919ba", + "creator": "ARJUN", + "createdAt": 1553663459000, + "text": "!!obj.theProperty fails when value is undefined. Ex: var a = {a : undefined, b : null}; !!a.a **will return false**", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9053d", + "creator": "handle", + "createdAt": 1435754749000, + "text": "

The accepted answer refers to Object. Beware using the in operator on Array to find data instead of keys:

\n\n
(\"true\" in [\"true\", \"false\"])\n// -> false (Because the keys of the above Array are actually 0 and 1)\n
\n\n

To test existing elements in an Array: Best way to find if an item is in a JavaScript array?

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9053c", + "creator": "vatsal", + "createdAt": 1401392222000, + "text": "

If you are using underscore.js library then object/array operations become simple.

\n\n

In your case _.has method can be used. Example:

\n\n
yourArray = {age: \"10\"}\n\n_.has(yourArray, \"age\")\n
\n\n

returns true

\n\n

But,

\n\n
_.has(yourArray, \"invalidKey\")\n
\n\n

returns false

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9053e", + "creator": "jaredwilli", + "createdAt": 1457182605000, + "text": "

Here's a helper function I find quite useful

\n\n

This keyExists(key, search) can be used to easily lookup a key within objects or arrays!

\n\n

Just pass it the key you want to find, and search obj (the object or array) you want to find it in.

\n\n

\r\n
\r\n
function keyExists(key, search) {\r\n        if (!search || (search.constructor !== Array && search.constructor !== Object)) {\r\n            return false;\r\n        }\r\n        for (var i = 0; i < search.length; i++) {\r\n            if (search[i] === key) {\r\n                return true;\r\n            }\r\n        }\r\n        return key in search;\r\n    }\r\n\r\n// How to use it:\r\n// Searching for keys in Arrays\r\nconsole.log(keyExists('apple', ['apple', 'banana', 'orange'])); // true\r\nconsole.log(keyExists('fruit', ['apple', 'banana', 'orange'])); // false\r\n\r\n// Searching for keys in Objects\r\nconsole.log(keyExists('age', {'name': 'Bill', 'age': 29 })); // true\r\nconsole.log(keyExists('title', {'name': 'Jason', 'age': 29 })); // false
\r\n
\r\n
\r\n

\n\n

It's been pretty reliable and works well cross-browser.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9053f", + "creator": "Webeng", + "createdAt": 1466562545000, + "text": "

Answer:

\n\n
if (\"key\" in myObj)\n{\n    console.log(\"key exists!\");\n}\nelse\n{\n    console.log(\"key doesn't exist!\");\n}\n
\n\n

Explanation:

\n\n

The in operator will check if the key exists in the object. If you checked if the value was undefined: if (myObj[\"key\"] === 'undefined'), you could run into problems because a key could possibly exist in your object with the undefined value.

\n\n

For that reason, it is much better practice to first use the in operator and then compare the value that is inside the key once you already know it exists.

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90540", + "creator": "Mohan Dere", + "createdAt": 1480693087000, + "text": "

We can use - hasOwnProperty.call(obj, key);

\n\n

The underscore.js way -

\n\n
if(_.has(this.options, 'login')){\n  //key 'login' exists in this.options \n}\n\n_.has = function(obj, key) {\n  return hasOwnProperty.call(obj, key);\n};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90541", + "creator": "Hajji Tarik", + "createdAt": 1485358787000, + "text": "

vanila js

\n\n
yourObjName.hasOwnProperty(key) : true ? false;\n
\n\n

If you want to check if the object has at least one property in es2015

\n\n
Object.keys(yourObjName).length : true ? false\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90543", + "creator": "vsync", + "createdAt": 1490798851000, + "text": "

Optional chaining operator:

\n

\r\n
\r\n
const invoice = {customer: {address: {city: \"foo\"}}}\n\nconsole.log( invoice?.customer?.address?.city )\nconsole.log( invoice?.customer?.address?.street )\nconsole.log( invoice?.xyz?.address?.city )
\r\n
\r\n
\r\n

\n

See supported browsers list

\n
\n

For those which have lodash included in their project:
There is a lodash _.get method which tries to get "deep" keys:

\n
\n

Gets the value at path of object. If the resolved value is undefined,\nthe defaultValue is returned in its place.

\n
\n

\r\n
\r\n
var object = { 'a': [{ 'b': { 'c': 3 } }] };\n\nconsole.log(\n  _.get(object, 'a[0].b.c'),           // => 3\n  _.get(object, ['a', '0', 'b', 'c']), // => 3\n  _.get(object, 'a.b.c'),              // => undefined \n  _.get(object, 'a.b.c', 'default')    // => 'default'\n)
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js\"></script>
\r\n
\r\n
\r\n

\n
\n

This will effectively check if that key, however deep, is defined and will not throw an error which might harm the flow of your program if that key is not defined.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90542", + "creator": "kind user", + "createdAt": 1490543157000, + "text": "

ES6 solution

\n\n

using Array#some and Object.keys. It will return true if given key exists in the object or false if it doesn't.

\n\n

\r\n
\r\n
var obj = {foo: 'one', bar: 'two'};\r\n    \r\nfunction isKeyInObject(obj, key) {\r\n    var res = Object.keys(obj).some(v => v == key);\r\n    console.log(res);\r\n}\r\n\r\nisKeyInObject(obj, 'foo');\r\nisKeyInObject(obj, 'something');
\r\n
\r\n
\r\n

\n\n

One-line example.

\n\n

\r\n
\r\n
console.log(Object.keys({foo: 'one', bar: 'two'}).some(v => v == 'foo'));
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b7082fcc3049e919c1", + "creator": "Sid", + "createdAt": 1502016143000, + "text": "It will fail for non-numerable properties of the object.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b7082fcc3049e919c3", + "creator": "kind user", + "createdAt": 1502016675000, + "text": "@Sid Give me some example.", + "upvotes": 345, + "upvoterUsernames": [], + "downvotes": 345, + "downvoterUsernames": [] + }, + { + "_id": "62f324b7082fcc3049e919c4", + "creator": "kind user", + "createdAt": 1502018869000, + "text": "@Sid isMarried key isn't even applied into joshua object. Use assigment instead: joshua.isMarried = true;", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90544", + "creator": "realappie", + "createdAt": 1514995511000, + "text": "

While this doesn't necessarily check if a key exists, it does check for the truthiness of a value. Which undefined and null fall under.

\n\n

Boolean(obj.foo)

\n\n

This solution works best for me because I use typescript, and using strings like so 'foo' in obj or obj.hasOwnProperty('foo') \n to check whether a key exists or not does not provide me with intellisense.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90546", + "creator": "NAVIN", + "createdAt": 1535738751000, + "text": "

New awesome solution with JavaScript Destructuring:

\n\n
let obj = {\n    \"key1\": \"value1\",\n    \"key2\": \"value2\",\n    \"key3\": \"value3\",\n};\n\nlet {key1, key2, key3, key4} = obj;\n\n// key1 = \"value1\"\n// key2 = \"value2\"\n// key3 = \"value3\"\n// key4 = undefined\n\n// Can easily use `if` here on key4\nif(!key4) { console.log(\"key not present\"); } // Key not present\n
\n\n

Do check other use of JavaScript Destructuring

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90545", + "creator": "Alexander", + "createdAt": 1519222986000, + "text": "

These example can demonstrate the differences between defferent ways. Hope it will help you to pick the right one for your needs:

\n\n
// Lets create object `a` using create function `A`\nfunction A(){};\nA.prototype.onProtDef=2;\nA.prototype.onProtUndef=undefined;\nvar a=new A();\na.ownProp = 3;\na.ownPropUndef = undefined;\n\n// Let's try different methods:\n\na.onProtDef; // 2\na.onProtUndef; // undefined\na.ownProp; // 3\na.ownPropUndef; // undefined\na.whatEver; // undefined\na.valueOf; // ƒ valueOf() { [native code] }\n\na.hasOwnProperty('onProtDef'); // false\na.hasOwnProperty('onProtUndef'); // false\na.hasOwnProperty('ownProp'); // true\na.hasOwnProperty('ownPropUndef'); // true\na.hasOwnProperty('whatEver'); // false\na.hasOwnProperty('valueOf'); // false\n\n'onProtDef' in a; // true\n'onProtUndef' in a; // true\n'ownProp' in a; // true\n'ownPropUndef' in a; // true\n'whatEver' in a; // false\n'valueOf' in a; // true (on the prototype chain - Object.valueOf)\n\nObject.keys(a); // [\"ownProp\", \"ownPropUndef\"]\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90548", + "creator": "shekhardtu", + "createdAt": 1541431833000, + "text": "

The easiest way to check is

\n\n
\"key\" in object\n
\n\n

for example:

\n\n
var obj = {\n  a: 1,\n  b: 2,\n}\n\"a\" in obj // true\n\"c\" in obj // false\n
\n\n

Return value as true implies that key exists in the object.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90547", + "creator": "Alex", + "createdAt": 1536314694000, + "text": "

If you want to check for any key at any depth on an object and account for falsey values consider this line for a utility function:

\n\n
var keyExistsOn = (o, k) => k.split(\".\").reduce((a, c) => a.hasOwnProperty(c) ? a[c] || 1 : false, Object.assign({}, o)) === false ? false : true;\n
\n\n

Results

\n\n
var obj = {\n    test: \"\",\n    locals: {\n        test: \"\",\n        test2: false,\n        test3: NaN,\n        test4: 0,\n        test5: undefined,\n        auth: {\n            user: \"hw\"\n        }\n    }\n}\n\nkeyExistsOn(obj, \"\")\n> false\nkeyExistsOn(obj, \"locals.test\")\n> true\nkeyExistsOn(obj, \"locals.test2\")\n> true\nkeyExistsOn(obj, \"locals.test3\")\n> true\nkeyExistsOn(obj, \"locals.test4\")\n> true\nkeyExistsOn(obj, \"locals.test5\")\n> true\nkeyExistsOn(obj, \"sdsdf\")\nfalse\nkeyExistsOn(obj, \"sdsdf.rtsd\")\nfalse\nkeyExistsOn(obj, \"sdsdf.234d\")\nfalse\nkeyExistsOn(obj, \"2134.sdsdf.234d\")\nfalse\nkeyExistsOn(obj, \"locals\")\ntrue\nkeyExistsOn(obj, \"locals.\")\nfalse\nkeyExistsOn(obj, \"locals.auth\")\ntrue\nkeyExistsOn(obj, \"locals.autht\")\nfalse\nkeyExistsOn(obj, \"locals.auth.\")\nfalse\nkeyExistsOn(obj, \"locals.auth.user\")\ntrue\nkeyExistsOn(obj, \"locals.auth.userr\")\nfalse\nkeyExistsOn(obj, \"locals.auth.user.\")\nfalse\nkeyExistsOn(obj, \"locals.auth.user\")\ntrue\n
\n\n

Also see this NPM package: https://www.npmjs.com/package/has-deep-value

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90549", + "creator": "Anupam Maurya", + "createdAt": 1550573973000, + "text": "

yourArray.indexOf(yourArrayKeyName) > -1

\n
fruit = ['apple', 'grapes', 'banana']\n\nfruit.indexOf('apple') > -1\n
\n

true

\n
\n
fruit = ['apple', 'grapes', 'banana']\n\nfruit.indexOf('apple1') > -1\n
\n

false

\n
\n

for strict object keys checking:

\n
const object1 = {};\nobject1.stackoverflow = 51; \nconsole.log(object1.hasOwnProperty('stackoverflow')); \n\n\n\noutput: true \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b8082fcc3049e919c9", + "creator": "ken", + "createdAt": 1649433417000, + "text": "Those are values, not keys.", + "upvotes": 144, + "upvoterUsernames": [], + "downvotes": 144, + "downvoterUsernames": [] + }, + { + "_id": "62f324b8082fcc3049e919cb", + "creator": "Anupam Maurya", + "createdAt": 1649656749000, + "text": "use this.. @ken const object1 = {}; object1.stackoverflow = 51; console.log(object1.hasOwnProperty('stackoverflow')); // output: true", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b8082fcc3049e919cd", + "creator": "Anupam Maurya", + "createdAt": 1650449091000, + "text": "cool @ken got your point i have update here, you can check now! thankyou :)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9054b", + "creator": "Kamil Kiełczewski", + "createdAt": 1580376002000, + "text": "

In 'array' world we can look on indexes as some kind of keys. What is surprising the in operator (which is good choice for object) also works with arrays. The returned value for non-existed key is undefined

\n\n

\r\n
\r\n
let arr = [\"a\",\"b\",\"c\"]; // we have indexes: 0,1,2\r\ndelete arr[1];           // set 'empty' at index 1\r\narr.pop();               // remove last item\r\n\r\nconsole.log(0 in arr,  arr[0]);\r\nconsole.log(1 in arr,  arr[1]);\r\nconsole.log(2 in arr,  arr[2]);
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9054a", + "creator": "sarea", + "createdAt": 1566828727000, + "text": "
const object1 = {\n  a: 'something',\n  b: 'something',\n  c: 'something'\n};\n\nconst key = 's';\n\n// Object.keys(object1) will return array of the object keys ['a', 'b', 'c']\n\nObject.keys(object1).indexOf(key) === -1 ? 'the key is not there' : 'yep the key is exist';\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9054c", + "creator": "jerryurenaa", + "createdAt": 1592500405000, + "text": "

A fast and easy solution is to convert your object to json then you will be able to do this easy task:

\n
const allowed = {\n    '/login' : '',\n    '/register': '',\n    '/resetpsw': ''\n};\nconsole.log('/login' in allowed); //returns true\n
\n

If you use an array the object key will be converted to integers ex 0,1,2,3 etc. therefore, it will always be false

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9054d", + "creator": "Aditya Rewari", + "createdAt": 1614346408000, + "text": "

Optional Chaining (?.) operator can also be used for this

\n

Source: MDN/Operators/Optional_chaining

\n

\r\n
\r\n
const adventurer = {\n  name: 'Alice',\n  cat: {\n    name: 'Dinah'\n  }\n}\n\nconsole.log(adventurer.dog?.name) // undefined\nconsole.log(adventurer.cat?.name) // Dinah
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9054e", + "creator": "Gel", + "createdAt": 1618401494000, + "text": "

In my case, I wanted to check an NLP metadata returned by LUIS which is an object. I wanted to check if a key which is a string "FinancialRiskIntent" exists as a key inside that metadata object.

\n
    \n
  1. I tried to target the nested object I needed to check -> data.meta.prediction.intents (for my own purposes only, yours could be any object)
  2. \n
  3. I used below code to check if the key exists:
  4. \n
\n

\r\n
\r\n
const hasKey = 'FinancialRiskIntent' in data.meta.prediction.intents;\n\nif(hasKey) {\n  console.log('The key exists.');\n}\nelse {\n  console.log('The key does not exist.');\n}
\r\n
\r\n
\r\n

\n

This is checking for a specific key which I was initially looking for.

\n

Hope this bit helps someone.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9054f", + "creator": "Miki", + "createdAt": 1621268123000, + "text": "

Worth noting that since the introduction of ES11 you can use the nullish coalescing operator, which simplifies things a lot:

\n
const obj = {foo: 'one', bar: 'two'};\n\nconst result = obj.foo ?? "Not found";\n
\n

The code above will return "Not found" for any "falsy" values in foo. Otherwise it will return obj.foo.

\n

See Combining with the nullish coalescing operator

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90551", + "creator": "Yogesh More", + "createdAt": 1638536806000, + "text": "

JS Double Exclamation !! sign may help in this case.

\n
const cars = {\n        petrol:{\n            price: 5000\n        },\n        gas:{\n            price:8000\n        }\n    }\n
\n

Suppose we have the object above and If you try to log car with petrol price.

\n
=> console.log(cars.petrol.price);\n=> 5000\n
\n
\n

You'll definitely get 5000 out of it. But what if you try to get an\nelectric car which does not exist then you'll get undefine

\n
\n
=> console.log(cars.electric);\n=> undefine\n
\n
\n

But using !! which is its short way to cast a variable to be a\nBoolean (true or false) value.

\n
\n
=> console.log(!!cars.electric);\n=> false\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90550", + "creator": "Rupam", + "createdAt": 1635649803000, + "text": "

An alternate approach using "Reflect"

\n

As per MDN

\n
\n

Reflect is a built-in object that provides methods for interceptable\nJavaScript operations.

\n

The static Reflect.has() method works like the in operator as a\nfunction.

\n
\n

\r\n
\r\n
var obj = {\n  a: undefined,\n  b: 1,\n  c: \"hello world\"\n}\nconsole.log(Reflect.has(obj, 'a'))\nconsole.log(Reflect.has(obj, 'b'))\nconsole.log(Reflect.has(obj, 'c'))\nconsole.log(Reflect.has(obj, 'd'))
\r\n
\r\n
\r\n

\n

Should I use it ?

\n

It depends.

\n

Reflect.has() is slower than the other methods mentioned on the accepted answer (as per my benchmark test). But, if you are using it only a few times in your code, I don't see much issues with this approach.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90552", + "creator": "Nice Books", + "createdAt": 1640964659000, + "text": "

To find if a key exists in an object, use

\n

Object.keys(obj).includes(key)

\n

The ES7 includes method checks if an Array includes an item or not, & is a simpler alternative to indexOf.

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90553", + "creator": "Anupam Maurya", + "createdAt": 1649656886000, + "text": "
const rawObject = {};\nrawObject.propertyKey = 'somethingValue';\n\nconsole.log(rawObject.hasOwnProperty('somethingValue'));\n// expected output: true\n
\n

checking particular key present in given object, hasOwnProperty will works here.

\n", + "upvotes": 221, + "upvoterUsernames": [], + "downvotes": 221, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90554", + "creator": "nos nart", + "createdAt": 1650252818000, + "text": "

If you have ESLint configured in your project follows ESLint rule no-prototype-builtins. The reason why has been described in the following link:

\n
// bad\nconsole.log(object.hasOwnProperty(key));\n\n// good\nconsole.log(Object.prototype.hasOwnProperty.call(object, key));\n\n// best\nconst has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.\nconsole.log(has.call(object, key));\n/* or */\nimport has from 'has'; // https://www.npmjs.com/package/has\nconsole.log(has(object, key));\n
\n", + "upvotes": 3222, + "upvoterUsernames": [], + "downvotes": 3222, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3014106, + "uvac": 3014135 + } + }, + { + "_id": "62f321bb082fcc3049e8feca", + "title": "Sort array of objects by string property value", + "title-lowercase": "sort array of objects by string property value", + "creator": "Tyrone Slothrop", + "createdAt": 1247627867000, + "status": "open", + "text": "

I have an array of JavaScript objects:

\n\n
var objs = [ \n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n
\n\n

How can I sort them by the value of last_nom in JavaScript?

\n\n

I know about sort(a,b), but that only seems to work on strings and numbers. Do I need to add a toString() method to my objects?

\n", + "upvotes": 6610, + "upvoterUsernames": [], + "downvotes": 2859, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2601345, + "answers": 53, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e9058b", + "creator": "Burak Keceli", + "createdAt": 1376476810000, + "text": "

You may need to convert them to the lower case in order to prevent from confusion.

\n\n
objs.sort(function (a,b) {\n\nvar nameA=a.last_nom.toLowerCase(), nameB=b.last_nom.toLowerCase()\n\nif (nameA < nameB)\n  return -1;\nif (nameA > nameB)\n  return 1;\nreturn 0;  //no sorting\n\n})\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9058c", + "creator": "Jamie Mason", + "createdAt": 1398852163000, + "text": "

Example Usage:

\n\n
objs.sort(sortBy('last_nom'));\n
\n\n

Script:

\n\n
/**\n * @description\n * Returns a function which will sort an\n * array of objects by the given key.\n *\n * @param  {String}  key\n * @param  {Boolean} reverse\n * @return {Function}\n */\nconst sortBy = (key, reverse) => {\n\n  // Move smaller items towards the front\n  // or back of the array depending on if\n  // we want to sort the array in reverse\n  // order or not.\n  const moveSmaller = reverse ? 1 : -1;\n\n  // Move larger items towards the front\n  // or back of the array depending on if\n  // we want to sort the array in reverse\n  // order or not.\n  const moveLarger = reverse ? -1 : 1;\n\n  /**\n   * @param  {*} a\n   * @param  {*} b\n   * @return {Number}\n   */\n  return (a, b) => {\n    if (a[key] < b[key]) {\n      return moveSmaller;\n    }\n    if (a[key] > b[key]) {\n      return moveLarger;\n    }\n    return 0;\n  };\n};\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9058e", + "creator": "Eduardo Cuomo", + "createdAt": 1423513389000, + "text": "

Using xPrototype: https://github.com/reduardo7/xPrototype/blob/master/README.md#sortbycol1-col2-coln

\n\n
var o = [ \n  { Name: 'Lazslo', LastName: 'Jamf'     },\n  { Name: 'Pig',    LastName: 'Bodine'   },\n  { Name: 'Pirate', LastName: 'Prentice' },\n  { Name: 'Pag',    LastName: 'Bodine'   }\n];\n\n\n// Original\no.each(function (a, b) { console.log(a, b); });\n/*\n 0 Object {Name: \"Lazslo\", LastName: \"Jamf\"}\n 1 Object {Name: \"Pig\", LastName: \"Bodine\"}\n 2 Object {Name: \"Pirate\", LastName: \"Prentice\"}\n 3 Object {Name: \"Pag\", LastName: \"Bodine\"}\n*/\n\n\n// Sort By LastName ASC, Name ASC\no.sortBy('LastName', 'Name').each(function(a, b) { console.log(a, b); });\n/*\n 0 Object {Name: \"Pag\", LastName: \"Bodine\"}\n 1 Object {Name: \"Pig\", LastName: \"Bodine\"}\n 2 Object {Name: \"Lazslo\", LastName: \"Jamf\"}\n 3 Object {Name: \"Pirate\", LastName: \"Prentice\"}\n*/\n\n\n// Sort by LastName ASC and Name ASC\no.sortBy('LastName'.asc, 'Name'.asc).each(function(a, b) { console.log(a, b); });\n/*\n 0 Object {Name: \"Pag\", LastName: \"Bodine\"}\n 1 Object {Name: \"Pig\", LastName: \"Bodine\"}\n 2 Object {Name: \"Lazslo\", LastName: \"Jamf\"}\n 3 Object {Name: \"Pirate\", LastName: \"Prentice\"}\n*/\n\n\n// Sort by LastName DESC and Name DESC\no.sortBy('LastName'.desc, 'Name'.desc).each(function(a, b) { console.log(a, b); });\n/*\n 0 Object {Name: \"Pirate\", LastName: \"Prentice\"}\n 1 Object {Name: \"Lazslo\", LastName: \"Jamf\"}\n 2 Object {Name: \"Pig\", LastName: \"Bodine\"}\n 3 Object {Name: \"Pag\", LastName: \"Bodine\"}\n*/\n\n\n// Sort by LastName DESC and Name ASC\no.sortBy('LastName'.desc, 'Name'.asc).each(function(a, b) { console.log(a, b); });\n/*\n 0 Object {Name: \"Pirate\", LastName: \"Prentice\"}\n 1 Object {Name: \"Lazslo\", LastName: \"Jamf\"}\n 2 Object {Name: \"Pag\", LastName: \"Bodine\"}\n 3 Object {Name: \"Pig\", LastName: \"Bodine\"}\n*/\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9058d", + "creator": "agershun", + "createdAt": 1418900944000, + "text": "

Acording your example, you need to sort by two fields (last name, first name), rather then one. You can use Alasql library to make this sort in one line:

\n\n
var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);\n
\n\n

Try this example at jsFiddle.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9058f", + "creator": "Morteza Tourani", + "createdAt": 1435540207000, + "text": "

I Just enhanced Ege Özcan's dynamic sort to dive deep inside objects. \nIf Data looks like this:

\n\n
obj = [\n    {\n        a: { a: 1, b: 2, c: 3 },\n        b: { a: 4, b: 5, c: 6 }\n    },\n    { \n        a: { a: 3, b: 2, c: 1 },\n        b: { a: 6, b: 5, c: 4 }\n}];\n
\n\n

and if you want to sort it over a.a property I think my enhancement helps very well. I add new functionality to objects like this:

\n\n
Object.defineProperty(Object.prototype, 'deepVal', {\n    enumerable: false,\n    writable: true,\n    value: function (propertyChain) {\n        var levels = propertyChain.split('.');\n        parent = this;\n        for (var i = 0; i < levels.length; i++) {\n            if (!parent[levels[i]])\n                return undefined;\n            parent = parent[levels[i]];\n        }\n        return parent;\n    }\n});\n
\n\n

and changed _dynamicSort's return function:

\n\n
return function (a,b) {\n        var result = ((a.deepVal(property) > b.deepVal(property)) - (a.deepVal(property) < b.deepVal(property)));\n        return result * sortOrder;\n    }\n
\n\n

And now you can sort by a.a. this way:

\n\n
obj.sortBy('a.a');\n
\n\n

See Commplete script in JSFiddle

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90590", + "creator": "Evgenii", + "createdAt": 1446124188000, + "text": "
function compare(propName) {\n    return function(a,b) {\n        if (a[propName] < b[propName])\n            return -1;\n        if (a[propName] > b[propName])\n            return 1;\n        return 0;\n    };\n}\n\nobjs.sort(compare(\"last_nom\"));\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90592", + "creator": "Caio Ladislau", + "createdAt": 1452864777000, + "text": "

A simple way:

\n\n
objs.sort(function(a,b) {\n  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();\n});\n
\n\n

See that '.toLowerCase()' is necessary to prevent erros \nin comparing strings.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90591", + "creator": "Gil Epshtain", + "createdAt": 1447943277000, + "text": "

This is a simple problem, don't know why people have such complex solution.
\nA simple sort function (based on quick-sort algorithm):

\n\n
function sortObjectsArray(objectsArray, sortKey)\n        {\n            // Quick Sort:\n            var retVal;\n\n            if (1 < objectsArray.length)\n            {\n                var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // middle index\n                var pivotItem = objectsArray[pivotIndex];                    // value in the middle index\n                var less = [], more = [];\n\n                objectsArray.splice(pivotIndex, 1);                          // remove the item in the pivot position\n                objectsArray.forEach(function(value, index, array)\n                {\n                    value[sortKey] <= pivotItem[sortKey] ?                   // compare the 'sortKey' proiperty\n                        less.push(value) :\n                        more.push(value) ;\n                });\n\n                retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));\n            }\n            else\n            {\n                retVal = objectsArray;\n            }\n\n            return retVal;\n        }\n
\n\n

Use example:

\n\n
var myArr = \n        [\n            { val: 'x', idx: 3 },\n            { val: 'y', idx: 2 },\n            { val: 'z', idx: 5 },\n        ];\nmyArr = sortObjectsArray(myArr, 'idx');\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324f9082fcc3049e91a02", + "creator": "Andrew", + "createdAt": 1448318769000, + "text": "How is implementing quick sort in js a simple solution? Simple algorithm but not a simple solution.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f324f9082fcc3049e91a03", + "creator": "Roberto14", + "createdAt": 1449682580000, + "text": "Well, let me try with different words: How reinventing the wheel is a simple solution?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90593", + "creator": "Vlad Bezden", + "createdAt": 1454096677000, + "text": "

In ES6/ES2015 or later you can do this way:

\n
objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));\n
\n

Prior to ES6/ES2015

\n
objs.sort(function(a, b) {\n    return a.last_nom.localeCompare(b.last_nom)\n});\n
\n", + "upvotes": 1443, + "upvoterUsernames": [], + "downvotes": 598, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324f9082fcc3049e91a06", + "creator": "Jon Harding", + "createdAt": 1456173059000, + "text": "this has been available since JS 1.1, the fat arrow piece to this is the ES6/2015 piece. But still very useful, and best answer in my opinion", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f324f9082fcc3049e91a08", + "creator": "Vlad Bezden", + "createdAt": 1464287061000, + "text": "@PratikKelwalkar: if you need reverse it just switch a and b comparison: objs.sort((a, b) => b.last_nom.localeCompare(a.last_nom));", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f324f9082fcc3049e91a0a", + "creator": "José Antonio Postigo", + "createdAt": 1655289298000, + "text": "The simplier solution for this problem at the present time.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324f9082fcc3049e91a0c", + "creator": "gamelover42", + "createdAt": 1655920695000, + "text": "IMHO this should be the accepted answer", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90594", + "creator": "Roshni Bokade", + "createdAt": 1457430696000, + "text": "
\n

Warning!
\nUsing this solution is not recommended as it does not result in a sorted array. It is being left here for future reference, because the idea is not rare.

\n
\n
objs.sort(function(a,b){return b.last_nom>a.last_nom})\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324f9082fcc3049e91a0e", + "creator": "madprops", + "createdAt": 1487675732000, + "text": "Actually it didn't seem to work, had to use the accepted answer. It wasn't sorting correctly.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90595", + "creator": "jmwierzbicki", + "createdAt": 1458136915000, + "text": "

I came into problem of sorting array of objects, with changing priority of values, basically I want to sort array of peoples by their Age, and then by surname - or just by surname, name. \nI think that this is most simple solution compared to another answers.

\n\n

it' is used by calling sortPeoples(['array', 'of', 'properties'], reverse=false)

\n\n

\r\n
\r\n
///////////////////////example array of peoples ///////////////////////\r\n\r\nvar peoples = [\r\n    {name: \"Zach\", surname: \"Emergency\", age: 1},\r\n    {name: \"Nancy\", surname: \"Nurse\", age: 1},\r\n    {name: \"Ethel\", surname: \"Emergency\", age: 1},\r\n    {name: \"Nina\", surname: \"Nurse\", age: 42},\r\n    {name: \"Anthony\", surname: \"Emergency\", age: 42},\r\n    {name: \"Nina\", surname: \"Nurse\", age: 32},\r\n    {name: \"Ed\", surname: \"Emergency\", age: 28},\r\n    {name: \"Peter\", surname: \"Physician\", age: 58},\r\n    {name: \"Al\", surname: \"Emergency\", age: 58},\r\n    {name: \"Ruth\", surname: \"Registration\", age: 62},\r\n    {name: \"Ed\", surname: \"Emergency\", age: 38},\r\n    {name: \"Tammy\", surname: \"Triage\", age: 29},\r\n    {name: \"Alan\", surname: \"Emergency\", age: 60},\r\n    {name: \"Nina\", surname: \"Nurse\", age: 58}\r\n];\r\n\r\n\r\n\r\n//////////////////////// Sorting function /////////////////////\r\nfunction sortPeoples(propertyArr, reverse) {\r\n        function compare(a,b) {\r\n            var i=0;\r\n            while (propertyArr[i]) {\r\n                if (a[propertyArr[i]] < b[propertyArr[i]])  return -1;\r\n                if (a[propertyArr[i]] > b[propertyArr[i]])  return 1;\r\n                i++;\r\n            }\r\n            return 0;\r\n            }\r\n        peoples.sort(compare);\r\n        if (reverse){\r\n            peoples.reverse();\r\n        }\r\n    };\r\n\r\n////////////////end of sorting method///////////////\r\nfunction printPeoples(){\r\n  $('#output').html('');\r\npeoples.forEach( function(person){\r\n $('#output').append(person.surname+\" \"+person.name+\" \"+person.age+\"<br>\");\r\n} )\r\n}
\r\n
<head>\r\n<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n</head>\r\n  <html>\r\n  <body>\r\n<button onclick=\"sortPeoples(['surname']); printPeoples()\">sort by ONLY by surname ASC results in mess with same name cases</button><br>\r\n<button onclick=\"sortPeoples(['surname', 'name'], true); printPeoples()\">sort by surname then name DESC</button><br>\r\n<button onclick=\"sortPeoples(['age']); printPeoples()\">sort by AGE ASC. Same issue as in first case</button><br>\r\n<button onclick=\"sortPeoples(['age', 'surname']); printPeoples()\">sort by AGE and Surname ASC. Adding second field fixed it.</button><br>\r\n        \r\n    <div id=\"output\"></div>\r\n    </body>\r\n  </html>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fa082fcc3049e91a11", + "creator": "Penguin9", + "createdAt": 1487081879000, + "text": "array of peoples :(", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90596", + "creator": "Tero Tolonen", + "createdAt": 1462448174000, + "text": "

There are many good answers here, but I would like to point out that they can be extended very simply to achieve a lot more complex sorting. The only thing you have to do is to use the OR operator to chain comparision functions like this:

\n\n
objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )\n
\n\n

Where fn1, fn2, ... are the sort functions which return [-1,0,1]. This results in \"sorting by fn1\", \"sorting by fn2\" which is pretty much equal to ORDER BY in SQL.

\n\n

This solution is based on the behaviour of || operator which evaluates to the first evaluated expression which can be converted to true.

\n\n

The simplest form has only one inlined function like this:

\n\n
// ORDER BY last_nom\nobjs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )\n
\n\n

Having two steps with last_nom,first_nom sort order would look like this:

\n\n
// ORDER_BY last_nom, first_nom\nobjs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || \n                  a.first_nom.localeCompare(b.first_nom)  )\n
\n\n

A generic comparision function could be something like this:

\n\n
// ORDER BY <n>\nlet cmp = (a,b,n)=>a[n].localeCompare(b[n])\n
\n\n

This function could be extended to support numeric fields, case sensitity, arbitary datatypes etc.

\n\n

You can them use it with chaining them by sort priority:

\n\n
// ORDER_BY last_nom, first_nom\nobjs.sort((a,b)=> cmp(a,b, \"last_nom\") || cmp(a,b, \"first_nom\") )\n// ORDER_BY last_nom, first_nom DESC\nobjs.sort((a,b)=> cmp(a,b, \"last_nom\") || -cmp(a,b, \"first_nom\") )\n// ORDER_BY last_nom DESC, first_nom DESC\nobjs.sort((a,b)=> -cmp(a,b, \"last_nom\") || -cmp(a,b, \"first_nom\") )\n
\n\n

The point here is that pure JavaScript with functional approach can take you a long way without external libraries or complex code. It is also very effective, since no string parsing have to be done

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90597", + "creator": "ravshansbox", + "createdAt": 1466932205000, + "text": "

One more option:

\n\n
var someArray = [...];\n\nfunction generateSortFn(prop, reverse) {\n    return function (a, b) {\n        if (a[prop] < b[prop]) return reverse ? 1 : -1;\n        if (a[prop] > b[prop]) return reverse ? -1 : 1;\n        return 0;\n    };\n}\n\nsomeArray.sort(generateSortFn('name', true));\n
\n\n

sorts ascending by default.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fa082fcc3049e91a14", + "creator": "Den Kerny", + "createdAt": 1590372010000, + "text": "agreed, but in some case i haven't got needs to look at utility functions.", + "upvotes": 219, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90598", + "creator": "Luke Schoen", + "createdAt": 1468042683000, + "text": "
// Sort Array of Objects\n\n// Data\nvar booksArray = [\n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\n// Property to Sort By\nvar args = "last_nom";\n\n// Function to Sort the Data by given Property\nfunction sortByProperty(property) {\n    return function (a, b) {\n        var sortStatus = 0,\n            aProp = a[property].toLowerCase(),\n            bProp = b[property].toLowerCase();\n        if (aProp < bProp) {\n            sortStatus = -1;\n        } else if (aProp > bProp) {\n            sortStatus = 1;\n        }\n        return sortStatus;\n    };\n}\n\n// Implementation\nvar sortedArray = booksArray.sort(sortByProperty(args));\n\nconsole.log("sortedArray: " + JSON.stringify(sortedArray) );\n
\n

Console log output:

\n
"sortedArray: \n[{"first_nom":"Pig","last_nom":"Bodine"},\n{"first_nom":"Lazslo","last_nom":"Jamf"},\n{"first_nom":"Pirate","last_nom":"Prentice"}]"\n
\n

Adapted based on this source: https://web.archive.org/web/20210515081841/http://opnsrce.github.io/code-snippet-how-to-sort-an-array-of-json-objects-by-property

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059a", + "creator": "a8m", + "createdAt": 1478438306000, + "text": "

I know this question is too old, but I didn't see any implementation similar to mine.
\nThis version is based on the Schwartzian transform idiom.

\n\n
function sortByAttribute(array, ...attrs) {\n  // generate an array of predicate-objects contains\n  // property getter, and descending indicator\n  let predicates = attrs.map(pred => {\n    let descending = pred.charAt(0) === '-' ? -1 : 1;\n    pred = pred.replace(/^-/, '');\n    return {\n      getter: o => o[pred],\n      descend: descending\n    };\n  });\n  // schwartzian transform idiom implementation. aka: \"decorate-sort-undecorate\"\n  return array.map(item => {\n    return {\n      src: item,\n      compareValues: predicates.map(predicate => predicate.getter(item))\n    };\n  })\n  .sort((o1, o2) => {\n    let i = -1, result = 0;\n    while (++i < predicates.length) {\n      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;\n      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;\n      if (result *= predicates[i].descend) break;\n    }\n    return result;\n  })\n  .map(item => item.src);\n}\n
\n\n

Here's an example how to use it:

\n\n
let games = [\n  { name: 'Mashraki',          rating: 4.21 },\n  { name: 'Hill Climb Racing', rating: 3.88 },\n  { name: 'Angry Birds Space', rating: 3.88 },\n  { name: 'Badland',           rating: 4.33 }\n];\n\n// sort by one attribute\nconsole.log(sortByAttribute(games, 'name'));\n// sort by mupltiple attributes\nconsole.log(sortByAttribute(games, '-rating', 'name'));\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90599", + "creator": "depiction", + "createdAt": 1473385726000, + "text": "

This will sort a two level nested array by the property passed to it in alpha numeric order.

\n\n
function sortArrayObjectsByPropAlphaNum(property) {\n    return function (a,b) {\n        var reA = /[^a-zA-Z]/g;\n        var reN = /[^0-9]/g;\n        var aA = a[property].replace(reA, '');\n        var bA = b[property].replace(reA, '');\n\n        if(aA === bA) {\n            var aN = parseInt(a[property].replace(reN, ''), 10);\n            var bN = parseInt(b[property].replace(reN, ''), 10);\n            return aN === bN ? 0 : aN > bN ? 1 : -1;\n        } else {\n            return a[property] > b[property] ? 1 : -1;\n        }\n    };\n}\n
\n\n

Usage:

\n\n
objs.sort(utils.sortArrayObjectsByPropAlphaNum('last_nom'));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059b", + "creator": "Partha Roy", + "createdAt": 1481295333000, + "text": "

So here is one sorting algorithm which can sort in any order , throughout array of any kind of objects , without the restriction of datatype comparison ( i.e. Number , String )

\n\n
function smoothSort(items,prop,reverse) {  \n    var length = items.length;\n    for (var i = (length - 1); i >= 0; i--) {\n        //Number of passes\n        for (var j = (length - i); j > 0; j--) {\n            //Compare the adjacent positions\n            if(reverse){\n              if (items[j][prop] > items[j - 1][prop]) {\n                //Swap the numbers\n                var tmp = items[j];\n                items[j] = items[j - 1];\n                items[j - 1] = tmp;\n            }\n            }\n\n            if(!reverse){\n              if (items[j][prop] < items[j - 1][prop]) {\n                  //Swap the numbers\n                  var tmp = items[j];\n                  items[j] = items[j - 1];\n                  items[j - 1] = tmp;\n              }\n            }\n        }\n    }\n\n    return items;\n}\n
\n\n\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059c", + "creator": "Sridhar Sg", + "createdAt": 1499240635000, + "text": "

Using Ramda,

\n\n

npm install ramda

\n\n
import R from 'ramda'\nvar objs = [ \n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\nvar ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)\nvar descendingSortedObjs = R.reverse(ascendingSortedObjs)\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059e", + "creator": "Nico Van Belle", + "createdAt": 1504102544000, + "text": "

Lodash.js (superset of Underscore.js)

\n\n

It's good not to add a framework for every simple piece of logic, but relying on well tested utility frameworks can speed up development and reduce the amount of bugs.

\n\n

Lodash produces very clean code and promotes a more functional programming style. In one glimpse it becomes clear what the intent of the code is.

\n\n

OP's issue can simply be solved as:

\n\n
const sortedObjs = _.sortBy(objs, 'last_nom');\n
\n\n

More info? E.g. we have following nested object:

\n\n
const users = [\n  { 'user': {'name':'fred', 'age': 48}},\n  { 'user': {'name':'barney', 'age': 36 }},\n  { 'user': {'name':'wilma'}},\n  { 'user': {'name':'betty', 'age': 32}}\n];\n
\n\n

We now can use the _.property shorthand user.age to specify the path to the property that should be matched. We will sort the user objects by the nested age property. Yes, it allows for nested property matching!

\n\n
const sortedObjs = _.sortBy(users, ['user.age']);\n
\n\n

Want it reversed? No problem. Use _.reverse.

\n\n
const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));\n
\n\n

Want to combine both using chain?

\n\n
const { chain } = require('lodash');\nconst sortedObjs = chain(users).sortBy('user.age').reverse().value();\n
\n\n

Or when do you prefer flow over chain

\n\n
const { flow, reverse, sortBy } = require('lodash/fp');\nconst sortedObjs = flow([sortBy('user.age'), reverse])(users); \n
\n", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059d", + "creator": "sg28", + "createdAt": 1503829602000, + "text": "

I will give you a solution implementing selectionSort algorithm ,it is simple and effective

\n\n
var objs = [ \n{ first_nom: 'Lazslo', last_nom: 'Jamf'     },\n{ first_nom: 'Pig',    last_nom: 'Bodine'   },\n{ first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\n\nfunction selection_Sort(num) { \n //console.log(num);  \n var temp, index;\n for (var i = 0; i <= num.length - 1; i++) {\nindex = i;\nfor (var j = i + 1; j <= num.length - 1; j++) {\n // you can use first_nom/last_nom,any way you choose to sort\n\n  if (num[j]. last_nom < num[index]. last_nom) {\n    index = j;\n  } \n}\n\n//below is the swapping part\ntemp = num[i]. last_nom;\nnum[i]. last_nom = num[index]. last_nom;\nnum[index]. last_nom = temp;\n };\n console.log(num); \n return num; \n  }\n  selection_Sort(objs);\n
\n\n

Great to see such great answers

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a0", + "creator": "Bob Stein", + "createdAt": 1519433696000, + "text": "

Given the original example:

\n\n
var objs = [ \n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n
\n\n

Sort by multiple fields:

\n\n
objs.sort(function(left, right) {\n    var last_nom_order = left.last_nom.localeCompare(right.last_nom);\n    var first_nom_order = left.first_nom.localeCompare(right.first_nom);\n    return last_nom_order || first_nom_order;\n});\n
\n\n

Notes

\n\n\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9059f", + "creator": "Damian Pavlica", + "createdAt": 1508501440000, + "text": "

Old answer that is not correct:

\n\n
arr.sort((a, b) => a.name > b.name)\n
\n\n

UPDATE

\n\n

From Beauchamp's comment:

\n\n
arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))\n
\n\n

More readable format:

\n\n
arr.sort((a, b) => {\n  if (a.name < b.name) return -1\n  return a.name > b.name ? 1 : 0\n})\n
\n\n

Without nested ternaries:

\n\n
arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))\n
\n\n

Explanation: Number() will cast true to 1 and false to 0.

\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fb082fcc3049e91a1e", + "creator": "TitanFighter", + "createdAt": 1517687705000, + "text": "It works, but the result is unstable for some reason", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fb082fcc3049e91a20", + "creator": "Patrick Roberts", + "createdAt": 1531905784000, + "text": "@AO17 no it won't. You can't subtract strings.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fb082fcc3049e91a22", + "creator": "Jean-François Beauchamp", + "createdAt": 1535556801000, + "text": "This should do it: arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f324fb082fcc3049e91a24", + "creator": "Ahsan", + "createdAt": 1548164966000, + "text": "@Jean-FrançoisBeauchamp, your solution works perfectly fine and much better.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324fb082fcc3049e91a26", + "creator": "Kojo", + "createdAt": 1579106639000, + "text": "the 3rd one with Number is straightforward and nice !", + "upvotes": 346, + "upvoterUsernames": [], + "downvotes": 346, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905a1", + "creator": "karthik006", + "createdAt": 1522042806000, + "text": "

Using lodash or Underscore, its a piece of cake

\n\n
> const sortedList = _.orderBy(objs, [last_nom], [asc]); // asc or desc\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a2", + "creator": "Francois Girard", + "createdAt": 1527537289000, + "text": "

A simple function that sort an array of object by a property

\n\n
function sortArray(array, property, direction) {\n    direction = direction || 1;\n    array.sort(function compare(a, b) {\n        let comparison = 0;\n        if (a[property] > b[property]) {\n            comparison = 1 * direction;\n        } else if (a[property] < b[property]) {\n            comparison = -1 * direction;\n        }\n        return comparison;\n    });\n    return array; // Chainable\n}\n
\n\n

Usage:

\n\n
var objs = [ \n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\nsortArray(objs, \"last_nom\"); // Asc\nsortArray(objs, \"last_nom\", -1); // Desc\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fb082fcc3049e91a2a", + "creator": "manjuvreddy", + "createdAt": 1584984208000, + "text": "This solution worked perfectly for me to sort bi-directional. Thank u", + "upvotes": 6918, + "upvoterUsernames": [], + "downvotes": 6918, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905a3", + "creator": "Harun Or Rashid", + "createdAt": 1527672079000, + "text": "

Way 1 :

\n\n

You can use Underscore.js. Import underscore first.

\n\n
 import * as _ from 'underscore';\n let SortedObjs = _.sortBy(objs, 'last_nom');\n
\n\n

Way 2 : Use compare function.

\n\n
function compare(first, second) {\n     if (first.last_nom < second.last_nom)\n         return -1;\n     if (first.last_nom > second.last_nom)\n       return 1;\n    return 0;\n }\n\nobjs.sort(compare);\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a4", + "creator": "0leg", + "createdAt": 1528125876000, + "text": "

As of 2018 there is a much shorter and elegant solution. Just use. Array.prototype.sort().

\n\n

Example:

\n\n
var items = [\n  { name: 'Edward', value: 21 },\n  { name: 'Sharpe', value: 37 },\n  { name: 'And', value: 45 },\n  { name: 'The', value: -12 },\n  { name: 'Magnetic', value: 13 },\n  { name: 'Zeros', value: 37 }\n];\n\n// sort by value\nitems.sort(function (a, b) {\n  return a.value - b.value;\n});\n
\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a5", + "creator": "chandan gupta", + "createdAt": 1530789261000, + "text": "
\n

It works for me. Here It will keep undefined to the end.

\n
\n\n

\r\n
\r\n
 function sort(items, property, direction) {\r\n\r\n    function compare(a, b) {\r\n      if(!a[property] && !b[property]) {\r\n        return 0;\r\n      } else if(a[property] && !b[property]) {\r\n        return -1;\r\n      } else if(!a[property] && b[property]) {\r\n        return 1;\r\n      } else {\r\n        const value1 = a[property].toString().toUpperCase(); // ignore upper and lowercase\r\n        const value2 = b[property].toString().toUpperCase(); // ignore upper and lowercase\r\n        if (value1 < value2) {\r\n          return direction === 0 ? -1 : 1;\r\n        } else if (value1 > value2) {\r\n          return direction === 0 ? 1 : -1;\r\n        } else {\r\n          return 0;\r\n        }\r\n        \r\n      }\r\n    }\r\n    \r\n    return items.sort(compare);\r\n   } \r\n   \r\n   var items = [\r\n  { name: 'Edward', value: 21 },\r\n  { name: 'Sharpe', value: 37 },\r\n  { name: 'And', value: 45 },\r\n  { name: 'The', value: -12 },\r\n  { name: undefined, value: -12 },\r\n  { name: 'Magnetic', value: 13 },\r\n  { name: 'Zeros', value: 37 }\r\n];\r\n   console.log('Ascending Order:- ');\r\n   console.log(sort(items, 'name', 0));\r\n   console.log('Decending Order:- ');\r\n   console.log(sort(items, 'name', 1));\r\n    \r\n    
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a6", + "creator": "Patrick Roberts", + "createdAt": 1531907593000, + "text": "

I haven't seen this particular approach suggested, so here's a terse comparison method I like to use that works for both string and number types:

\n

\r\n
\r\n
const objs = [ \n  { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n  { first_nom: 'Pig',    last_nom: 'Bodine'   },\n  { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\nconst sortBy = fn => {\n  const cmp = (a, b) => -(a < b) || +(a > b);\n  return (a, b) => cmp(fn(a), fn(b));\n};\n\nconst getLastName = o => o.last_nom;\nconst sortByLastName = sortBy(getLastName);\n\nobjs.sort(sortByLastName);\nconsole.log(objs.map(getLastName));
\r\n
\r\n
\r\n

\n

Explanation of sortBy()

\n

sortBy() accepts a fn that selects a value from an object to use in comparison, and returns a function that can be passed to Array.prototype.sort(). In this example, we're comparing o.last_nom. Whenever we receive two objects such as

\n
a = { first_nom: 'Lazslo', last_nom: 'Jamf' }\nb = { first_nom: 'Pig', last_nom: 'Bodine' }\n
\n

we compare them with (a, b) => cmp(fn(a), fn(b)). Given that

\n
fn = o => o.last_nom\n
\n

we can expand the comparison function to (a, b) => cmp(a.last_nom, b.last_nom). Because of the way logical OR (||) works in JavaScript, cmp(a.last_nom, b.last_nom) is equivalent to

\n
if (a.last_nom < b.last_nom) return -1;\nif (a.last_nom > b.last_nom) return 1;\nreturn 0;\n
\n

Incidentally, this is called the three-way comparison "spaceship" (<=>) operator in other languages.

\n

Finally, here's the ES5-compatible syntax without using arrow functions:

\n

\r\n
\r\n
var objs = [ \n  { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n  { first_nom: 'Pig',    last_nom: 'Bodine'   },\n  { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\nfunction sortBy(fn) {\n  function cmp(a, b) { return -(a < b) || +(a > b); }\n  return function (a, b) { return cmp(fn(a), fn(b)); };\n}\n\nfunction getLastName(o) { return o.last_nom; }\nvar sortByLastName = sortBy(getLastName);\n\nobjs.sort(sortByLastName);\nconsole.log(objs.map(getLastName));
\r\n
\r\n
\r\n

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a8", + "creator": "Mas", + "createdAt": 1552759935000, + "text": "

In case you have nested objects

\n\n
const objs = [{\n        first_nom: 'Lazslo',\n        last_nom: 'Jamf',\n        moreDetails: {\n            age: 20\n        }\n    }, {\n        first_nom: 'Pig',\n        last_nom: 'Bodine',\n        moreDetails: {\n            age: 21\n        }\n    }, {\n        first_nom: 'Pirate',\n        last_nom: 'Prentice',\n        moreDetails: {\n            age: 22\n        }\n    }];\n\nnestedSort = (prop1, prop2 = null, direction = 'asc') => (e1, e2) => {\n        const a = prop2 ? e1[prop1][prop2] : e1[prop1],\n            b = prop2 ? e2[prop1][prop2] : e2[prop1],\n            sortOrder = direction === \"asc\" ? 1 : -1\n        return (a < b) ? -sortOrder : (a > b) ? sortOrder : 0;\n    }\n
\n\n

and call it like

\n\n
objs.sort(nestedSort(\"last_nom\"));\nobjs.sort(nestedSort(\"last_nom\", null, \"desc\"));\nobjs.sort(nestedSort(\"moreDetails\", \"age\"));\nobjs.sort(nestedSort(\"moreDetails\", \"age\", \"desc\"));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a7", + "creator": "Harshal Y.", + "createdAt": 1535035140000, + "text": "

You can use\nEasiest Way: Lodash

\n

(https://lodash.com/docs/4.17.10#orderBy)

\n

This method is like _.sortBy except that it allows specifying the sort orders of the iteratees to sort by. If orders is unspecified, all values are sorted in ascending order. Otherwise, specify an order of "desc" for descending or "asc" for ascending sort order of corresponding values.

\n

Arguments

\n

collection (Array|Object): The collection to iterate over.\n[iteratees=[_.identity]] (Array[]|Function[]|Object[]|string[]): The iteratees to sort by.\n[orders] (string[]): The sort orders of iteratees.

\n

Returns

\n

(Array): Returns the new sorted array.

\n
\n
var _ = require('lodash');\nvar homes = [\n    {"h_id":"3",\n     "city":"Dallas",\n     "state":"TX",\n     "zip":"75201",\n     "price":"162500"},\n    {"h_id":"4",\n     "city":"Bevery Hills",\n     "state":"CA",\n     "zip":"90210",\n     "price":"319250"},\n    {"h_id":"6",\n     "city":"Dallas",\n     "state":"TX",\n     "zip":"75000",\n     "price":"556699"},\n    {"h_id":"5",\n     "city":"New York",\n     "state":"NY",\n     "zip":"00010",\n     "price":"962500"}\n    ];\n    \n_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905a9", + "creator": "Ferrybig", + "createdAt": 1557236312000, + "text": "

It is also possible to make a dynamic sorting function when programming in TypeScript, but the types become more tricky in this case.

\n\n\n\n
function sortByKey<O>(key: keyof O, decending: boolean = false): (a: O, b: O) => number {\n    const order = decending ? -1 : 1;\n    return (a, b): number => {\n        const valA = a[key];\n        const valB = b[key];\n        if (valA < valB) {\n            return -order;\n        } else if (valA > valB) {\n            return order;\n        } else {\n            return 0;\n        }\n    }\n}\n
\n\n

This can be used in TypeScript as the following:

\n\n\n\n
const test = [\n    {\n        id: 0,\n    },\n    {\n        id: 2,\n    }\n]\n\ntest.sort(sortByKey('id')) // OK\ntest.sort(sortByKey('id1')) // ERROR\ntest.sort(sortByKey('')) // ERROR\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905aa", + "creator": "Jadli", + "createdAt": 1574162133000, + "text": "

this sorting funciton can be use for all object sorting,

\n\n\n\n

you can also do assending or desending sort by passing 1,-1 as param

\n\n

\r\n
\r\n
Object.defineProperty(Object.prototype, 'deepVal', {\r\n    enumerable: false,\r\n    writable: true,\r\n    value: function (propertyChain) {\r\n        var levels = propertyChain.split('.');\r\n        parent = this;\r\n        for (var i = 0; i < levels.length; i++) {\r\n            if (!parent[levels[i]])\r\n                return undefined;\r\n            parent = parent[levels[i]];\r\n        }\r\n        return parent;\r\n    }\r\n});\r\n\r\n\r\nfunction dynamicSortAll(property,sortOrders=1) {\r\n\r\n/**default sorting will be ascending order if you need descending order\r\nsording you have to pass -1 as param**/\r\n\r\n    var sortOrder = sortOrders;\r\n   \r\n    return function (a,b) {\r\n\r\n\t\t var result =(property? ((a.deepVal(property) > b.deepVal(property)) ? 1 : (a.deepVal(property) < b.deepVal(property)) ? -1 : 0) :((a > b) ? 1 : (a < b) ? -1 : 0))\r\n\t\t\r\n        return result * sortOrder;\r\n\t\t\r\n   \r\n    }\r\n}\r\n\r\ndeepObj = [\r\n    {\r\n        a: { a: 1, b: 2, c: 3 },\r\n        b: { a: 4, b: 5, c: 6 }\r\n    },\r\n    { \r\n        a: { a: 3, b: 2, c: 1 },\r\n        b: { a: 6, b: 5, c: 4 }\r\n}];\r\n\r\nlet deepobjResult=deepObj.sort(dynamicSortAll('a.a',1))\r\nconsole.log('deepobjResult :'+ JSON.stringify(deepobjResult))\r\nvar obj = [ \r\n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\r\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\r\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\r\n];\r\nlet objResult=obj.sort(dynamicSortAll('last_nom',1))\r\nconsole.log('objResult :'+ JSON.stringify(objResult))\r\n\r\nvar numericObj=[1,2,3,4,5,6]\r\n\r\nlet numResult=numericObj.sort(dynamicSortAll(null,-1))\r\nconsole.log('numResult :'+ JSON.stringify(numResult))\r\n\r\nlet stringSortResult='helloworld'.split('').sort(dynamicSortAll(null,1))\r\n\r\n console.log('stringSortResult:'+ JSON.stringify(stringSortResult))\r\n\r\nlet uniqueStringOrger=[...new Set(stringSortResult)]; \r\n console.log('uniqueStringOrger:'+ JSON.stringify(uniqueStringOrger))
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905ab", + "creator": "SwiftNinjaPro", + "createdAt": 1574647418000, + "text": "

Hers a function you can use to sort the list by multiple objects, where if the first object is equal, the second order will be used as a fallback. empty values should also be ignored to fallback order if possible.

\n\n
function sortObjects(list, orderBy){\n    list.sort(function(a, b){\n        let byIndex = 0;\n        let order = orderBy[byIndex];\n        while(!a[order.by] || !b[order.by] || a[order.by] === b[order.by]){\n            byIndex++;\n            if(byIndex >= orderBy.length){break;}\n            order = orderBy[byIndex];\n        }\n        if(!a[order.by] || !b[order.by] || a[order.by] === b[order.by]){\n            return false;\n        }\n        if(order.desc){\n            return a[order.by] < b[order.by];\n        }\n        return a[order.by] > b[order.by];\n    });\n    return list;\n}\n
\n\n

usage:

\n\n
var objs = [\n    {a: 10, b: 20, c: 30},\n    {a: 30, b: 10, c: 20},\n    {a: 20, b: 10, c: 30},\n];\n\nsortObjectList(objs, [{by: 'a'}]);\n[\n    {a: 10, b: 20, c: 30},\n    {a: 20, b: 10, c: 30},\n    {a: 30, b: 10, c: 20},\n]\n\nsortObjectList(objs, [{by: 'a', desc: true}]);\n[\n    {a: 30, b: 10, c: 20},\n    {a: 20, b: 10, c: 30},\n    {a: 10, b: 20, c: 30},\n]\n\nsortObjectList(objs, [{by: 'b', desc: true}, {by: 'c'}]);\n[\n    {a: 10, b: 20, c: 30},\n    {a: 30, b: 10, c: 20},\n    {a: 20, b: 10, c: 30},\n]\n
\n\n

another example:

\n\n
var objs = [\n    {a: 5, b: 5},\n    {a: 10, b: 15},\n    {a: 15, b: 25},\n    {b: 10},\n    {b: 20},\n    {a: 10, b: 30},\n    {a: 10, b: 12},\n];\n\nsortObjectList(objs, [{by: 'a'}, {by: 'b'}]);\n[\n    {a: 5, b: 5},\n    {b: 10},\n    {a: 10, b: 12},\n    {a: 10, b: 15},\n    {b: 20},\n    {a: 10, b: 30},\n    {a: 15, b: 25},\n]\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905ac", + "creator": "cbdeveloper", + "createdAt": 1576835347000, + "text": "

This is my take on this:

\n

The order parameter is optional and defaults to "ASC" for ascending order.

\n

Works on accented chars and it's case insensitive.

\n

NOTE: It sorts and returns the ORIGINAL array.

\n
function sanitizeToSort(str) {\n  return str\n    .normalize('NFD')                   // REMOVE ACCENTED AND DIACRITICS\n    .replace(/[\\u0300-\\u036f]/g,'')     // REMOVE ACCENTED AND DIACRITICS\n    .toLowerCase()                      // SORT WILL BE CASE INSENSITIVE\n  ;\n}\n\nfunction sortByProperty(arr, property, order="ASC") {\n  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));\n  arr.sort((a,b) => order === "ASC" ?\n      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0\n    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0\n  );\n  arr.forEach((item) => delete item.tempProp);\n  return arr;\n}\n
\n

SNIPPET

\n

\r\n
\r\n
function sanitizeToSort(str) {\n  return str\n    .normalize('NFD')                   // REMOVE ACCENTED CHARS\n    .replace(/[\\u0300-\\u036f]/g,'')     // REMOVE DIACRITICS\n    .toLowerCase()\n  ;\n}\n\nfunction sortByProperty(arr, property, order=\"ASC\") {\n  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));\n  arr.sort((a,b) => order === \"ASC\" ?\n      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0\n    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0\n  );\n  arr.forEach((item) => delete item.tempProp);\n  return arr;\n}\n\nconst rockStars = [\n  { name: \"Axl\",\n    lastname: \"Rose\" },\n  { name: \"Elthon\",\n    lastname: \"John\" },\n  { name: \"Paul\",\n    lastname: \"McCartney\" },\n  { name: \"Lou\",\n    lastname: \"Reed\" },\n  { name: \"freddie\",             // WORKS ON LOWER/UPPER CASE\n    lastname: \"mercury\" },\n  { name: \"Ámy\",                 // WORKS ON ACCENTED CHARS TOO\n    lastname: \"winehouse\"}\n  \n];\n\nsortByProperty(rockStars,\"name\");\n\nconsole.log(\"Ordered by name A-Z:\");\nrockStars.forEach((item) => console.log(item.name + \" \" + item.lastname));\n\nsortByProperty(rockStars,\"lastname\",\"DESC\");\n\nconsole.log(\"\\nOrdered by lastname Z-A:\");\nrockStars.forEach((item) => console.log(item.lastname + \", \" + item.name));
\r\n
\r\n
\r\n

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fc082fcc3049e91a32", + "creator": "Ankesh Pandey", + "createdAt": 1598539049000, + "text": "not working if the list contain name in the combination of uppercase and lowercase character", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f324fc082fcc3049e91a34", + "creator": "cbdeveloper", + "createdAt": 1599466229000, + "text": "@AnkeshPandey Thanks for pointing that out. I've fixed it.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ad", + "creator": "Abhishek", + "createdAt": 1583225427000, + "text": "

Try this,

\n\n
UPTO ES5\n\n//Ascending Sort\nitems.sort(function (a, b) {\n   return a.value - b.value;\n});\n\n\n//Descending Sort\nitems.sort(function (a, b) {\n   return b.value - a.value;\n});\n\n\nIN ES6 & above:\n\n// Ascending sort\nitems.sort((a, b) => a.value - b.value);\n\n// Descending Sort\n items.sort((a, b) => b.value - a.value);\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fc082fcc3049e91a36", + "creator": "Omar Hasan", + "createdAt": 1584350491000, + "text": "best and easy solution", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f324fc082fcc3049e91a38", + "creator": "Thorvald", + "createdAt": 1648765070000, + "text": "Doesn't work for me, tried other solutions that does indeed work but this one doesn't. Attempted to sort by strings.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ae", + "creator": "Nur", + "createdAt": 1586377748000, + "text": "

You can use a reusable sort function.

\n
Array.prototype.order = function (prop, methods = {}) {\n    if (prop?.constructor == Object) {\n        methods = prop;\n        prop = null;\n    }\n    const [orderType_a, orderType_b] = methods.reverse ? [1, -1] : [-1, 1];\n\n    const $ = x => prop\n        ? methods.insensitive\n            ? String(x[prop]).toLowerCase()\n            : x[prop]\n        : methods.insensitive\n            ? String(x).toLowerCase()\n            : x;\n\n    const fn = (a, b) => $(a) < $(b) ? orderType_a : $(b) < $(a) ? orderType_b : 0;\n    return this.sort(fn);\n};\n
\n

Its can be use to sort both Array and Object in array
.

\n
let items = [{ x: "Z" }, 3, "1", "0", 2, { x: "a" }, { x: 0 }];\nitems\n    .order("x", { insensitive: 1 })\n    // [ { x: 0 }, { x: 'a' }, 3, '1', '0', 2, { x: 'Z' } ]\n    .order({ reverse: 1 })\n    // [ { x: 0 }, { x: 'a' }, 3, 2, { x: 'Z' }, '1', '0' ]\n    .sort(x => typeof x == "string" || typeof x == "number" ? -1 : 0)\n    // [ '0', '1', 2, 3, { x: 0 }, { x: 'a' }, { x: 'Z' } ]\n
\n

1nd (optional) > to sort object contain in array.
\n2rd is method > { reverse: any, insensitive: any }

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905af", + "creator": "SeyyedKhandon", + "createdAt": 1607072713000, + "text": "

Simple answer:

\n
objs.sort((a,b)=>a.last_nom.localeCompare(b.last_nom))\n
\n

Details:

\n

Today it is very simple, You can compare strings with localeCompare. As the Mozilla Doc says:

\n
\n

The localeCompare() method returns a number indicating whether a\nreference string comes before, or after, or is the same as the given string in sort order.

\n
\n
    //example1:\n    console.log("aaa".localeCompare("aab")); //-1\n    console.log("aaa".localeCompare("aaa")); //0\n    console.log("aab".localeCompare("aaa")); //1\n
\n
\n    //example2:\n    const a = 'réservé'; // with accents, lowercase\n    const b = 'RESERVE'; // no accents, uppercase\n\n    console.log(a.localeCompare(b));\n    // expected output: 1\n    console.log(a.localeCompare(b, 'en', { sensitivity: 'base' }));\n    // expected output: 0\n
\n

For more details see Mozilla doclocaleCompare:

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b0", + "creator": "Marinos An", + "createdAt": 1611782893000, + "text": "

For fp-holics:

\n
const objectSorter = (p)=>(a,b)=>((a,b)=>a>b?1:a<b?-1:0)(a[p], b[p]);\nobjs.sort(objectSorter('first_nom'));\n\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b2", + "creator": "Wallace Sidhrée", + "createdAt": 1616147676000, + "text": "

I've been using this utility in a variety of projects and it works great. It's very modular too:

\n\n

sortArrayOfObjsByKeyUtil.js

\n
// Sort array of objects by key\n// ------------------------------------------------------------\nconst sortArrayOfObjsByKey = (array, key, ascdesc) =>\n  array.sort((a, b) => {\n    const x = a[key];\n    const y = b[key];\n    if (ascdesc === 'asc') {\n      return x < y ? -1 : x > y ? 1 : 0;\n    }\n    if (ascdesc === 'desc') {\n      return x > y ? -1 : x < y ? 1 : 0;\n    }\n    return null;\n  });\n\n
\n

sortArrayOfObjsByKeyUtil.test.js

\n
import sortArrayOfObjsByKey from './sortArrayOfObjsByKeyUtil';\n\nconst unsortedArray = [\n  {\n    _id: '3df55221-ce5c-4147-8e14-32effede6133',\n    title: 'Netlife Design',\n    address: {\n      PostalAddress: {\n        streetAddress: 'Youngstorget 3',\n        addressLocality: 'Oslo',\n        addressRegion: null,\n        postalCode: '0181',\n        addressCountry: 'Norway',\n      },\n    },\n    geopoint: { lat: 59.914322, lng: 10.749272 },\n  },\n  {\n    _id: 'cd00459f-3755-49f1-8847-66591ef935b2',\n    title: 'Home',\n    address: {\n      PostalAddress: {\n        streetAddress: 'Stockfleths gate 58A',\n        addressLocality: 'Oslo',\n        addressRegion: null,\n        postalCode: '0461',\n        addressCountry: 'Norway',\n      },\n    },\n    geopoint: { lat: 59.937316, lng: 10.751862 },\n  },\n];\n\nconst sortedArray = [\n  {\n    _id: 'cd00459f-3755-49f1-8847-66591ef935b2',\n    title: 'Home',\n    address: {\n      PostalAddress: {\n        streetAddress: 'Stockfleths gate 58A',\n        addressLocality: 'Oslo',\n        addressRegion: null,\n        postalCode: '0461',\n        addressCountry: 'Norway',\n      },\n    },\n    geopoint: { lat: 59.937316, lng: 10.751862 },\n  },\n  {\n    _id: '3df55221-ce5c-4147-8e14-32effede6133',\n    title: 'Netlife Design',\n    address: {\n      PostalAddress: {\n        streetAddress: 'Youngstorget 3',\n        addressLocality: 'Oslo',\n        addressRegion: null,\n        postalCode: '0181',\n        addressCountry: 'Norway',\n      },\n    },\n    geopoint: { lat: 59.914322, lng: 10.749272 },\n  },\n];\n\ndescribe('sortArrayOfObjsByKey', () => {\n  it(`sort array by 'title' key, ascending`, () => {\n    const testInput = sortArrayOfObjsByKey(unsortedArray, 'title', 'asc');\n    const testOutput = sortedArray;\n    expect(testInput).toEqual(testOutput);\n  });\n});\n\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b1", + "creator": "Satish Chandra Gupta", + "createdAt": 1612339981000, + "text": "

Use JavaScript sort method

\n

The sort method can be modified to sort anything like an array of numbers, strings and even objects using a compare function.

\n

A compare function is passed as an optional argument to the sort method.

\n

This compare function accepts 2 arguments generally called a and b. Based on these 2 arguments you can modify the sort method to work as you want.

\n
    \n
  1. If the compare function returns less than 0, then the sort() method sorts a at a lower index than b. Simply a will come before b.
  2. \n
  3. If the compare function returns equal to 0, then the sort() method leaves the element positions as they are.
  4. \n
  5. If the compare function returns greater than 0, then the sort() method sorts a at greater index than b. Simply a will come after b.
  6. \n
\n

Use the above concept to apply on your object where a will be your object property.

\n

\r\n
\r\n
var objs = [\n  { first_nom: 'Lazslo', last_nom: 'Jamf' },\n  { first_nom: 'Pig', last_nom: 'Bodine' },\n  { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\nfunction compare(a, b) {\n  if (a.last_nom > b.last_nom) return 1;\n  if (a.last_nom < b.last_nom) return -1;\n  return 0;\n}\nobjs.sort(compare);\nconsole.log(objs)\n// for better look use console.table(objs)
\r\n
\r\n
\r\n\n\"output\"

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b4", + "creator": "Force Bolt", + "createdAt": 1622300510000, + "text": "
//Try this way\n\n\nlet objs = [ \n        { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n        { first_nom: 'Pig',    last_nom: 'Bodine'   },\n        { first_nom: 'Pirate', last_nom: 'Prentice' }\n    ];\n    const compareBylastNom = (a, b) => {\n        // converting to uppercase to have case-insensitive comparison\n        const name1 = a.last_nom.toUpperCase();\n        const name2 = b.last_nom.toUpperCase();\n    \n        let comparison = 0;\n    \n        if (name1 > name2) {\n            comparison = 1;\n        } else if (name1 < name2) {\n            comparison = -1;\n        }\n        return comparison;\n    }\n    console.log(objs.sort(compareBylastNom));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b3", + "creator": "artem", + "createdAt": 1617973088000, + "text": "

Why don't you write short code?

\n
objs.sort((a, b) => a.last_nom > b.last_nom ? 1 : -1)\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a3f", + "creator": "Someone Special", + "createdAt": 1618479365000, + "text": "what if values are equal? considering there are 3 values you can return - 1, -1, 0", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a41", + "creator": "artem", + "createdAt": 1618485307000, + "text": "@SomeoneSpecial so what? The result will be the same", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a42", + "creator": "Kaleem Elahi", + "createdAt": 1624730002000, + "text": "What does 1 || -1 mean ?", + "upvotes": 454, + "upvoterUsernames": [], + "downvotes": 454, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a44", + "creator": "Rustam", + "createdAt": 1642252822000, + "text": "I think it can be simplfied to a objs.sort((a, b) => a.last_nom > b.last_nom || -1). No need for && 1", + "upvotes": 3729, + "upvoterUsernames": [], + "downvotes": 3729, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905b6", + "creator": "Rustam", + "createdAt": 1642252470000, + "text": "

I know there is already plenty of answers, including those with localeCompare ones, but if you don't want to/can't use localeCompare for some reason, I would suggest you to use this solution instead of ternary operator solution:

\n
objects.sort((a, b) => (a.name > b.name) - (a.name < b.name));\n
\n

Someone could say that it's not obvious what this code is doing, but in my opinion ternary operator is worse. If one ternary operator is readable enough, two ternary operators one embedded into another — really hard to read and ugly. One-line code with just two comparison operators and one minus operator is very simple to read and thus to reason.

\n", + "upvotes": 2147, + "upvoterUsernames": [], + "downvotes": 2147, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905b5", + "creator": "muasif80", + "createdAt": 1623784502000, + "text": "

Case sensitive

\n
arr.sort((a, b) => a.name > b.name ? 1 : -1);\n
\n

Case Insensitive

\n
arr.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);\n
\n

Useful Note

\n

If no change in order (in case of the same strings) then the condition > will fail and -1 will be returned. But if strings are same then returning 1 or -1 will result in correct output

\n

The other option could be to use >= operator instead of >

\n
\n

\r\n
\r\n
var objs = [ \n    { first_nom: 'Lazslo', last_nom: 'Jamf'     },\n    { first_nom: 'Pig',    last_nom: 'Bodine'   },\n    { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\n\n// Define a couple of sorting callback functions, one with hardcoded sort key and the other with an argument sort key\nconst sorter1 = (a, b) => a.last_nom.toLowerCase() > b.last_nom.toLowerCase() ? 1 : -1;\nconst sorter2 = (sortBy) => (a, b) => a[sortBy].toLowerCase() > b[sortBy].toLowerCase() ? 1 : -1;\n\nobjs.sort(sorter1);\nconsole.log(\"Using sorter1 - Hardcoded sort property last_name\", objs);\n\nobjs.sort(sorter2('first_nom'));\nconsole.log(\"Using sorter2 - passed param sortBy='first_nom'\", objs);\n\nobjs.sort(sorter2('last_nom'));\nconsole.log(\"Using sorter2 - passed param sortBy='last_nom'\", objs);
\r\n
\r\n
\r\n

\n", + "upvotes": 195, + "upvoterUsernames": [], + "downvotes": 91, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a46", + "creator": "Gangula", + "createdAt": 1635535589000, + "text": "The case sensitive approach is a good shorthand - especially if the values are numeric or date.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a48", + "creator": "muasif80", + "createdAt": 1635614980000, + "text": "What about changing (a, b) to (b, a) :)", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f324fd082fcc3049e91a4a", + "creator": "Gangula", + "createdAt": 1635617015000, + "text": "Yes, that works too. I just find swapping 1 & -1 more straight forward and logical.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905b7", + "creator": "ccpizza", + "createdAt": 1645729246000, + "text": "

Sorting objects with Intl.Collator for the specific case when you want natural sorting (i.e. 1,2,10,11,111).

\n

\r\n
\r\n
const files = [\n {name: \"1.mp3\", size: 123},\n {name: \"10.mp3\", size: 456},\n {name: \"100.mp3\", size: 789},\n {name: \"11.mp3\", size: 123},\n {name: \"111.mp3\", size: 456},\n {name: \"2.mp3\", size: 789},\n];\n\nconst naturalCollator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});\n\nfiles.sort((a, b) => naturalCollator.compare(a.name, b.name));\n\nconsole.log(files);
\r\n
\r\n
\r\n

\n

Browser support for Intl.Collator

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324fd082fcc3049e91a4b", + "creator": "David Scott Kirby", + "createdAt": 1646317209000, + "text": "This is a neat approach.", + "upvotes": 3045, + "upvoterUsernames": [], + "downvotes": 3045, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90583", + "creator": "Wogan", + "createdAt": 1247628951000, + "text": "

It's easy enough to write your own comparison function:

\n
function compare( a, b ) {\n  if ( a.last_nom < b.last_nom ){\n    return -1;\n  }\n  if ( a.last_nom > b.last_nom ){\n    return 1;\n  }\n  return 0;\n}\n\nobjs.sort( compare );\n
\n

Or inline (c/o Marco Demaio):

\n
objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))\n
\n

Or simplified for numeric (c/o Andre Figueiredo):

\n
objs.sort((a,b) => a.last_nom - b.last_nom); // b - a for reverse sort\n
\n", + "upvotes": 10042, + "upvoterUsernames": [], + "downvotes": 4868, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f329be082fcc3049e92e27", + "creator": "Marco Demaio", + "createdAt": 1267036150000, + "text": "Or inline: objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} );", + "upvotes": 1012, + "upvoterUsernames": [], + "downvotes": 470, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e28", + "creator": "Cerbrus", + "createdAt": 1360838240000, + "text": "return a.last_nom.localeCompare(b.last_nom) will work, too.", + "upvotes": 455, + "upvoterUsernames": [], + "downvotes": 185, + "downvoterUsernames": [] + }, + { + "_id": "62f329be082fcc3049e92e2a", + "creator": "Andre Figueiredo", + "createdAt": 1389182785000, + "text": "for those looking for a sort where the field is numeric, the compare function body: return a.value - b.value; (ASC)", + "upvotes": 210, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90585", + "creator": "Christoph", + "createdAt": 1247642508000, + "text": "

Instead of using a custom comparison function, you could also create an object type with custom toString() method (which is invoked by the default comparison function):

\n\n
function Person(firstName, lastName) {\n    this.firtName = firstName;\n    this.lastName = lastName;\n}\n\nPerson.prototype.toString = function() {\n    return this.lastName + ', ' + this.firstName;\n}\n\nvar persons = [ new Person('Lazslo', 'Jamf'), ...]\npersons.sort();\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90584", + "creator": "kennebec", + "createdAt": 1247630618000, + "text": "

If you have duplicate last names you might sort those by first name-

\n\n
obj.sort(function(a,b){\n  if(a.last_nom< b.last_nom) return -1;\n  if(a.last_nom >b.last_nom) return 1;\n  if(a.first_nom< b.first_nom) return -1;\n  if(a.first_nom >b.first_nom) return 1;\n  return 0;\n});\n
\n", + "upvotes": 107, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90588", + "creator": "Vinay Aggarwal", + "createdAt": 1341921258000, + "text": "

Simple and quick solution to this problem using prototype inheritance:

\n\n
Array.prototype.sortBy = function(p) {\n  return this.slice(0).sort(function(a,b) {\n    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;\n  });\n}\n
\n\n

Example / Usage

\n\n
objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];\n\nobjs.sortBy('age');\n// Returns\n// [{\"age\":24,\"name\":\"deepak\"},{\"age\":44,\"name\":\"vinay\"},{\"age\":74,\"name\":\"suresh\"}]\n\nobjs.sortBy('name');\n// Returns\n// [{\"age\":24,\"name\":\"deepak\"},{\"age\":74,\"name\":\"suresh\"},{\"age\":44,\"name\":\"vinay\"}]\n
\n\n

Update: No longer modifies original array.

\n", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e2b", + "creator": "Vinay Aggarwal", + "createdAt": 1342849409000, + "text": "It dosn't just return another array. but actually sorts the original one!.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90589", + "creator": "Behnam Shomali", + "createdAt": 1347826938000, + "text": "

additional desc params for Ege Özcan code

\n\n
function dynamicSort(property, desc) {\n    if (desc) {\n        return function (a, b) {\n            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;\n        }   \n    }\n    return function (a, b) {\n        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;\n    }\n}\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90586", + "creator": "Ege Özcan", + "createdAt": 1295622186000, + "text": "

You can also create a dynamic sort function that sorts objects by their value that you pass:

\n
function dynamicSort(property) {\n    var sortOrder = 1;\n    if(property[0] === "-") {\n        sortOrder = -1;\n        property = property.substr(1);\n    }\n    return function (a,b) {\n        /* next line works with strings and numbers, \n         * and you may want to customize it to your needs\n         */\n        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;\n        return result * sortOrder;\n    }\n}\n
\n

So you can have an array of objects like this:

\n
var People = [\n    {Name: "Name", Surname: "Surname"},\n    {Name:"AAA", Surname:"ZZZ"},\n    {Name: "Name", Surname: "AAA"}\n];\n
\n

...and it will work when you do:

\n
People.sort(dynamicSort("Name"));\nPeople.sort(dynamicSort("Surname"));\nPeople.sort(dynamicSort("-Surname"));\n
\n

Actually this already answers the question. Below part is written because many people contacted me, complaining that it doesn't work with multiple parameters.

\n

Multiple Parameters

\n

You can use the function below to generate sort functions with multiple sort parameters.

\n
function dynamicSortMultiple() {\n    /*\n     * save the arguments object as it will be overwritten\n     * note that arguments object is an array-like object\n     * consisting of the names of the properties to sort by\n     */\n    var props = arguments;\n    return function (obj1, obj2) {\n        var i = 0, result = 0, numberOfProperties = props.length;\n        /* try getting a different result from 0 (equal)\n         * as long as we have extra properties to compare\n         */\n        while(result === 0 && i < numberOfProperties) {\n            result = dynamicSort(props[i])(obj1, obj2);\n            i++;\n        }\n        return result;\n    }\n}\n
\n

Which would enable you to do something like this:

\n
People.sort(dynamicSortMultiple("Name", "-Surname"));\n
\n

Subclassing Array

\n

For the lucky among us who can use ES6, which allows extending the native objects:

\n
class MyArray extends Array {\n    sortBy(...args) {\n        return this.sort(dynamicSortMultiple(...args));\n    }\n}\n
\n

That would enable this:

\n
MyArray.from(People).sortBy("Name", "-Surname");\n
\n", + "upvotes": 1301, + "upvoterUsernames": [], + "downvotes": 267, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e2e", + "creator": "zero_cool", + "createdAt": 1656714440000, + "text": "You shouldn't ever extend Array.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9058a", + "creator": "Mike R", + "createdAt": 1366733254000, + "text": "

Combining Ege's dynamic solution with Vinay's idea, you get a nice robust solution:

\n

\r\n
\r\n
Array.prototype.sortBy = function() {\n  function _sortByAttr(attr) {\n    var sortOrder = 1;\n    if (attr[0] == \"-\") {\n      sortOrder = -1;\n      attr = attr.substr(1);\n    }\n    return function(a, b) {\n      var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;\n      return result * sortOrder;\n    }\n  }\n\n  function _getSortFunc() {\n    if (arguments.length == 0) {\n      throw \"Zero length arguments not allowed for Array.sortBy()\";\n    }\n    var args = arguments;\n    return function(a, b) {\n      for (var result = 0, i = 0; result == 0 && i < args.length; i++) {\n        result = _sortByAttr(args[i])(a, b);\n      }\n      return result;\n    }\n  }\n  return this.sort(_getSortFunc.apply(null, arguments));\n}\n\nUsage:\n\n  // Utility for printing objects\n  Array.prototype.print = function(title) {\n    console.log(\"************************************************************************\");\n    console.log(\"**** \" + title);\n    console.log(\"************************************************************************\");\n    for (var i = 0; i < this.length; i++) {\n      console.log(\"Name: \" + this[i].FirstName, this[i].LastName, \"Age: \" + this[i].Age);\n    }\n  }\n\n// Setup sample data\nvar arrObj = [{\n    FirstName: \"Zach\",\n    LastName: \"Emergency\",\n    Age: 35\n  },\n  {\n    FirstName: \"Nancy\",\n    LastName: \"Nurse\",\n    Age: 27\n  },\n  {\n    FirstName: \"Ethel\",\n    LastName: \"Emergency\",\n    Age: 42\n  },\n  {\n    FirstName: \"Nina\",\n    LastName: \"Nurse\",\n    Age: 48\n  },\n  {\n    FirstName: \"Anthony\",\n    LastName: \"Emergency\",\n    Age: 44\n  },\n  {\n    FirstName: \"Nina\",\n    LastName: \"Nurse\",\n    Age: 32\n  },\n  {\n    FirstName: \"Ed\",\n    LastName: \"Emergency\",\n    Age: 28\n  },\n  {\n    FirstName: \"Peter\",\n    LastName: \"Physician\",\n    Age: 58\n  },\n  {\n    FirstName: \"Al\",\n    LastName: \"Emergency\",\n    Age: 51\n  },\n  {\n    FirstName: \"Ruth\",\n    LastName: \"Registration\",\n    Age: 62\n  },\n  {\n    FirstName: \"Ed\",\n    LastName: \"Emergency\",\n    Age: 38\n  },\n  {\n    FirstName: \"Tammy\",\n    LastName: \"Triage\",\n    Age: 29\n  },\n  {\n    FirstName: \"Alan\",\n    LastName: \"Emergency\",\n    Age: 60\n  },\n  {\n    FirstName: \"Nina\",\n    LastName: \"Nurse\",\n    Age: 54\n  }\n];\n\n//Unit Tests\narrObj.sortBy(\"LastName\").print(\"LastName Ascending\");\narrObj.sortBy(\"-LastName\").print(\"LastName Descending\");\narrObj.sortBy(\"LastName\", \"FirstName\", \"-Age\").print(\"LastName Ascending, FirstName Ascending, Age Descending\");\narrObj.sortBy(\"-FirstName\", \"Age\").print(\"FirstName Descending, Age Ascending\");\narrObj.sortBy(\"-Age\").print(\"Age Descending\");
\r\n
\r\n
\r\n

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e30", + "creator": "Ege Özcan", + "createdAt": 1368197468000, + "text": "Thanks for the idea! By the way, please do not encourage people to change the Array Prototype (see the warning at the end of my example).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90587", + "creator": "David Morrow", + "createdAt": 1336685072000, + "text": "

underscore.js

\n\n

use underscore, its small and awesome...

\n\n
\n

sortBy_.sortBy(list, iterator, [context]) Returns a sorted copy of\n list, ranked in ascending order by the results of running each value\n through iterator. Iterator may also be the string name of the property\n to sort by (eg. length).

\n
\n\n
var objs = [ \n  { first_nom: 'Lazslo',last_nom: 'Jamf' },\n  { first_nom: 'Pig', last_nom: 'Bodine'  },\n  { first_nom: 'Pirate', last_nom: 'Prentice' }\n];\n\nvar sortedObjs = _.sortBy( objs, 'first_nom' );\n
\n", + "upvotes": 393, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329bf082fcc3049e92e36", + "creator": "Erdal G.", + "createdAt": 1454236987000, + "text": "To reverse sort: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f329bf082fcc3049e92e38", + "creator": "WoJ", + "createdAt": 1523962172000, + "text": "Also available in Lodash for the ones who prefer that one", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329bf082fcc3049e92e3a", + "creator": "Lys", + "createdAt": 1654099537000, + "text": "@TravisHeeter for lodash descending should be desc", + "upvotes": 677, + "upvoterUsernames": [], + "downvotes": 677, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321c1082fcc3049e90555", + "creator": "Ali NajafZadeh", + "createdAt": 1628009816000, + "text": "objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))", + "upvotes": 246, + "upvoterUsernames": [], + "downvotes": 246, + "downvoterUsernames": [] + }, + { + "_id": "62f321c1082fcc3049e90556", + "creator": "artem", + "createdAt": 1636093120000, + "text": "@RobertTalada it's my answer stackoverflow.com/a/67021585/7012450", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2607957, + "uvac": 2608010 + } + }, + { + "_id": "62f321bb082fcc3049e8feba", + "title": "How do I replace all occurrences of a string in JavaScript?", + "title-lowercase": "how do i replace all occurrences of a string in javascript?", + "creator": "Ali", + "createdAt": 1247853226000, + "status": "open", + "text": "

Given a string:

\n
s = "Test abc test test abc test test test abc test test abc";\n
\n

This seems to only remove the first occurrence of abc in the string above:

\n
s = s.replace('abc', '');\n
\n

How do I replace all occurrences of it?

\n", + "upvotes": 8863, + "upvoterUsernames": [], + "downvotes": 3642, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 4222641, + "answers": 73, + "answerItems": [ + { + "_id": "62f321be082fcc3049e901fa", + "creator": "scronide", + "createdAt": 1241652220000, + "text": "

Match against a global regular expression:

\n\n
anotherString = someString.replace(/cat/g, 'dog');\n
\n", + "upvotes": 128, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f9", + "creator": "Adam A", + "createdAt": 1241651925000, + "text": "

Using a regular expression with the g flag set will replace all:

\n
someString = 'the cat looks like a cat';\nanotherString = someString.replace(/cat/g, 'dog');\n// anotherString now contains "the dog looks like a dog"\n
\n

See here also

\n", + "upvotes": 1597, + "upvoterUsernames": [], + "downvotes": 787, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901fb", + "creator": "Sean Bright", + "createdAt": 1247853289000, + "text": "

As of August 2020: Modern browsers have support for the String.replaceAll() method defined by the ECMAScript 2021 language specification.

\n
\n

For older/legacy browsers:

\n
function escapeRegExp(string) {\n  return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n\nfunction replaceAll(str, find, replace) {\n  return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);\n}\n
\n

Here is how this answer evolved:

\n
str = str.replace(/abc/g, '');\n
\n

In response to comment "what's if 'abc' is passed as a variable?":

\n
var find = 'abc';\nvar re = new RegExp(find, 'g');\n\nstr = str.replace(re, '');\n
\n

In response to Click Upvote's comment, you could simplify it even more:

\n
function replaceAll(str, find, replace) {\n  return str.replace(new RegExp(find, 'g'), replace);\n}\n
\n

Note: Regular expressions contain special (meta) characters, and as such it is dangerous to blindly pass an argument in the find function above without pre-processing it to escape those characters. This is covered in the Mozilla Developer Network's JavaScript Guide on Regular Expressions, where they present the following utility function (which has changed at least twice since this answer was originally written, so make sure to check the MDN site for potential updates):

\n
function escapeRegExp(string) {\n  return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n
\n

So in order to make the replaceAll() function above safer, it could be modified to the following if you also include escapeRegExp:

\n
function replaceAll(str, find, replace) {\n  return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);\n}\n
\n", + "upvotes": 9505, + "upvoterUsernames": [], + "downvotes": 4472, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901fc", + "creator": "SolutionYogi", + "createdAt": 1247853318000, + "text": "\n
str = str.replace(/abc/g, '');\n
\n

Or try the replaceAll method, as recommended in this answer:

\n
str = str.replaceAll('abc', '');\n
\n

or:

\n
var search = 'abc';\nstr = str.replaceAll(search, '');\n
\n

EDIT: Clarification about replaceAll availability

\n

The replaceAll method is added to String's prototype. This means it will be available for all string objects/literals.

\n

Example:

\n
var output = "test this".replaceAll('this', 'that'); // output is 'test that'.\noutput = output.replaceAll('that', 'this'); // output is 'test this'\n
\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901fd", + "creator": "Donnie DeBoer", + "createdAt": 1247853363000, + "text": "

Use a regular expression:

\n\n
str.replace(/abc/g, '');\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901fe", + "creator": "Matthew Crumley", + "createdAt": 1247862552000, + "text": "

Update: In the latest versions of most popular browsers, you can use replaceAll\nas shown here:

\n
let result = "1 abc 2 abc 3".replaceAll("abc", "xyz");\n// `result` is "1 xyz 2 xyz 3"\n
\n

But check Can I use or another compatibility table first to make sure the browsers you're targeting have added support for it first.

\n
\n

For Node and compatibility with older/non-current browsers:

\n

Note: Don't use the following solution in performance critical code.

\n

As an alternative to regular expressions for a simple literal string, you could use

\n
str = "Test abc test test abc test...".split("abc").join("");\n
\n

The general pattern is

\n
str.split(search).join(replacement)\n
\n

This used to be faster in some cases than using replaceAll and a regular expression, but that doesn't seem to be the case anymore in modern browsers.

\n

Benchmark: https://jsben.ch/TZYzj

\n

Conclusion:

\n

If you have a performance critical use case (e.g processing hundreds of strings), use the Regexp method. But for most typical use cases, this is well worth not having to worry about special characters.

\n", + "upvotes": 2921, + "upvoterUsernames": [], + "downvotes": 610, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3233f082fcc3049e9141b", + "creator": "Abion47", + "createdAt": 1614217098000, + "text": "NodeJS supports replaceAll in 15.x versions.", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + }, + { + "_id": "62f3233f082fcc3049e9141d", + "creator": "Kunal Tanwar", + "createdAt": 1630105413000, + "text": "what to do in case-sensitive case", + "upvotes": 855, + "upvoterUsernames": [], + "downvotes": 855, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90200", + "creator": "jesal", + "createdAt": 1360623807000, + "text": "

Here's a string prototype function based on the accepted answer:

\n
String.prototype.replaceAll = function (find, replace) {\n    var str = this;\n    return str.replace(new RegExp(find, 'g'), replace);\n};\n
\n

EDIT

\n

If your find will contain special characters then you need to escape them:

\n
String.prototype.replaceAll = function (find, replace) {\n    var str = this;\n    return str.replace(new RegExp(find.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&'), 'g'), replace);\n};\n
\n

Fiddle: http://jsfiddle.net/cdbzL/

\n", + "upvotes": 140, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ff", + "creator": "Vitim.us", + "createdAt": 1319769374000, + "text": "

My implementation, very self explanatory

\n\n
function replaceAll(string, token, newtoken) {\n    if(token!=newtoken)\n    while(string.indexOf(token) > -1) {\n        string = string.replace(token, newtoken);\n    }\n    return string;\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3233f082fcc3049e91421", + "creator": "Vitim.us", + "createdAt": 1366128962000, + "text": "it depends if you allow to replace "recursively" or not.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3233f082fcc3049e91422", + "creator": "user1002973", + "createdAt": 1368715483000, + "text": "replaceAll("abc", "a", "ab") never terminates", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90201", + "creator": "Owen", + "createdAt": 1368626583000, + "text": "

I like this method (it looks a little cleaner):

\n\n
text = text.replace(new RegExp(\"cat\",\"g\"), \"dog\"); \n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90202", + "creator": "Raseela", + "createdAt": 1370408224000, + "text": "

Loop it until number occurrences comes to 0, like this:

\n
function replaceAll(find, replace, str) {\n    while (str.indexOf(find) > -1) {\n        str = str.replace(find, replace);\n    }\n    return str;\n}\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90203", + "creator": "rakslice", + "createdAt": 1371684069000, + "text": "

If what you want to find is already in a string, and you don't have a regex escaper handy, you can use join/split:

\n\n

\r\n
\r\n
    function replaceMulti(haystack, needle, replacement)\r\n    {\r\n        return haystack.split(needle).join(replacement);\r\n    }\r\n\r\n    someString = 'the cat looks like a cat';\r\n    console.log(replaceMulti(someString, 'cat', 'dog'));
\r\n
\r\n
\r\n

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90204", + "creator": "Cory Gross", + "createdAt": 1373593584000, + "text": "

For the sake of completeness, I got to thinking about which method I should use to do this. There are basically two ways to do this as suggested by the other answers on this page.

\n\n

Note: In general, extending the built-in prototypes in JavaScript is generally not recommended. I am providing as extensions on the String prototype simply for purposes of illustration, showing different implementations of a hypothetical standard method on the String built-in prototype.

\n\n
\n\n

Regular Expression Based Implementation

\n\n
String.prototype.replaceAll = function(search, replacement) {\n    var target = this;\n    return target.replace(new RegExp(search, 'g'), replacement);\n};\n
\n\n

Split and Join (Functional) Implementation

\n\n
String.prototype.replaceAll = function(search, replacement) {\n    var target = this;\n    return target.split(search).join(replacement);\n};\n
\n\n
\n\n

Not knowing too much about how regular expressions work behind the scenes in terms of efficiency, I tended to lean toward the split and join implementation in the past without thinking about performance. When I did wonder which was more efficient, and by what margin, I used it as an excuse to find out.

\n\n

On my Chrome Windows 8 machine, the regular expression based implementation is the fastest, with the split and join implementation being 53% slower. Meaning the regular expressions are twice as fast for the lorem ipsum input I used.

\n\n

Check out this benchmark running these two implementations against each other.

\n\n
\n\n

As noted in the comment below by @ThomasLeduc and others, there could be an issue with the regular expression-based implementation if search contains certain characters which are reserved as special characters in regular expressions. The implementation assumes that the caller will escape the string beforehand or will only pass strings that are without the characters in the table in Regular Expressions (MDN).

\n\n

MDN also provides an implementation to escape our strings. It would be nice if this was also standardized as RegExp.escape(str), but alas, it does not exist:

\n\n
function escapeRegExp(str) {\n  return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"); // $& means the whole matched string\n}\n
\n\n

We could call escapeRegExp within our String.prototype.replaceAll implementation, however, I'm not sure how much this will affect the performance (potentially even for strings for which the escape is not needed, like all alphanumeric strings).

\n", + "upvotes": 2873, + "upvoterUsernames": [], + "downvotes": 364, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3233f082fcc3049e91427", + "creator": "Sunil Garg", + "createdAt": 1641358334000, + "text": "m using nestjs, so typescript showing error that replaceAll is not method of String prototype, any solution for this?", + "upvotes": 1755, + "upvoterUsernames": [], + "downvotes": 1755, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90206", + "creator": "SiwachGaurav", + "createdAt": 1387883133000, + "text": "

For replacing all kind of characters, try this code:

\n\n
Suppose we have need to send \" and \\ in my string, then we will convert it \" to \\\" and \\ to \\\\\n
\n\n

So this method will solve this issue.

\n\n
String.prototype.replaceAll = function (find, replace) {\n     var str = this;\n     return str.replace(new RegExp(find.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&'), 'g'), replace);\n };\n\nvar message = $('#message').val();\n             message = message.replaceAll('\\\\', '\\\\\\\\'); /*it will replace \\ to \\\\ */\n             message = message.replaceAll('\"', '\\\\\"');   /*it will replace \" to \\\\\"*/\n
\n\n

I was using Ajax, and I had the need to send parameters in JSON format. Then my method is looking like this:

\n\n
 function sendMessage(source, messageID, toProfileID, userProfileID) {\n\n     if (validateTextBox()) {\n         var message = $('#message').val();\n         message = message.replaceAll('\\\\', '\\\\\\\\');\n         message = message.replaceAll('\"', '\\\\\"');\n         $.ajax({\n             type: \"POST\",\n             async: \"false\",\n             contentType: \"application/json; charset=utf-8\",\n             url: \"services/WebService1.asmx/SendMessage\",\n             data: '{\"source\":\"' + source + '\",\"messageID\":\"' + messageID + '\",\"toProfileID\":\"' + toProfileID + '\",\"userProfileID\":\"' + userProfileID + '\",\"message\":\"' + message + '\"}',\n             dataType: \"json\",\n             success: function (data) {\n                 loadMessageAfterSend(toProfileID, userProfileID);\n                 $(\"#<%=PanelMessageDelete.ClientID%>\").hide();\n                 $(\"#message\").val(\"\");\n                 $(\"#delMessageContainer\").show();\n                 $(\"#msgPanel\").show();\n             },\n             error: function (result) {\n                 alert(\"message sending failed\");\n             }\n         });\n     }\n     else {\n         alert(\"Please type message in message box.\");\n         $(\"#message\").focus();\n\n     }\n }\n\n String.prototype.replaceAll = function (find, replace) {\n     var str = this;\n     return str.replace(new RegExp(find.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&'), 'g'), replace);\n };\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90205", + "creator": "pkdkk", + "createdAt": 1378288877000, + "text": "
var str = \"ff ff f f a de def\";\nstr = str.replace(/f/g,'');\nalert(str);\n
\n\n

http://jsfiddle.net/ANHR9/

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90207", + "creator": "Antonio Mirarchi", + "createdAt": 1389121889000, + "text": "

Try this:

\n\n
String.prototype.replaceAll = function (sfind, sreplace) {\n    var str = this;\n\n    while (str.indexOf(sfind) > -1) {\n        str = str.replace(sfind, sreplace);\n    }\n\n    return str;\n};\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32340082fcc3049e9142c", + "creator": "Jonathan ANTOINE", + "createdAt": 1466757788000, + "text": "if the replacement contains the "find" youi will have an infinite loop", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9020f", + "creator": "Termininja", + "createdAt": 1452642228000, + "text": "

I use p to store the result from the previous recursion replacement:

\n\n
function replaceAll(s, m, r, p) {\n    return s === p || r.contains(m) ? s : replaceAll(s.replace(m, r), m, r, s);\n}\n
\n\n

It will replace all occurrences in the string s until it is possible:

\n\n
replaceAll('abbbbb', 'ab', 'a') → 'abbbb' → 'abbb' → 'abb' → 'ab' → 'a'\n
\n\n

To avoid infinite loop I check if the replacement r contains a match m:

\n\n
replaceAll('abbbbb', 'a', 'ab') → 'abbbbb'\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90210", + "creator": "tk_", + "createdAt": 1455537954000, + "text": "

You can simply use below method

\n\n
/**\n * Replace all the occerencess of $find by $replace in $originalString\n * @param  {originalString} input - Raw string.\n * @param  {find} input - Target key word or regex that need to be replaced.\n * @param  {replace} input - Replacement key word\n * @return {String}       Output string\n */\nfunction replaceAll(originalString, find, replace) {\n  return originalString.replace(new RegExp(find, 'g'), replace);\n};\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90212", + "creator": "Nivesh Saharan", + "createdAt": 1457079087000, + "text": "

Here is the working code with prototype:

\n\n
String.prototype.replaceAll = function(find, replace) {\n    var str = this;\n    return str.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\\[\\]\\/\\\\])/g, \"\\\\$1\"), 'g'), replace);\n};\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90211", + "creator": "Sandeep Gantait", + "createdAt": 1455616386000, + "text": "

The following function works for me:

\n\n
String.prototype.replaceAllOccurence = function(str1, str2, ignore)\n{\n    return this.replace(new RegExp(str1.replace(/([\\/\\,\\!\\\\\\^\\$\\{\\}\\[\\]\\(\\)\\.\\*\\+\\?\\|\\<\\>\\-\\&])/g,\"\\\\$&\"),(ignore?\"gi\":\"g\")),(typeof(str2)==\"string\")?str2.replace(/\\$/g,\"$$$$\"):str2);\n} ;\n
\n\n

Now call the functions like this:

\n\n
\"you could be a Project Manager someday, if you work like this.\".replaceAllOccurence (\"you\", \"I\");\n
\n\n

Simply copy and paste this code in your browser console to TEST.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90213", + "creator": "theWalker", + "createdAt": 1460397669000, + "text": "
function replaceAll(str, find, replace) {\n    var $r=\"\";\n    while($r!=str){ \n        $r = str;\n        str = str.replace(find, replace);\n    }\n    return str;\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3237d082fcc3049e91433", + "creator": "Jonathan ANTOINE", + "createdAt": 1466757841000, + "text": "if the replacement contains the "find" youi will have an infinite loop", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90214", + "creator": "Cheezy Code", + "createdAt": 1462300764000, + "text": "

Although people have mentioned the use of regex but there's a better approach if you want to replace the text irrespective of the case of the text. Like uppercase or lowercase. Use below syntax

\n\n
//Consider below example\noriginalString.replace(/stringToBeReplaced/gi, '');\n\n//Output will be all the occurrences removed irrespective of casing.\n
\n\n

You can refer the detailed example here.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90216", + "creator": "C. Morgan", + "createdAt": 1476550485000, + "text": "

This can be solved using regular expressions and the flag g, which means to not stop after finding the first match. Really, regular expressions are life savers!

\n\n
function replaceAll(string, pattern, replacement) {\n    return string.replace(new RegExp(pattern, \"g\"), replacement);\n}\n\n// or if you want myString.replaceAll(\"abc\", \"\");\n\nString.prototype.replaceAll = function(pattern, replacement) {\n    return this.replace(new RegExp(pattern, \"g\"), replacement);\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90215", + "creator": "Emilio Grisolía", + "createdAt": 1473519559000, + "text": "

Say you want to replace all the 'abc' with 'x':

\n\n
let some_str = 'abc def def lom abc abc def'.split('abc').join('x')\nconsole.log(some_str) //x def def lom x x def\n
\n\n

I was trying to think about something more simple than modifying the string prototype.

\n", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90217", + "creator": "User", + "createdAt": 1489726727000, + "text": "

Most people are likely doing this to encode a URL. To encode a URL, you shouldn't only consider spaces, but convert the entire string properly with encodeURI.

\n\n
encodeURI(\"http://www.google.com/a file with spaces.html\")\n
\n\n

to get:

\n\n
http://www.google.com/a%20file%20with%20spaces.html\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90218", + "creator": "Alireza", + "createdAt": 1496756362000, + "text": "

Using RegExp in JavaScript could do the job for you, just simply do something like below code, don't forget the /g after which standout for global:

\n\n
var str =\"Test abc test test abc test test test abc test test abc\";\nstr = str.replace(/abc/g, '');\n
\n\n

If you think of reuse, create a function to do that for you, but it's not recommended as it's only one line function, but again if you heavily use this, you can write something like this:

\n\n
String.prototype.replaceAll = String.prototype.replaceAll || function(string, replaced) {\n  return this.replace(new RegExp(string, 'g'), replaced);\n};\n
\n\n

and simply use it in your code over and over like below:

\n\n
var str =\"Test abc test test abc test test test abc test test abc\";\nstr = str.replaceAll('abc', '');\n
\n\n

But as I mention earlier, it won't make a huge difference in terms of lines to be written or performance, only caching the function may effect some faster performance on long strings and also a good practice of DRY code if you want to reuse.

\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9021a", + "creator": "KARTHIKEYAN.A", + "createdAt": 1514454291000, + "text": "

In string first element search and replace

\n\n

\r\n
\r\n
var str = '[{\"id\":1,\"name\":\"karthikeyan.a\",\"type\":\"developer\"}]'\r\nvar i = str.replace('\"[','[').replace(']\"',']');\r\nconsole.log(i,'//first element search and replace')
\r\n
\r\n
\r\n

\n\n

In string global search and replace

\n\n

\r\n
\r\n
var str = '[{\"id\":1,\"name\":\"karthikeyan.a\",\"type\":\"developer\"}]'\r\nvar j = str.replace(/\\\"\\[/g,'[').replace(/\\]\\\"/g,']');\r\nconsole.log(j,'//global search and replace')
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90219", + "creator": "mostafa elmadany", + "createdAt": 1507409776000, + "text": "

If the string contain similar pattern like abccc, you can use this:

\n\n
str.replace(/abc(\\s|$)/g, \"\")\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9021b", + "creator": "csomakk", + "createdAt": 1514464062000, + "text": "
str = str.replace(new RegExp(\"abc\", 'g'), \"\");\n
\n\n

worked better for me than the above answers. so new RegExp(\"abc\", 'g') creates a RegExp what matches all occurence ('g' flag) of the text (\"abc\"). The second part is what gets replaced to, in your case empty string (\"\").\nstr is the string, and we have to override it, as replace(...) just returns result, but not overrides. In some cases you might want to use that.

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9021c", + "creator": "Andrés", + "createdAt": 1517007935000, + "text": "

I just want to share my solution, based on some of the functional features of last versions of JavaScript:

\n\n
   var str = \"Test abc test test abc test test test abc test test abc\";\n\n   var result = str.split(' ').reduce((a, b) => {\n      return b == 'abc' ? a : a + ' ' + b;   })\n\n  console.warn(result)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9021d", + "creator": "TheAivis", + "createdAt": 1523448984000, + "text": "

For unique replaceable values

\n\n

\r\n
\r\n
String.prototype.replaceAll = function(search_array, replacement_array) {\r\n  //\r\n  var target = this;\r\n  //\r\n  search_array.forEach(function(substr, index) {\r\n    if (typeof replacement_array[index] != \"undefined\") {\r\n      target = target.replace(new RegExp(substr, 'g'), replacement_array[index])\r\n    }\r\n  });\r\n  //\r\n  return target;\r\n};\r\n\r\n//  Use:\r\nvar replacedString = \"This topic commented on :year. Talking :question.\".replaceAll([':year', ':question'], ['2018', 'How to replace all occurrences of a string in JavaScript']);\r\n//\r\nconsole.log(replacedString);
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3237e082fcc3049e9143d", + "creator": "TheAivis", + "createdAt": 1524232342000, + "text": "Not meant for it. Only for unique replaceable values.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3237e082fcc3049e9143f", + "creator": "rak007", + "createdAt": 1524232383000, + "text": "this is useless then in most case", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9021e", + "creator": "Ferie", + "createdAt": 1536078243000, + "text": "

In terms of performance related to the main answers these are some online tests.

\n\n

While the following are some performance tests using console.time() (they work best in your own console, the time is very short to be seen in the snippet)

\n\n

\r\n
\r\n
console.time('split and join');\r\n\"javascript-test-find-and-replace-all\".split('-').join(' ');\r\nconsole.timeEnd('split and join')\r\n\r\nconsole.time('regular expression');\r\n\"javascript-test-find-and-replace-all\".replace(new RegExp('-', 'g'), ' ');\r\nconsole.timeEnd('regular expression');\r\n\r\nconsole.time('while');\r\nlet str1 = \"javascript-test-find-and-replace-all\";\r\nwhile (str1.indexOf('-') !== -1) {\r\n    str1 = str1.replace('-', ' ');\r\n}\r\nconsole.timeEnd('while');
\r\n
\r\n
\r\n

\n\n

The interesting thing to notice is that if you run them multiple time the results are always different even though the RegExp solution seems the fastest on average and the while loop solution the slowest.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9021f", + "creator": "Black", + "createdAt": 1540287242000, + "text": "

The previous answers are way too complicated. Just use the replace function like this:

\n\n
str.replace(/your_regex_pattern/g, replacement_string);\n
\n\n

Example:

\n\n

\r\n
\r\n
var str = \"Test abc test test abc test test test abc test test abc\";\r\n\r\nvar res = str.replace(/[abc]+/g, \"\");\r\n\r\nconsole.log(res);
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90220", + "creator": "Arian Saputra", + "createdAt": 1548160417000, + "text": "

You can try like this:

\n

Example data:

\n
var text = "heloo,hai,hei"\n\ntext = text.replace(/[,]+/g, '')\n
\n

or

\n
text.forEach((value) => {\n  hasil = hasil.replace(',', '')\n})\n
\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90222", + "creator": "Brijesh Kumar Kushwaha", + "createdAt": 1549700906000, + "text": "

The simplest solution -

\n\n

\r\n
\r\n
let str = \"Test abc test test abc test test test abc test test abc\";\r\n\r\nstr = str.split(\" \");\r\nstr = str.filter((ele, key)=> ele!==\"abc\")\r\nstr = str.join(\" \")
\r\n
\r\n
\r\n

\n\n

Or simply -

\n\n

\r\n
\r\n
str = str.split(\" \").filter((ele, key) => ele != \"abc\").join(\" \")
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90221", + "creator": "Ashish", + "createdAt": 1548738324000, + "text": "

Method 1

\n\n

Try to implement a regular expression:

\n\n

\"Test abc test test abc test test test abc test test abc\".replace(/\\abc/g, ' ');

\n\n

Method 2

\n\n

Split and join. Split with abc and join with empty space.

\n\n

\"Test abc test test abc test test test abc test test abc\".split(\"abc\").join(\" \")

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90223", + "creator": "prime", + "createdAt": 1550258119000, + "text": "

This can be achieved using regular expressions. A few combinations that might help someone:

\n\n
var word = \"this,\\\\ .is*a*test,    '.and? / only /     'a \\ test?\";\nvar stri = \"This      is    a test         and only a        test\";\n
\n\n

To replace all non alpha characters,

\n\n
console.log(word.replace(/([^a-z])/g,' ').replace(/ +/g, ' ')); \nResult: [this is a test and only a test]\n
\n\n

To replace multiple continuous spaces with one space,

\n\n
console.log(stri.replace(/  +/g,' ')); \nResult: [This is a test and only a test]\n
\n\n

To replace all * characters,

\n\n
console.log(word.replace(/\\*/g,'')); \nResult: [this,\\ .isatest,    '.and? / only /     'a  test?]\n
\n\n

To replace question marks (?)

\n\n
console.log(word.replace(/\\?/g,'#')); \nResult: [this,\\ .is*a*test,    '.and# / only /     'a  test#]\n
\n\n

To replace quotation marks,

\n\n
console.log(word.replace(/'/g,'#'));  \nResult: [this,\\ .is*a*test,    #.and? / only /     #a  test?]\n
\n\n

To replace all ' characters,

\n\n
console.log(word.replace(/,/g,'')); \nResult: [this\\ .is*a*test    '.and? / only /     'a  test?]\n
\n\n

To replace a specific word,

\n\n
console.log(word.replace(/test/g,'')); \nResult: [this,\\ .is*a*,    '.and? / only /     'a  ?]\n
\n\n

To replace back-slash,

\n\n
console.log(word.replace(/\\\\/g,''));  \nResult: [this, .is*a*test,    '.and? / only /     'a  test?]\n
\n\n

To replace forward slash,

\n\n
console.log(word.replace(/\\//g,''));  \nResult: [this,\\ .is*a*test,    '.and?  only      'a  test?]\n
\n\n

To replace all spaces,

\n\n
console.log(word.replace(/ /g,'#'));  \nResult: [this,\\#.is*a*test,####'.and?#/#only#/#####'a##test?]\n
\n\n

To replace dots,

\n\n
console.log(word.replace(/\\./g,'#')); \nResult: [this,\\ #is*a*test,    '#and? / only /     'a  test?]\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90225", + "creator": "Adnan Toky", + "createdAt": 1553506718000, + "text": "

These are the most common and readable methods.

\n
var str = "Test abc test test abc test test test abc test test abc"\n
\n

Method 1:

\n
str = str.replace(/abc/g, "replaced text");\n
\n

Method 2:

\n
str = str.split("abc").join("replaced text");\n
\n

Method 3:

\n
str = str.replace(new RegExp("abc", "g"), "replaced text");\n
\n

Method 4:

\n
while(str.includes("abc")){\n   str = str.replace("abc", "replaced text");\n}\n
\n

Output:

\n
console.log(str);\n// Test replaced text test test replaced text test test test replaced text test test replaced text\n
\n", + "upvotes": 181, + "upvoterUsernames": [], + "downvotes": 89, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90224", + "creator": "Rinold", + "createdAt": 1551202612000, + "text": "
 var myName = 'r//i//n//o//l////d';\n  var myValidName = myName.replace(new RegExp('\\//', 'g'), ''); > // rinold\n  console.log(myValidName);\n\nvar myPetName = 'manidog';\nvar renameManiToJack = myPetName.replace(new RegExp('mani', 'g'), 'jack'); > // jackdog\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90226", + "creator": "VhsPiceros", + "createdAt": 1555375063000, + "text": "

I use split and join or this funcion

\n\n
function replaceAll( text, busca, reemplaza ){\n  while (text.toString().indexOf(busca) != -1)\n      text = text.toString().replace(busca,reemplaza);\n  return text;\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3237f082fcc3049e91447", + "creator": "MMMahdy-PAPION", + "createdAt": 1643432790000, + "text": "That work wrong in case of replaceAll('aaaaaa','aa','a')", + "upvotes": 1658, + "upvoterUsernames": [], + "downvotes": 1658, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90227", + "creator": "Jessie Lesbian", + "createdAt": 1557378704000, + "text": "

This should work.

\n\n
String.prototype.replaceAll = function (search, replacement) {\n    var str1 = this.replace(search, replacement);\n    var str2 = this;\n    while(str1 != str2) {\n        str2 = str1;\n        str1 = str1.replace(search, replacement);\n    }\n    return str1;\n}\n
\n\n

Example:

\n\n
Console.log(\"Steve is the best character in Minecraft\".replaceAll(\"Steve\", \"Alex\"));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3237f082fcc3049e9144a", + "creator": "Peter Mortensen", + "createdAt": 1583692013000, + "text": "@aabbccsmith: In what way? Can you elaborate?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3237f082fcc3049e9144b", + "creator": "Luiz Felipe", + "createdAt": 1588783745000, + "text": "" loops on strings like this shouldn't be endorsed " it would be good if that programming language wasn't so crappy", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90228", + "creator": "sajadre", + "createdAt": 1557859340000, + "text": "

The simplest way to this without using any regex is split and join like the code here:

\n

\r\n
\r\n
var str = \"Test abc test test abc test test test abc test test abc\";\nconsole.log(str.split('abc').join(''));
\r\n
\r\n
\r\n

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90229", + "creator": "Nouman Dilshad", + "createdAt": 1558343905000, + "text": "

All the answers are accepted, you can do this by many ways. One of the trick to do this is this.

\n\n

\r\n
\r\n
 const str = \"Test abc test test abc test test test abc test test abc\";\r\n    \r\n    const compare = \"abc\";\r\n    arrayStr = str.split(\" \");\r\n    arrayStr.forEach((element, index) => {\r\n      if (element == compare) {\r\n        arrayStr.splice(index, 1);\r\n      }\r\n    });\r\n    const newString = arrayStr.join(\" \");\r\n    console.log(newString);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022a", + "creator": "Indrajeet Singh", + "createdAt": 1559128271000, + "text": "

For replacing a single time use:

\n\n
var res = str.replace('abc', \"\");\n
\n\n

For replacing multiple times use:

\n\n
var res = str.replace(/abc/g, \"\");\n
\n", + "upvotes": 87, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022b", + "creator": "Raghavendra S", + "createdAt": 1559191419000, + "text": "

Check this answer may it will help and I used in my project.

\n\n
function replaceAll(searchString, replaceString, str) {\n   return str.split(searchString).join(replaceString);\n}\nreplaceAll('abc', '',\"Test abc test test abc test test test abc test test abc\" ); // \"Test  test test  test test test  test test \"\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022c", + "creator": "Stefan Steiger", + "createdAt": 1562849137000, + "text": "

You can do it without Regex, but you need to be careful if the replacement text contains the search text.

\n\n

e.g.

\n\n
replaceAll(\"nihIaohi\", \"hI\", \"hIcIaO\", true)\n
\n\n

So here is a proper variant of replaceAll, including string-prototype:

\n\n
function replaceAll(str, find, newToken, ignoreCase)\n{\n    let i = -1;\n\n    if (!str)\n    {\n        // Instead of throwing, act as COALESCE if find == null/empty and str == null\n        if ((str == null) && (find == null))\n            return newToken;\n\n        return str;\n    }\n\n    if (!find) // sanity check \n        return str;\n\n    ignoreCase = ignoreCase || false;\n    find = ignoreCase ? find.toLowerCase() : find;\n\n    while ((\n        i = (ignoreCase ? str.toLowerCase() : str).indexOf(\n            find, i >= 0 ? i + newToken.length : 0\n        )) !== -1\n    )\n    {\n        str = str.substring(0, i) +\n            newToken +\n            str.substring(i + find.length);\n    } // Whend \n\n    return str;\n}\n
\n\n

Or, if you want to have a string-prototype function:

\n\n
String.prototype.replaceAll = function (find, replace) {\n    let str = this;\n\n    let i = -1;\n\n    if (!str)\n    {\n        // Instead of throwing, act as COALESCE if find == null/empty and str == null\n        if ((str == null) && (find == null))\n            return newToken;\n\n        return str;\n    }\n\n    if (!find) // sanity check \n        return str;\n\n    ignoreCase = ignoreCase || false;\n    find = ignoreCase ? find.toLowerCase() : find;\n\n    while ((\n        i = (ignoreCase ? str.toLowerCase() : str).indexOf(\n            find, i >= 0 ? i + newToken.length : 0\n        )) !== -1\n    )\n    {\n        str = str.substring(0, i) +\n            newToken +\n            str.substring(i + find.length);\n    } // Whend \n\n    return str;\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022d", + "creator": "Rakib Uddin", + "createdAt": 1570202046000, + "text": "
str = \"Test abc test test abc test test test abc test test abc\"\n\nstr.split(' ').join().replace(/abc/g,'').replace(/,/g, ' ')\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022e", + "creator": "CertainPerformance", + "createdAt": 1571366907000, + "text": "

There is now a finished proposal for integrating String.prototype.replaceAll into the official specification. Eventually, developers will not have to come up with their own implementations for replaceAll - instead, modern Javascript engines will support it natively.

\n

The proposal is at stage 4, which means that everything is complete, and all that's left is for browsers to start implementing it.

\n

It has shipped in the latest versions of Chrome, Firefox, and Safari.

\n

Here are the implementation details:

\n
\n

Per the current TC39 consensus, String.prototype.replaceAll behaves identically to String.prototype.replace in all cases, except for the following two cases:

\n
    \n
  1. If searchValue is a string, String.prototype.replace only replaces a single occurrence of the searchValue, whereas String.prototype.replaceAll replaces all occurrences of the searchValue (as if .split(searchValue).join(replaceValue) or a global & properly-escaped regular expression had been used).
  2. \n
  3. If searchValue is a non-global regular expression, String.prototype.replace replaces a single match, whereas String.prototype.replaceAll throws an exception. This is done to avoid the inherent confusion between the lack of a global flag (which implies "do NOT replace all") and the name of the method being called (which strongly suggests "replace all").
  4. \n
\n

Notably, String.prototype.replaceAll behaves just like String.prototype.replace if searchValue is a global regular expression.

\n
\n

You can see a spec-compliant polyfill here.

\n

In supported environments, the following snippet will log foo-bar-baz, without throwing an error:

\n

\r\n
\r\n
const str = 'foo bar baz';\nconsole.log(\n  str.replaceAll(' ', '-')\n);
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9022f", + "creator": "Thomas Orlita", + "createdAt": 1573457136000, + "text": "

As of August 2020 there is a Stage 4 proposal to ECMAScript that adds the replaceAll method to String.

\n

It's now supported in Chrome 85+, Edge 85+, Firefox 77+, Safari 13.1+.

\n

The usage is the same as the replace method:

\n
String.prototype.replaceAll(searchValue, replaceValue)\n
\n

Here's an example usage:

\n
'Test abc test test abc test.'.replaceAll('abc', 'foo'); // -> 'Test foo test test foo test.'\n
\n
\n

It's supported in most modern browsers, but there exist polyfills:

\n\n

It is supported in the V8 engine behind an experimental flag --harmony-string-replaceall.\nRead more on the V8 website.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90230", + "creator": "Ws Memon", + "createdAt": 1574421558000, + "text": "

Check this. I'm sure it will help you:

\n\n
<!DOCTYPE html>\n<html>\n<body>\n<p>Click the button to do a global search and replace for \"is\" in a string.</p>\n<button onclick=\"myFunction()\">Try it</button>\n<p id=\"demo\"></p>\n<script>\nfunction myFunction() {\n  var str = 'Is this \"3\" dris \"3\"?';\n  var allvar= '\"3\"';\n  var patt1 = new RegExp( allvar, 'g' );\n  document.getElementById(\"demo\").innerHTML = str.replace(patt1,'\"5\"');\n}\n</script>\n</body>\n</html>\n
\n\n

Here is the JSFiddle link.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90231", + "creator": "y.kaf.", + "createdAt": 1576172046000, + "text": "

In November 2019 a new feature is added to the JavaScript string.prototype.replaceAll().

\n\n

Currently it's only supported with babel.JS, but maybe in the future it can be implemented in all the browsers. For more information, read here.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32380082fcc3049e91456", + "creator": "Ali", + "createdAt": 1576252752000, + "text": "Wow, javascript is finally adding what Java 1.0 had in 1995. Great!", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e90232", + "creator": "Lova Chittumuri", + "createdAt": 1580973764000, + "text": "

We can use the replace method in JavaScript:

\n\n
var result = yourString.replace('regexPattern', \"replaceString\");\n
\n\n

\r\n
\r\n
var str = \"Test abc test test abc test test test abc test test abc\";\r\n\r\nvar expectedString = str.replace(/abc(\\s|$)/g, \"\");\r\n\r\nconsole.log(expectedString);
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90233", + "creator": "e102", + "createdAt": 1583081676000, + "text": "

I know this isnt the best way to do this, but you can try this:

\n\n
var annoyingString = \"Test abc test test abc test test test abc test test abc\";\n\nwhile (annoyingString.includes(\"abc\")) {\n    annoyingString = annoyingString.replace(\"abc\", \"\")\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90234", + "creator": "Shavais", + "createdAt": 1590284149000, + "text": "

I added the function below to this perf test page in the \"library\" section:

\n\n

https://jsben.ch/LFfWA

\n\n
function _replace(t, s, r){\n    var i = t.indexOf(s);\n    if (i == -1) return t;\n    return t.slice(0, i) + r + _replace(t.slice(i + s.length, t.length), s,r);\n}\n
\n\n

..and put this in as the test:

\n\n
var replaced = _replace(testString, 'abc','123');\n
\n\n

.. and that function performs about 34% faster for me than split or regex. The idea / hope was to end up pasting smaller and smaller pieces of the string onto the stack and then building the entire result by unrolling the stack, thereby minimizing extra string copies and extra searches through the same string data and hopefully optimizing use of the CPU cache.

\n\n

Part of the thought was that if the string isn't too big, it may end up in the CPU cache; passing it and pasting pieces of it puts those bits into the cache, and then the searching can operate entirely using CPU cached data. Now whether or not that's actually what ends up happening I'm sure is entirely js implementation dependant.

\n\n

This isn't as fast as possible, but it's as fast as I could manage without mutable strings. Arrays in JavaScript probably have a pointer for each element, so, a solution involving a lot of array elements is not likely to be as CPU cache friendly as this.

\n", + "upvotes": 800, + "upvoterUsernames": [], + "downvotes": 800, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90235", + "creator": "Matěj Štágl", + "createdAt": 1591699168000, + "text": "

Starting from v85, chrome now supports String.prototype.replaceAll natively. Note this outperform all other proposed solutions and should be used once majorly supported.

\n

Feature status:\nhttps://chromestatus.com/feature/6040389083463680

\n

\r\n
\r\n
var s = \"hello hello world\";\ns = s.replaceAll(\"hello\", \"\"); // s is now \"world\"\nconsole.log(s)
\r\n
\r\n
\r\n

\n", + "upvotes": 268, + "upvoterUsernames": [], + "downvotes": 268, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90236", + "creator": "Iftikhar Hussain", + "createdAt": 1592807427000, + "text": "

Here's very simple solution.\nYou can assign a new method to String object

\n
String.prototype.replaceAll = function(search, replace){\n   return this.replace(new RegExp(search, 'g'), replace)\n}\n\nvar str = "Test abc test test abc test test test abc test test abc";\nstr = str.replaceAll('abc', '');\n\nconsole.log(str) // -> Test  test test  test test test  test test\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90237", + "creator": "Asakkour Soufiane", + "createdAt": 1594142485000, + "text": "

Use Split and Join

\n

\r\n
\r\n
var str = \"Test abc test test abc test test test abc test test abc\";\nvar replaced_str = str.split('abc').join('');\nconsole.log(replaced_str);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90238", + "creator": "Mohit Yadav", + "createdAt": 1598334923000, + "text": "

I would suggest adding a global method for string class by appending it to prototype chain.

\n
String.prototype.replaceAll = function(fromReplace, toReplace, {ignoreCasing} = {}) { return this.replace(new RegExp(fromReplace, ignoreCasing ? 'ig': 'g'), toReplace);}\n
\n

and it can be used like

\n
'stringwithpattern'.replaceAll('pattern','new-pattern')\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32381082fcc3049e9145f", + "creator": "Khalid Khan", + "createdAt": 1598347761000, + "text": "Can you please elaborate what the above code is doing ? Try to give proper answers rather than one liners.", + "upvotes": 831, + "upvoterUsernames": [], + "downvotes": 831, + "downvoterUsernames": [] + }, + { + "_id": "62f32381082fcc3049e91461", + "creator": "Mohit Yadav", + "createdAt": 1598422157000, + "text": "@JakeCoxon yeah my bad, updated the answer to fix the issues :D cheers!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9023a", + "creator": "Chungmin Park", + "createdAt": 1613816940000, + "text": "

There is a way to use the new replaceAll() method.

\n

But you need to use a cutting-edge browser or a JavaScript run time environment.

\n

You can check the browser compatibility in here.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90239", + "creator": "Nisharg Shah", + "createdAt": 1598390382000, + "text": "

In August 2020

\n
\n

No more regular expression stuff

\n
\n

\r\n
\r\n
const str = \"Test abc test test abc test test test abc test test abc\";\nconst modifiedStr = str.replaceAll('abc', '');\nconsole.log(modifiedStr);
\r\n
\r\n
\r\n

\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32381082fcc3049e91464", + "creator": "raven", + "createdAt": 1615546426000, + "text": "Uncaught TypeError: str.replaceAll is not a function",", + "upvotes": 147, + "upvoterUsernames": [], + "downvotes": 147, + "downvoterUsernames": [] + }, + { + "_id": "62f32381082fcc3049e91466", + "creator": "Nisharg Shah", + "createdAt": 1615606097000, + "text": "only the modern browser support it, if you are not using modern browsers then use regex", + "upvotes": 664, + "upvoterUsernames": [], + "downvotes": 664, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9023c", + "creator": "Force Bolt", + "createdAt": 1623075474000, + "text": "
// Try this way\n\nconst str = "Test abc test test abc test test test abc test test abc";\nconst result = str.split('abc').join('');\nconsole.log(result);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9023b", + "creator": "Satish Chandra Gupta", + "createdAt": 1618829718000, + "text": "

JavaScript provides a direct way to replace a part of a string with another string and there are some tricks also by which you can do this.

\n

To replace all the occurrences you can use replace() or replaceAll method in JavaScript.

\n
    \n
  1. replace() method - To replace all elements using this method use a regular expression as a pattern to find the matching string and then replace it with a new string. Please consider using the /g flag with it.
  2. \n
\n

\r\n
\r\n
const str = \"To do or not to do\";\nconst pattern = /do/g;\nconst replaceBy = \"Code\";\nconsole.log(str.replace(pattern, replaceBy));
\r\n
\r\n
\r\n

\n
    \n
  1. replaceAll() method - To replace all elements using this method, use either a string or regular expression as a pattern to find the matching string and then replace it with a new string. We must use the /g flag with a regular expression in the replaceAll method.
  2. \n
\n

\r\n
\r\n
const str = \"To do or not to do\";\nconst pattern = \"do\";\nconst replaceBy = \"Code\";\nconsole.log(str.replaceAll(pattern, replaceBy));\n\nconst pattern2 = /do/g;\nconsole.log(str.replaceAll(pattern2, replaceBy));
\r\n
\r\n
\r\n

\n

Alternate method: By using split and join method

\n

Split the string at what you want to replace and join by using the new string as a separator. See the example.

\n

\r\n
\r\n
const str = \"To do or not to do\";\nconst newStr = str.split(\"do\").join(\"Code\");\nconsole.log(newStr);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9023d", + "creator": "francis", + "createdAt": 1627217384000, + "text": "

With Regex i flag for case insensitive

\n
console.log('App started'.replace(/a/g, '')) // App strted\nconsole.log('App started'.replace(/a/gi, '')) // pp strted\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9023e", + "creator": "Oliver M Grech", + "createdAt": 1630318571000, + "text": "

After several trials and a lot of fails I found that the below function seemsto be the best all rounder when it comes to browser compatability, ease of use. This is the only working solution for older browsers that I found. (Yes, even though old browser are discouraged and outdated, some legacy apps still make heavy use of OLE browsers (such as old VB6 apps or excel xlsm macros with forms)

\n

Anyway, here's the simple function.

\n
function replaceAll(str, match, replacement){\n   return str.split(match).join(replacement);\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90240", + "creator": "Ran Turner", + "createdAt": 1634396609000, + "text": "

String.prototype.replaceAll - ECMAScript 2021

\n

The new String.prototype.replaceAll() method returns a new string with all matches of a pattern replaced by a replacement. The pattern can be either a string or a RegExp, and the replacement can be either a string or a function to be called for each match.

\n

\r\n
\r\n
const message = 'dog barks meow meow';\nconst messageFormatted = message.replaceAll('meow', 'woof')\n\nconsole.log(messageFormatted);
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32381082fcc3049e9146b", + "creator": "Sebastian Simon", + "createdAt": 1647851450000, + "text": "@RixTheTyrunt Of course this will work in an up-to-date version of Node.js.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e9023f", + "creator": "pedro casas", + "createdAt": 1632908907000, + "text": "

repeat until you have replaced them all

\n
const regex = /^>.*/im;\nwhile (regex.test(cadena)) {\n   cadena = cadena.replace(regex, '*');\n}   \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90241", + "creator": "Alecx", + "createdAt": 1638998644000, + "text": "

I have read this question and answers, but didn't find appropriate solution for me. Although this thread is quite useful, I decided to create my own solution from scratch. The problems about this kind of replacing are:

\n\n

So, I was writing functions to highlight search results in a table, where table data cells may have links inside, as well as other HTML tags. And these links were intended to be kept, so innerText was not enough.

\n

The solution I decided to provide for everyone with the same issues. Surely, you can use it not only for tables, but for any elements. Here is the code:

\n
/* Iterate over table data cells to insert a highlight tag */\nfunction highlightSearchResults(textFilter) {\n  textFilter = textFilter.toLowerCase().replace('<', '&lt;').replace('>', '&gt;');\n  let tds;\n  tb = document.getElementById('sometable'); //root element where to search\n  if (tb) {\n    tds = tb.getElementsByTagName("td"); //sub-elements where to make replacements\n  }\n  if (textFilter && tds) {\n    for (td of tds) {\n      //specify your span class or whatever you need before and after\n      td.innerHTML = insertCaseInsensitive(td.innerHTML, textFilter, '<span class="highlight">', '</span>');\n    }\n  }\n}\n\n/* Insert a highlight tag */\nfunction insertCaseInsensitive(srcStr, lowerCaseFilter, before, after) {\n  let lowStr = srcStr.toLowerCase();\n  let flen = lowerCaseFilter.length;\n  let i = -1;\n  while ((i = lowStr.indexOf(lowerCaseFilter, i + 1)) != -1) {\n    if (insideTag(i, srcStr)) continue;\n    srcStr = srcStr.slice(0, i) + before + srcStr.slice(i, i+flen) + after + srcStr.slice(i+flen);\n    lowStr = srcStr.toLowerCase();\n    i += before.length + after.length;\n  }\n  return srcStr;\n}\n\n/* Check if an ocurrence is inside any tag by index */\nfunction insideTag(si, s) {\n  let ahead = false;\n  let back = false;\n  for (let i = si; i < s.length; i++) {\n    if (s[i] == "<") {\n      break;\n    }\n    if (s[i] == ">") {\n      ahead = true;\n      break;\n    }\n  }\n  for (let i = si; i >= 0; i--) {\n    if (s[i] == ">") {\n      break;\n    }\n    if (s[i] == "<") {\n      back = true;\n      break;\n    }\n  }\n  return (ahead && back);\n}\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9020a", + "creator": "Guy", + "createdAt": 1410718332000, + "text": "

If using a library is an option for you then you will get the benefits of the testing and community support that goes with a library function. For example, the string.js library has a replaceAll() function that does what you're looking for:

\n\n
// Include a reference to the string.js library and call it (for example) S.\nstr = S(str).replaceAll('abc', '').s;\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90208", + "creator": "Cole Lawrence", + "createdAt": 1396637386000, + "text": "

This is the fastest version that doesn't use regular expressions.

\n\n

Revised jsperf

\n\n
replaceAll = function(string, omit, place, prevstring) {\n  if (prevstring && string === prevstring)\n    return string;\n  prevstring = string.replace(omit, place);\n  return replaceAll(prevstring, omit, place, string)\n}\n
\n\n

It is almost twice as fast as the split and join method.

\n\n

As pointed out in a comment here, this will not work if your omit variable contains place, as in: replaceAll(\"string\", \"s\", \"ss\"), because it will always be able to replace another occurrence of the word.

\n\n

There is another jsperf with variants on my recursive replace that go even faster (http://jsperf.com/replace-all-vs-split-join/12)!

\n\n\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e90209", + "creator": "zdennis", + "createdAt": 1398767112000, + "text": "
while (str.indexOf('abc') !== -1)\n{\n    str = str.replace('abc', '');\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9020c", + "creator": "Tim Rivoli", + "createdAt": 1412017944000, + "text": "
function replaceAll(str, find, replace) {\n  var i = str.indexOf(find);\n  if (i > -1){\n    str = str.replace(find, replace); \n    i = i + replace.length;\n    var st2 = str.substring(i);\n    if(st2.indexOf(find) > -1){\n      str = str.substring(0,i) + replaceAll(st2, find, replace);\n    }       \n  }\n  return str;\n}\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9020b", + "creator": "SamGoody", + "createdAt": 1411934322000, + "text": "

If you are trying to ensure that the string you are looking for won't exist even after the replacement, you need to use a loop.

\n\n

For example:

\n\n
var str = 'test aabcbc';\nstr = str.replace(/abc/g, '');\n
\n\n

When complete, you will still have 'test abc'!

\n\n

The simplest loop to solve this would be:

\n\n
var str = 'test aabcbc';\nwhile (str != str.replace(/abc/g, '')){\n   str.replace(/abc/g, '');\n}\n
\n\n

But that runs the replacement twice for each cycle. Perhaps (at risk of being voted down) that can be combined for a slightly more efficient but less readable form:

\n\n
var str = 'test aabcbc';\nwhile (str != (str = str.replace(/abc/g, ''))){}\n// alert(str); alerts 'test '!\n
\n\n

This can be particularly useful when looking for duplicate strings.
\nFor example, if we have 'a,,,b' and we wish to remove all duplicate commas.
\n[In that case, one could do .replace(/,+/g,','), but at some point the regex gets complex and slow enough to loop instead.]

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9020e", + "creator": "Chris Rosete", + "createdAt": 1434749061000, + "text": "

Replacing single quotes:

\n\n
function JavaScriptEncode(text){\n    text = text.replace(/'/g,'&apos;')\n    // More encode here if required\n\n    return text;\n}\n
\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e9020d", + "creator": "Reza Fahmi", + "createdAt": 1433488611000, + "text": "

Just add /g

\n\n
document.body.innerHTML = document.body.innerHTML.replace('hello', 'hi');\n
\n\n

to

\n\n
// Replace 'hello' string with /hello/g regular expression.\ndocument.body.innerHTML = document.body.innerHTML.replace(/hello/g, 'hi');\n
\n\n

/g means global

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321bd082fcc3049e9012b", + "creator": "sarea", + "createdAt": 1596003438000, + "text": "Use regex instead of string, should look like str.replace(/abc/g, ''); so g to get all matches.", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 4231505, + "uvac": 4231578 + } + }, + { + "_id": "62f321bb082fcc3049e8ff0a", + "title": "How do I empty an array in JavaScript?", + "title-lowercase": "how do i empty an array in javascript?", + "creator": "akano1", + "createdAt": 1249463319000, + "status": "open", + "text": "

Is there a way to empty an array and if so possibly with .remove()?

\n\n

For instance,

\n\n
A = [1,2,3,4];\n
\n\n

How can I empty that?

\n", + "upvotes": 2273, + "upvoterUsernames": [], + "downvotes": 78, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 3614885, + "answers": 16, + "answerItems": [ + { + "_id": "62f3220c082fcc3049e90fdd", + "creator": "cssimsek", + "createdAt": 1380628002000, + "text": "

Use a modified version of Jan's initial suggestion:

\n
var originalLength = A.length;\nfor (var i = originalLength; i > 0; i--) {\n     A.pop();\n}\n
\n

Terser:

\n
for (let i = A.length; i > 0;A.pop(),i--) {}\n
\n

Or here's another take:

\n
while(!A[Symbol.iterator]().next().done)A.shift()\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92c97", + "creator": "klh", + "createdAt": 1410735063000, + "text": "Why would you want to do such thing? Why add two more variables and a bunch of code to do the same thing?", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fde", + "creator": "matanster", + "createdAt": 1386803999000, + "text": "

In case you are interested in the memory allocation, you may compare each approach using something like this jsfiddle in conjunction with chrome dev tools' timeline tab. You will want to use the trash bin icon at the bottom to force a garbage collection after 'clearing' the array. This should give you a more definite answer for the browser of your choice. A lot of answers here are old and I wouldn't rely on them but rather test as in @tanguy_k's answer above.

\n\n

(for an intro to the aforementioned tab you can check out here)

\n\n

Stackoverflow forces me to copy the jsfiddle so here it is:

\n\n
<html>\n<script>\nvar size = 1000*100\nwindow.onload = function() {\n  document.getElementById(\"quantifier\").value = size\n}\n\nfunction scaffold()\n{\n  console.log(\"processing Scaffold...\");\n  a = new Array\n}\nfunction start()\n{\n  size = document.getElementById(\"quantifier\").value\n  console.log(\"Starting... quantifier is \" + size);\n  console.log(\"starting test\")\n  for (i=0; i<size; i++){\n    a[i]=\"something\"\n  }\n  console.log(\"done...\")\n}\n\nfunction tearDown()\n{\n  console.log(\"processing teardown\");\n  a.length=0\n}\n\n</script>\n<body>\n    <span style=\"color:green;\">Quantifier:</span>\n    <input id=\"quantifier\" style=\"color:green;\" type=\"text\"></input>\n    <button onclick=\"scaffold()\">Scaffold</button>\n    <button onclick=\"start()\">Start</button>\n    <button onclick=\"tearDown()\">Clean</button>\n    <br/>\n</body>\n</html>\n
\n\n

And you should take note that it may depend on the type of the array elements, as javascript manages strings differently than other primitive types, not to mention arrays of objects. The type may affect what happens.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fda", + "creator": "leech", + "createdAt": 1355194692000, + "text": "

You can add this to your JavaScript file to allow your arrays to be \"cleared\":

\n\n
Array.prototype.clear = function() {\n    this.splice(0, this.length);\n};\n
\n\n

Then you can use it like this:

\n\n
var list = [1, 2, 3];\nlist.clear();\n
\n\n

Or if you want to be sure you don't destroy something:

\n\n
if (!Array.prototype.clear) {\n    Array.prototype.clear = function() {\n       this.splice(0, this.length);\n    };\n}\n
\n\n

Lots of people think you shouldn't modify native objects (like Array), and I'm inclined to agree. Please use caution in deciding how to handle this.

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92c99", + "creator": "Undefined", + "createdAt": 1379348680000, + "text": "@naomik Can you explain your reasoning why doing such a thing is frowned upon?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92c9b", + "creator": "ErikE", + "createdAt": 1439510438000, + "text": "How about the problem where doing a foreach over the members of an array will suddenly start including a clear key?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92c9d", + "creator": "Emile Bergeron", + "createdAt": 1505232201000, + "text": "As a reference: Why is extending native objects a bad practice?", + "upvotes": 1281, + "upvoterUsernames": [], + "downvotes": 1281, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fdf", + "creator": "David Campbell", + "createdAt": 1391023449000, + "text": "

A.splice(0);

\n\n

I just did this on some code I am working on. It cleared the array.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fe0", + "creator": "kenshou.html", + "createdAt": 1400051642000, + "text": "

Performance test:

\n\n

http://jsperf.com/array-clear-methods/3

\n\n
a = []; // 37% slower\na.length = 0; // 89% slower\na.splice(0, a.length)  // 97% slower\nwhile (a.length > 0) {\n    a.pop();\n} // Fastest\n
\n", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92ca0", + "creator": "Reza-S4", + "createdAt": 1429088380000, + "text": "Testing in Firefox 39.0 32-bit on Windows NT 6.3 64-bit, the a=[] is fastest !", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92ca2", + "creator": "Philippe Leybaert", + "createdAt": 1479356035000, + "text": "@chqrlie It's not. It's the slowest method. The benchmark test is flawed.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92ca3", + "creator": "James Wakefield", + "createdAt": 1494221748000, + "text": "Please delete this answer as it is wrong, and links to a meaningless flawed test as fake evidence.", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fdb", + "creator": "Bendegúz", + "createdAt": 1361568536000, + "text": "
Array.prototype.clear = function() {\n    this.length = 0;\n};\n
\n\n

And call it: array.clear();

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92ca6", + "creator": "Mulan", + "createdAt": 1373179981000, + "text": "Please don't encourage modification of the native objects.", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92ca7", + "creator": "Design by Adrian", + "createdAt": 1395761040000, + "text": "Why not just type array.length = 0?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92ca8", + "creator": "Dennis", + "createdAt": 1419256219000, + "text": "How could this get any upvote? No browser I know allows this = ... EDIT: I see, answer was different in previous versions.", + "upvotes": 401, + "upvoterUsernames": [], + "downvotes": 401, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92ca9", + "creator": "Emile Bergeron", + "createdAt": 1505232169000, + "text": "As a reference: Why is extending native objects a bad practice?", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92caa", + "creator": "Loligans", + "createdAt": 1518652120000, + "text": "I don't agree. Adding prototypes to native objects is a bad practice. Look at @EmileBergeron link as to why.", + "upvotes": 273, + "upvoterUsernames": [], + "downvotes": 273, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fdc", + "creator": "tanguy_k", + "createdAt": 1372192330000, + "text": "

Here the fastest working implementation while keeping the same array ("mutable"):

\n
function clearArray(array) {\n  while (array.length > 0) {\n    array.pop();\n  }\n}\n
\n

FYI it cannot be simplified to while (array.pop()): the tests will fail.

\n

FYI Map and Set define clear(), it would have seem logical to have clear() for Array too.

\n

TypeScript version:

\n
function clearArray<T>(array: T[]) {\n  while (array.length > 0) {\n    array.pop();\n  }\n}\n
\n

The corresponding tests:

\n
describe('clearArray()', () => {\n  test('clear regular array', () => {\n    const array = [1, 2, 3, 4, 5];\n    clearArray(array);\n    expect(array.length).toEqual(0);\n    expect(array[0]).toEqual(undefined);\n    expect(array[4]).toEqual(undefined);\n  });\n\n  test('clear array that contains undefined and null', () => {\n    const array = [1, undefined, 3, null, 5];\n    clearArray(array);\n    expect(array.length).toEqual(0);\n    expect(array[0]).toEqual(undefined);\n    expect(array[4]).toEqual(undefined);\n  });\n});\n
\n

Here the updated jsPerf: http://jsperf.com/array-destroy/32 http://jsperf.com/array-destroy/152

\n

jsPerf offline. Similar benchmark: https://jsben.ch/hyj65

\n", + "upvotes": 547, + "upvoterUsernames": [], + "downvotes": 229, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92cad", + "creator": "thefourtheye", + "createdAt": 1376887401000, + "text": "@naomik But this is one of the basic functionalities, which should have been there by default.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92caf", + "creator": "thefourtheye", + "createdAt": 1379171397000, + "text": "@jpillora So what do you suggest? Its okay to add unless there is a function by the name already exists?", + "upvotes": 1770, + "upvoterUsernames": [], + "downvotes": 1770, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cb0", + "creator": "Iván Pérez", + "createdAt": 1563950461000, + "text": "No need for the use of generics in the TypeScript version. The parameter can be just array: any[].", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fd9", + "creator": "Anthony", + "createdAt": 1321350577000, + "text": "

A more cross-browser friendly and more optimal solution will be to use the splice method to empty the content of the array A as below:

\n\n

A.splice(0, A.length);

\n", + "upvotes": 363, + "upvoterUsernames": [], + "downvotes": 113, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92cb3", + "creator": "stricjux", + "createdAt": 1321888324000, + "text": "Why is this more cross-browser friendly? What browsers have issues with A.length?", + "upvotes": 111, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cb5", + "creator": "Shamasis Bhattacharya", + "createdAt": 1337761972000, + "text": "This is the most correct answer since this actually "clears the array content and retains the reference to the original array object.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cb7", + "creator": "alex", + "createdAt": 1351552787000, + "text": "@David Don't know why I wrote that. I must have missed the p and confused it with slice().", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cb8", + "creator": "corwin.amber", + "createdAt": 1417715353000, + "text": "I have also found that just A.splice(0) also works.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fd8", + "creator": "Matthew Crumley", + "createdAt": 1249489797000, + "text": "

If you need to keep the original array because you have other references to it that should be updated too, you can clear it without creating a new array by setting its length to zero:

\n\n
A.length = 0;\n
\n", + "upvotes": 3140, + "upvoterUsernames": [], + "downvotes": 504, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32972082fcc3049e92cbb", + "creator": "Pacerier", + "createdAt": 1308639603000, + "text": "what does ECMAScript 5 Standard says about this?", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cbc", + "creator": "Matthew Crumley", + "createdAt": 1357309130000, + "text": "@LosManos Even in strict mode, length is a special property, but not read only, so it will still work.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cbd", + "creator": "LosManos", + "createdAt": 1357558886000, + "text": "@MatthewCrumley As a serious developer I am not sure without being able to reproduce it and not even then...", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cbf", + "creator": "thefourtheye", + "createdAt": 1376887321000, + "text": "@VKen does it mean that, it corrects the length automatically?", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc0", + "creator": "thefourtheye", + "createdAt": 1376914741000, + "text": "@VKen Thanks for the reference. So, who will delete the array elements? Garbage collector?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc1", + "creator": "Hatoru Hansou", + "createdAt": 1424292974000, + "text": "@PhilippeLeybaert True, by calling those you do not invalidate the references. I still like length = 0 opposing to ref.splice(0, ref.length)", + "upvotes": 3737, + "upvoterUsernames": [], + "downvotes": 3737, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc3", + "creator": "NeverGiveUp161", + "createdAt": 1495447616000, + "text": "how to update it so that it makes the length=0 in all the references?", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc5", + "creator": "magor", + "createdAt": 1513598716000, + "text": "setting length to 0 does not work when i have an array of objects", + "upvotes": 5855, + "upvoterUsernames": [], + "downvotes": 5855, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc7", + "creator": "Matthew Crumley", + "createdAt": 1513611977000, + "text": "@mazs I'm not sure what you mean. It shouldn't care what type of values are in the array.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cc8", + "creator": "Gaurav Gandhi", + "createdAt": 1561447290000, + "text": "This solution helps with using arrays via Proxy, thanks.", + "upvotes": 151, + "upvoterUsernames": [], + "downvotes": 151, + "downvoterUsernames": [] + }, + { + "_id": "62f32972082fcc3049e92cca", + "creator": "Blessing", + "createdAt": 1615358155000, + "text": "Won't the first method will cause memory leakage?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe2", + "creator": "Diadistis", + "createdAt": 1424112752000, + "text": "

There is a lot of confusion and misinformation regarding the while;pop/shift performance both in answers and comments. The while/pop solution has (as expected) the worst performance. What's actually happening is that setup runs only once for each sample that runs the snippet in a loop. eg:

\n\n
var arr = [];\n\nfor (var i = 0; i < 100; i++) { \n    arr.push(Math.random()); \n}\n\nfor (var j = 0; j < 1000; j++) {\n    while (arr.length > 0) {\n        arr.pop(); // this executes 100 times, not 100000\n    }\n}\n
\n\n

I have created a new test that works correctly :

\n\n

http://jsperf.com/empty-javascript-array-redux

\n\n

Warning: even in this version of the test you can't actually see the real difference because cloning the array takes up most of the test time. It still shows that splice is the fastest way to clear the array (not taking [] into consideration because while it is the fastest it's not actually clearing the existing array).

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92ccc", + "creator": "Philippe Leybaert", + "createdAt": 1424130643000, + "text": "Very good point! I'll update the original answer with the correct benchmark results.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32973082fcc3049e92ccd", + "creator": "Philippe Leybaert", + "createdAt": 1424132143000, + "text": "I can't believe nobody spotted that benchmark error. With over half a million views you would expect someone to notice it. Great work Diadistis", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe1", + "creator": "Laxmikant Dange", + "createdAt": 1417442820000, + "text": "

If you are using

\n\n
a = []; \n
\n\n

Then you are assigning new array reference to a, if reference in a is already assigned to any other variable, then it will not empty that array too and hence garbage collector will not collect that memory.

\n\n

For ex.

\n\n
var a=[1,2,3];\nvar b=a;\na=[];\nconsole.log(b);// It will print [1,2,3];\n
\n\n

or

\n\n
a.length = 0;\n
\n\n

When we specify a.length, we are just resetting boundaries of the array and memory for rest array elements will be connected by garbage collector.

\n\n

Instead of these two solutions are better.

\n\n
a.splice(0,a.length)\n
\n\n

and

\n\n
while(a.length > 0) {\n    a.pop();\n}\n
\n\n

As per previous answer by kenshou.html, second method is faster.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92ccf", + "creator": "Bergi", + "createdAt": 1417444385000, + "text": "Apart from being wrong on a.length, I don't see what this new answer adds to the thread?", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32973082fcc3049e92cd0", + "creator": "Laxmikant Dange", + "createdAt": 1417512655000, + "text": "@Bergi I just want to focus about actual memory representation about array", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe4", + "creator": "Damian Pavlica", + "createdAt": 1487810368000, + "text": "

If you use constants then you have no choice:

\n\n
const numbers = [1, 2, 3]\n
\n\n

You can not reasign:

\n\n
numbers = []\n
\n\n

You can only truncate:

\n\n
numbers.length = 0\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fe3", + "creator": "Bekim Bacaj", + "createdAt": 1462029494000, + "text": "

The answers that have no less that 2739 upvotes by now are misleading and incorrect.

\n\n

The question is: \"How do you empty your existing array?\" E.g. for A = [1,2,3,4].

\n\n
    \n
  1. Saying \"A = [] is the answer\" is ignorant and absolutely incorrect. [] == [] is false.

    \n\n

    This is because these two arrays are two separate, individual objects, with their own two identities, taking up their own space in the digital world, each on its own.

  2. \n
\n\n
\n\n

Let's say your mother asks you to empty the trash can.

\n\n\n\n

Emptying an array object is the easiest thing ever:

\n\n
A.length = 0;\n
\n\n

This way, the can under \"A\" is not only empty, but also as clean as new!

\n\n
\n\n
    \n
  1. Furthermore, you are not required to remove the trash by hand until the can is empty! You were asked to empty the existing one, completely, in one turn, not to pick up the trash until the can gets empty, as in:

    \n\n
    while(A.length > 0) {\n    A.pop();\n}\n
  2. \n
  3. Nor, to put your left hand at the bottom of the trash, holding it with your right at the top to be able to pull its content out as in:

    \n\n
    A.splice(0, A.length);\n
  4. \n
\n\n

No, you were asked to empty it:

\n\n
A.length = 0;\n
\n\n

This is the only code that correctly empties the contents of a given JavaScript array.

\n", + "upvotes": 171, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92cd2", + "creator": "Victor", + "createdAt": 1532208308000, + "text": "I loved the metaphors with the real world in this answer. Impressive!", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe6", + "creator": "Manish Jain", + "createdAt": 1494523708000, + "text": "

Use below if you need to empty Angular 2+ FormArray.

\n\n
public emptyFormArray(formArray:FormArray) {\n    for (let i = formArray.controls.length - 1; i >= 0; i--) {\n        formArray.removeAt(i);\n    }\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92cd3", + "creator": "Emile Bergeron", + "createdAt": 1505232812000, + "text": "This is irrelevant and not even JavaScript.", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe5", + "creator": "Cauterite", + "createdAt": 1488528121000, + "text": "

I'm surprised no one has suggested this yet:

\n\n
let xs = [1,2,3,4];\nfor (let i in xs)\n    delete xs[i];\n
\n\n

This yields an array in quite a different state from the other solutions. In a sense, the array has been 'emptied':

\n\n
xs\n=> Array [ <4 empty slots> ]\n\n[...xs]\n=> Array [ undefined, undefined, undefined, undefined ]\n\nxs.length\n=> 4\n\nxs[0]\n=> ReferenceError: reference to undefined property xs[0]\n
\n\n

You can produce an equivalent array with [,,,,] or Array(4)

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92cd5", + "creator": "Ruben", + "createdAt": 1490468098000, + "text": "It could be a good answer to a different question: "How to convert all elements in an array to undefined elements".", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32973082fcc3049e92cd7", + "creator": "Cauterite", + "createdAt": 1566536348000, + "text": "Though internally there is a difference between undefined elements and empty slots ("holes")", + "upvotes": 1182, + "upvoterUsernames": [], + "downvotes": 1182, + "downvoterUsernames": [] + }, + { + "_id": "62f32973082fcc3049e92cd9", + "creator": "Sebastian Simon", + "createdAt": 1639666473000, + "text": "Then this can be shortened to const originalLength = array.length; array.length = 0; array.length = originalLength;.", + "upvotes": 4654, + "upvoterUsernames": [], + "downvotes": 4654, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fe7", + "creator": "Alireza", + "createdAt": 1495621722000, + "text": "

You can easily create a function to do that for you, change the length or even add it to native Array as remove() function for reuse.

\n\n

Imagine you have this array:

\n\n
var arr = [1, 2, 3, 4, 5]; //the array\n
\n\n

OK, just simply run this:

\n\n
arr.length = 0; //change the length\n
\n\n

and the result is:

\n\n
[] //result\n
\n\n

easy way to empty an array...

\n\n

Also using loop which is not necessary but just another way to do that:

\n\n
/* could be arr.pop() or arr.splice(0)\ndon't need to return as main array get changed */\n\nfunction remove(arr) {\n  while(arr.length) {\n    arr.shift(); \n  }\n}\n
\n\n

There are also tricky way which you can think about, for example something like this:

\n\n
arr.splice(0, arr.length); //[]\n
\n\n

So if arr has 5 items, it will splice 5 items from 0, which means nothing will remain in the array.

\n\n

Also other ways like simply reassign the array for example:

\n\n
arr = []; //[]\n
\n\n

If you look at the Array functions, there are many other ways to do this, but the most recommended one could be changing the length.

\n\n

As I said in the first place, you can also prototype remove() as it's the answer to your question. you can simply choose one of the methods above and prototype it to Array object in JavaScript, something like:

\n\n
Array.prototype.remove = Array.prototype.remove || function() {\n  this.splice(0, this.length);\n};\n
\n\n

and you can simply call it like this to empty any array in your javascript application:

\n\n
arr.remove(); //[]\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32973082fcc3049e92cda", + "creator": "Therichpost", + "createdAt": 1587545846000, + "text": "arr.length = 0; //change the length", + "upvotes": 356, + "upvoterUsernames": [], + "downvotes": 356, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f57", + "creator": "EscapeNetscape", + "createdAt": 1477325838000, + "text": "here a benchmark with different possibilities: jsben.ch/#/7QyI1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3617159, + "uvac": 3617175 + } + }, + { + "_id": "62f321bb082fcc3049e8feae", + "title": "What does "use strict" do in JavaScript, and what is the reasoning behind it?", + "title-lowercase": "what does "use strict" do in javascript, and what is the reasoning behind it?", + "creator": "Mark Rogers", + "createdAt": 1251303013000, + "status": "open", + "text": "

Recently, I ran some of my JavaScript code through Crockford's JSLint, and it gave the following error:

\n
\n

Problem at line 1 character 1: Missing "use strict" statement.

\n
\n

Doing some searching, I realized that some people add "use strict"; into their JavaScript code. Once I added the statement, the error stopped appearing. Unfortunately, Google did not reveal much of the history behind this string statement. Certainly it must have something to do with how the JavaScript is interpreted by the browser, but I have no idea what the effect would be.

\n

So what is "use strict"; all about, what does it imply, and is it still relevant?

\n

Do any of the current browsers respond to the "use strict"; string or is it for future use?

\n", + "upvotes": 13265, + "upvoterUsernames": [], + "downvotes": 5033, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1180048, + "answers": 28, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e8ff51", + "creator": "Stephen", + "createdAt": 1331350275000, + "text": "

If you use a browser released in the last year or so then it most likely supports JavaScript Strict mode. Only older browsers around before ECMAScript 5 became the current standard don't support it.

\n\n

The quotes around the command make sure that the code will still work in older browsers as well (although the things that generate a syntax error in strict mode will generally just cause the script to malfunction in some hard to detect way in those older browsers).

\n", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff52", + "creator": "Jamie Hutber", + "createdAt": 1342394753000, + "text": "

If people are worried about using use strict it might be worth checking out this article:

\n\n

ECMAScript 5 'Strict mode' support in browsers. What does this mean?
\n NovoGeek.com - Krishna's weblog

\n\n

It talks about browser support, but more importantly how to deal with it safely:

\n\n
function isStrictMode(){\n    return !this;\n} \n/*\n   returns false, since 'this' refers to global object and \n   '!this' becomes false\n*/\n\nfunction isStrictMode(){   \n    \"use strict\";\n    return !this;\n} \n/* \n   returns true, since in strict mode the keyword 'this'\n   does not refer to global object, unlike traditional JS. \n   So here, 'this' is 'undefined' and '!this' becomes true.\n*/\n
\n", + "upvotes": 466, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff54", + "creator": "Pank", + "createdAt": 1373053104000, + "text": "

I strongly recommend every developer to start using strict mode now. There are enough browsers supporting it that strict mode will legitimately help save us from errors we didn’t even know were in your code.

\n\n

Apparently, at the initial stage there will be errors we have never encountered before. To get the full benefit, we need to do proper testing after switching to strict mode to make sure we have caught everything. Definitely we don’t just throw use strict in our code and assume there are no errors. So the churn is that it’s time to start using this incredibly useful language feature to write better code.

\n\n

For example,

\n\n
var person = {\n    name : 'xyz',\n    position : 'abc',\n    fullname : function () {  \"use strict\"; return this.name; }\n};\n
\n\n

JSLint is a debugger written by Douglas Crockford. Simply paste in your script, and it’ll quickly scan for any noticeable issues and errors in your code.

\n", + "upvotes": 198, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff53", + "creator": "user2436758", + "createdAt": 1370024973000, + "text": "

\"Use Strict\"; is an insurance that programmer will not use the loose or the bad properties of JavaScript. It is a guide, just like a ruler will help you make straight lines. \"Use Strict\" will help you do \"Straight coding\".

\n\n

Those that prefer not to use rulers to do their lines straight usually end up in those pages asking for others to debug their code.

\n\n

Believe me. The overhead is negligible compared to poorly designed code. Doug Crockford, who has been a senior JavaScript developer for several years, has a very interesting post here. Personally, I like to return to his site all the time to make sure I don't forget my good practice.

\n\n

Modern JavaScript practice should always evoke the \"Use Strict\"; pragma. The only reason that the ECMA Group has made the \"Strict\" mode optional is to permit less experienced coders access to JavaScript and give then time to adapt to the new and safer coding practices.

\n", + "upvotes": 93, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff56", + "creator": "Renganathan M G", + "createdAt": 1395922710000, + "text": "

Strict mode makes several changes to normal JavaScript semantics:

\n\n\n\n

for more information vistit Strict Mode- Javascript

\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff55", + "creator": "DWoldrich", + "createdAt": 1393832239000, + "text": "

A word of caution, all you hard-charging programmers: applying \"use strict\" to existing code can be hazardous! This thing is not some feel-good, happy-face sticker that you can slap on the code to make it 'better'. With the \"use strict\" pragma, the browser will suddenly THROW exceptions in random places that it never threw before just because at that spot you are doing something that default/loose JavaScript happily allows but strict JavaScript abhors! You may have strictness violations hiding in seldom used calls in your code that will only throw an exception when they do eventually get run - say, in the production environment that your paying customers use!

\n\n

If you are going to take the plunge, it is a good idea to apply \"use strict\" alongside comprehensive unit tests and a strictly configured JSHint build task that will give you some confidence that there is no dark corner of your module that will blow up horribly just because you've turned on Strict Mode. Or, hey, here's another option: just don't add \"use strict\" to any of your legacy code, it's probably safer that way, honestly. DEFINITELY DO NOT add \"use strict\" to any modules you do not own or maintain, like third party modules.

\n\n

I think even though it is a deadly caged animal, \"use strict\" can be good stuff, but you have to do it right. The best time to go strict is when your project is greenfield and you are starting from scratch. Configure JSHint/JSLint with all the warnings and options cranked up as tight as your team can stomach, get a good build/test/assert system du jour rigged like Grunt+Karma+Chai, and only THEN start marking all your new modules as \"use strict\". Be prepared to cure lots of niggly errors and warnings. Make sure everyone understands the gravity by configuring the build to FAIL if JSHint/JSLint produces any violations.

\n\n

My project was not a greenfield project when I adopted \"use strict\". As a result, my IDE is full of red marks because I don't have \"use strict\" on half my modules, and JSHint complains about that. It's a reminder to me about what refactoring I should do in the future. My goal is to be red mark free due to all of my missing \"use strict\" statements, but that is years away now.

\n", + "upvotes": 437, + "upvoterUsernames": [], + "downvotes": 189, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff58", + "creator": "Placeholder", + "createdAt": 1409921606000, + "text": "

Including use strict in the beginning of your all sensitive JavaScript files from this point is a small way to be a better JavaScript programmer and avoid random variables becoming global and things change silently.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff57", + "creator": "FutureNerd", + "createdAt": 1396054044000, + "text": "

There's a good talk by some people who were on the ECMAScript committee: Changes to JavaScript, Part 1: ECMAScript 5\" about how incremental use of the \"use strict\" switch allows JavaScript implementers to clean up a lot of the dangerous features of JavaScript without suddenly breaking every website in the world.

\n\n

Of course it also talks about just what a lot of those misfeatures are (were) and how ECMAScript 5 fixes them.

\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff59", + "creator": "Shubh", + "createdAt": 1413898284000, + "text": "

My two cents:

\n\n

One of the goals of strict mode is to allow for faster debugging of issues. It helps the developers by throwing exception when certain wrong things occur that can cause silent & strange behaviour of your webpage. The moment we use use strict, the code will throw out errors which helps developer to fix it in advance.

\n\n

Few important things which I have learned after using use strict :

\n\n

Prevents Global Variable Declaration:

\n\n
var tree1Data = { name: 'Banana Tree',age: 100,leafCount: 100000};\n\nfunction Tree(typeOfTree) {\n    var age;\n    var leafCount;\n\n    age = typeOfTree.age;\n    leafCount = typeOfTree.leafCount;\n    nameoftree = typeOfTree.name;\n};\n\nvar tree1 = new Tree(tree1Data);\nconsole.log(window);\n
\n\n

Now,this code creates nameoftree in global scope which could be accessed using window.nameoftree. When we implement use strict the code would throw error.

\n\n
\n

Uncaught ReferenceError: nameoftree is not defined

\n
\n\n

Sample

\n\n

Eliminates with statement :

\n\n

with statements can't be minified using tools like uglify-js. They're also deprecated and removed from future JavaScript versions.

\n\n

Sample

\n\n

Prevents Duplicates :

\n\n

When we have duplicate property, it throws an exception

\n\n
\n

Uncaught SyntaxError: Duplicate data property in object literal not\n allowed in strict mode

\n
\n\n
\"use strict\";\nvar tree1Data = {\n    name: 'Banana Tree',\n    age: 100,\n    leafCount: 100000,\n    name:'Banana Tree'\n};\n
\n\n

There are few more but I need to gain more knowledge on that.

\n", + "upvotes": 176, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff5a", + "creator": "gprasant", + "createdAt": 1416864128000, + "text": "

The statement "use strict"; instructs the browser to use the Strict mode, which is a reduced and safer feature set of JavaScript.

\n

List of features (non-exhaustive)

\n
    \n
  1. Disallows global variables. (Catches missing var declarations and typos in variable names)

    \n
  2. \n
  3. Silent failing assignments will throw error in strict mode (assigning NaN = 5;)

    \n
  4. \n
  5. Attempts to delete undeletable properties will throw (delete Object.prototype)

    \n
  6. \n
  7. Requires all property names in an object literal to be unique (var x = {x1: "1", x1: "2"})

    \n
  8. \n
  9. Function parameter names must be unique (function sum (x, x) {...})

    \n
  10. \n
  11. Forbids octal syntax (var x = 023; some devs assume wrongly that a preceding zero does nothing to change the number.)

    \n
  12. \n
  13. Forbids the with keyword

    \n
  14. \n
  15. eval in strict mode does not introduce new variables

    \n
  16. \n
  17. Forbids deleting plain names (delete x;)

    \n
  18. \n
  19. Forbids binding or assignment of the names eval and arguments in any form

    \n
  20. \n
  21. Strict mode does not alias properties of the arguments object with the formal parameters. (e.g. in function sum (a,b) { return arguments[0] + b;} This works because arguments[0] is bound to a and so on. ) (See examples section below to understand the difference)

    \n
  22. \n
  23. arguments.callee is not supported

    \n
  24. \n
\n

[Ref: Strict mode, Mozilla Developer Network]

\n
\n

Examples:

\n
    \n
  1. Strict mode code doesn't alias properties of arguments objects created within it
  2. \n
\n
function show( msg ){\n    msg = 42;\n    console.log( msg );          // msg === 42\n    console.log( arguments[0] ); // arguments === 42\n}\nshow( "Hey" );\n\n// In strict mode arguments[i] does not track the value of \n// the corresponding named argument, nor does a named argument track the value in the corresponding arguments[i]\nfunction showStrict( msg ){\n    "use strict";\n    msg = 42;\n    console.log( msg );          // msg === 42\n    console.log( arguments[0] ); // arguments === "Hey"\n}\nshowStrict( "Hey" );\n
\n", + "upvotes": 1610, + "upvoterUsernames": [], + "downvotes": 803, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3224e082fcc3049e9108a", + "creator": "RobG", + "createdAt": 1615505789000, + "text": "The example for 11. is unclear, it's not clear what the difference in strict mode is.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ff5b", + "creator": "Heich-B", + "createdAt": 1430302219000, + "text": "

Quoting from w3schools:

\n\n
\n

The \"use strict\" Directive

\n \n

The \"use strict\" directive is new in JavaScript 1.8.5 (ECMAScript\n version 5).

\n \n

It is not a statement, but a literal expression, ignored by earlier\n versions of JavaScript.

\n \n

The purpose of \"use strict\" is to indicate that the code should be\n executed in \"strict mode\".

\n \n

With strict mode, you can not, for example, use undeclared variables.

\n \n

Why Strict Mode?

\n \n

Strict mode makes it easier to write \"secure\" JavaScript.

\n \n

Strict mode changes previously accepted \"bad syntax\" into real errors.

\n \n

As an example, in normal JavaScript, mistyping a variable name creates\n a new global variable. In strict mode, this will throw an error,\n making it impossible to accidentally create a global variable.

\n \n

In normal JavaScript, a developer will not receive any error feedback\n assigning values to non-writable properties.

\n \n

In strict mode, any assignment to a non-writable property, a\n getter-only property, a non-existing property, a non-existing\n variable, or a non-existing object, will throw an error.

\n
\n\n

Please refer to http://www.w3schools.com/js/js_strict.asp to know more

\n", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff5c", + "creator": "zangw", + "createdAt": 1450840238000, + "text": "

When adding \"use strict\";, the following cases will throw a SyntaxError before the script is executing:

\n\n\n\n

Sources:

\n\n\n", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff5e", + "creator": "PippoApps.com", + "createdAt": 1462280343000, + "text": "

\"use strict\"; is the ECMA effort to make JavaScript a little bit more robust. It brings in JS an attempt to make it at least a little \"strict\" (other languages implement strict rules since the 90s). It actually \"forces\" JavaScript developers to follow some sort of coding best practices.\nStill, JavaScript is very fragile. There is no such thing as typed variables, typed methods, etc.\nI strongly recommend JavaScript developers to learn a more robust language such as Java or ActionScript3, and implement the same best practices in your JavaScript code, it will work better and be easier to debug.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff5d", + "creator": "Oriol", + "createdAt": 1460420707000, + "text": "

Note that use strict was introduced in EcmaScript 5 and was kept since then.

\n\n

Below are the conditions to trigger strict mode in ES6 and ES7:

\n\n
\n \n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff5f", + "creator": "Просто программист", + "createdAt": 1463524289000, + "text": "

use strict is a way to make your code safer, because you can't use dangerous features that can work not as you expect. And, as was written before, it makes code more strict.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff61", + "creator": "Wesam", + "createdAt": 1476712755000, + "text": "

Use Strict is used to show common and repeated errors so that it is handled differently , and changes the way java script runs , such changes are :

\n\n\n\n

you can also read this article for the details

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff60", + "creator": "Tân", + "createdAt": 1471815804000, + "text": "

Small examples to compare:

\n\n

Non-strict mode:

\n\n

\r\n
\r\n
for (i of [1,2,3]) console.log(i)\r\n    \r\n// output:\r\n// 1\r\n// 2\r\n// 3
\r\n
\r\n
\r\n

\n\n

Strict mode:

\n\n

\r\n
\r\n
'use strict';\r\nfor (i of [1,2,3]) console.log(i)\r\n\r\n// output:\r\n// Uncaught ReferenceError: i is not defined
\r\n
\r\n
\r\n

\n\n

Non-strict mode:

\n\n

\r\n
\r\n
String.prototype.test = function () {\r\n  console.log(typeof this === 'string');\r\n};\r\n\r\n'a'.test();\r\n\r\n// output\r\n// false
\r\n
\r\n
\r\n

\n\n

\r\n
\r\n
String.prototype.test = function () {\r\n  'use strict';\r\n  \r\n  console.log(typeof this === 'string');\r\n};\r\n\r\n'a'.test();\r\n\r\n// output\r\n// true
\r\n
\r\n
\r\n

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff62", + "creator": "Rabin Pantha", + "createdAt": 1478842845000, + "text": "

JavaScript “strict” mode was introduced in ECMAScript 5.

\n\n
(function() {\n  \"use strict\";\n  your code...\n})();\n
\n\n

Writing \"use strict\"; at the very top of your JS file turns on strict\nsyntax checking. It does the following tasks for us:

\n\n
    \n
  1. shows an error if you try to assign to an undeclared variable

  2. \n
  3. stops you from overwriting key JS system libraries

  4. \n
  5. forbids some unsafe or error-prone language features

  6. \n
\n\n

use strict also works inside of individual functions. It is always a better practice to include use strict in your code.

\n\n

Browser compatibility issue: \nThe \"use\" directives are meant to be backwards-compatible. Browsers that do not support them will just see a string literal that isn't referenced further. So, they will pass over it and move on.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff63", + "creator": "Pritam Banerjee", + "createdAt": 1479462835000, + "text": "

The main reasons why developers should use \"use strict\" are:

\n\n
    \n
  1. Prevents accidental declaration of global variables.Using \"use strict()\" will make sure that variables are declared with var before use. \nEg:

    \n\n
    function useStrictDemo(){\n 'use strict';\n //works fine\n var a = 'No Problem';\n\n //does not work fine and throws error\n k = \"problem\"\n\n //even this will throw error\n someObject = {'problem': 'lot of problem'};\n}\n
  2. \n
  3. N.B: The \"use strict\" directive is only recognized at the beginning of a script or a function.
  4. \n
  5. The string \"arguments\" cannot be used as a variable:

    \n\n
    \"use strict\";\nvar arguments = 3.14;    // This will cause an error\n
  6. \n
  7. Will restrict uses of keywords as variables. Trying to use them will throw errors.

  8. \n
\n\n

In short will make your code less error prone and in turn will make you write good code.

\n\n

To read more about it you can refer here.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff64", + "creator": "Bikash Chapagain", + "createdAt": 1479659034000, + "text": "

Normally, JavaScript does not follow strict rules, hence increasing chances of errors. After using \"use strict\", the JavaScript code should follow strict set of rules as in other programming languages such as use of terminators, declaration before initialization, etc.

\n\n

If \"use strict\" is used, the code should be written by following a strict set of rules, hence decreasing the chances of errors and ambiguities.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff66", + "creator": "Ashish", + "createdAt": 1548672145000, + "text": "

\"use strict\"; Defines that JavaScript code should be executed in\n \"strict mode\".

\n\n\n\n

All modern browsers support \"use strict\" except Internet Explorer 9 and lower.

\n\n

Disadvantage

\n\n

If a developer used a library that was in strict mode, but the developer was used to working in normal mode, they might call some actions on the library that wouldn’t work as expected.

\n\n

Worse, since the developer is in normal mode, they don’t have the advantages of extra errors being thrown, so the error might fail silently.

\n\n

Also, as listed above, strict mode stops you from doing certain things.

\n\n

People generally think that you shouldn’t use those things in the first place, but some developers don’t like the constraint and want to use all the features of the language.

\n\n\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff65", + "creator": "Alireza", + "createdAt": 1495456715000, + "text": "

\"use strict\" makes JavaScript code to run in strict mode, which basically means everything needs to be defined before use. The main reason for using strict mode is to avoid accidental global uses of undefined methods.

\n\n

Also in strict mode, things run faster, some warnings or silent warnings throw fatal errors, it's better to always use it to make a neater code.

\n\n

\"use strict\" is widely needed to be used in ECMA5, in ECMA6 it's part of JavaScript by default, so it doesn't need to be added if you're using ES6.

\n\n

Look at these statements and examples from MDN:

\n\n
\n

The \"use strict\" Directive
The \"use strict\" directive is new in\n JavaScript 1.8.5 (ECMAScript version 5). It is not a statement, but a\n literal expression, ignored by earlier versions of JavaScript. The\n purpose of \"use strict\" is to indicate that the code should be\n executed in \"strict mode\". With strict mode, you can not, for example,\n use undeclared variables.

\n \n

Examples of using \"use strict\":
\n Strict mode for functions: Likewise, to invoke strict mode for a\n function, put the exact statement \"use strict\"; (or 'use strict';) in\n the function's body before any other statements.

\n
\n\n

1) strict mode in functions

\n\n
 function strict() {\n     // Function-level strict mode syntax\n     'use strict';\n     function nested() { return 'And so am I!'; }\n     return \"Hi!  I'm a strict mode function!  \" + nested();\n }\n function notStrict() { return \"I'm not strict.\"; }\n\n console.log(strict(), notStrict());\n
\n\n

2) whole-script strict mode

\n\n
'use strict';\nvar v = \"Hi! I'm a strict mode script!\";\nconsole.log(v);\n
\n\n

3) Assignment to a non-writable global

\n\n
'use strict';\n\n// Assignment to a non-writable global\nvar undefined = 5; // throws a TypeError\nvar Infinity = 5; // throws a TypeError\n\n// Assignment to a non-writable property\nvar obj1 = {};\nObject.defineProperty(obj1, 'x', { value: 42, writable: false });\nobj1.x = 9; // throws a TypeError\n\n// Assignment to a getter-only property\nvar obj2 = { get x() { return 17; } };\nobj2.x = 5; // throws a TypeError\n\n// Assignment to a new property on a non-extensible object.\nvar fixed = {};\nObject.preventExtensions(fixed);\nfixed.newProp = 'ohai'; // throws a TypeError\n
\n\n

You can read more on MDN.

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff67", + "creator": "Jerin K Alexander", + "createdAt": 1562575611000, + "text": "

Strict mode can prevent memory leaks.

\n\n

Please check the function below written in non-strict mode:

\n\n
function getname(){\n    name = \"Stack Overflow\"; // Not using var keyword\n    return name;\n}\ngetname();\nconsole.log(name); // Stack Overflow\n
\n\n

In this function, we are using a variable called name inside the function. Internally, the compiler will first check if there is any variable declared with that particular name in that particular function scope. Since the compiler understood that there is no such variable, it will check in the outer scope. In our case, it is the global scope. Again, the compiler understood that there is also no variable declared in the global space with that name, so it creates such a variable for us in the global space. Conceptually, this variable will be created in the global scope and will be available in the entire application.

\n\n

Another scenario is that, say, the variable is declared in a child function. In that case, the compiler checks the validity of that variable in the outer scope, i.e., the parent function. Only then it will check in the global space and create a variable for us there.\nThat means additional checks need to be done. This will affect the performance of the application.

\n\n
\n\n

Now let's write the same function in strict mode.

\n\n
\"use strict\"\nfunction getname(){\n    name = \"Stack Overflow\"; // Not using var keyword\n    return name;\n}\ngetname();\nconsole.log(name); \n
\n\n

We will get the following error.

\n\n
Uncaught ReferenceError: name is not defined\nat getname (<anonymous>:3:15)\nat <anonymous>:6:5\n
\n\n

Here, the compiler throws the reference error. In strict mode, the compiler does not allow us to use the variable without declaring it. So memory leaks can be prevented. In addition, we can write more optimized code.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff68", + "creator": "snnsnn", + "createdAt": 1627197264000, + "text": "

JavaScript was designed and implemented hastily because of the browser wars and bad management. As a result many poor design decisions, un-intuitive syntax and confusing semantics found their way into the language. Strict mode aims to amend some of these mistakes.

\n

But fixing these mistakes without creating alternative interpretation breaks backward compatibility. So, "use strict" directive creates that alternative interpretation of the code while communicating it to the programmer.

\n

For example, this keywords refers to the object in a method definition, like this or self in other languages.

\n
let o = {\n  name: 'John Doe',\n  sayName: function(){\n    console.log(this.name);\n  }\n};\n\no.sayName(); // 'John Doe'\n
\n

this has no purpose outside the method context but all JavaScript functions have this keyword whether they are methods or not:

\n
function run() {\n  console.log(this);\n}\n\nrun(); // Window\n
\n

Here this resolves to the global object which does not make sense and serves no purpose because global object is already available in the scope.

\n

In strict mode this in a global function resolves to undefined, which is what we expect.

\n
"use strict"\n\nfunction run() {\n  console.log(this);\n}\n\nrun(); // undefined\n
\n

Some mistakes can not be fixed even in strict mode because syntax should be valid for older browsers since they ignore "strict mode" directive. This is by design.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff6a", + "creator": "Abdul Rahman", + "createdAt": 1659506621000, + "text": "

The "use strict" Directive

\n

It is not a statement, but a literal expression, ignored by earlier versions of JavaScript. The purpose of "use strict" is to indicate that the code should be executed in "strict mode". With strict mode, you can not, for example, use undeclared variables.

\n
//It's a new feature of ECMAScript 5. \n//With strict mode, you can not, for example, use undeclared variables.\n\nInsert a 'use strict'; statement on top of your script:\n/***START ANY JS FILE***/\n'use strict';\nvar a = 2;\n....\n/***END***/ \nOr, insert a 'use strict'; statement on top of your function body:\n\nfunction doSomething() {\n'use strict';\n...\n}   \n
\n

For Example:

\n
class Person {\nconstructor() {\n a = 0;\n this.name = a;\n }\n}\n\nlet p = new Person(); // ReferenceError: Can't find variable: a\n
\n", + "upvotes": 239, + "upvoterUsernames": [], + "downvotes": 239, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff69", + "creator": "Amin Azimi", + "createdAt": 1655764580000, + "text": "

strict mode enables strict features in the v8 engine. Short example of some features:

\n

You can enable it globally by writing:

\n
'use strict'; // strict mode enabled!\n
\n

Per function you just include in function:

\n
let myfunc = () => {\n  'use strict'; // strict mode enabled\n  \n   b = 0; // broke\n}\n
\n\n
  var x;\n  x = '0'; // ok\n  y = '';  // not ok\n
\n\n

There are more features as well, check here for more!

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff4f", + "creator": "seth", + "createdAt": 1251303263000, + "text": "

It's a new feature of ECMAScript 5. John Resig wrote up a nice summary of it.

\n\n

It's just a string you put in your JavaScript files (either at the top of your file or inside of a function) that looks like this:

\n\n
\"use strict\";\n
\n\n

Putting it in your code now shouldn't cause any problems with current browsers as it's just a string. It may cause problems with your code in the future if your code violates the pragma. For instance, if you currently have foo = \"bar\" without defining foo first, your code will start failing...which is a good thing in my opinion.

\n", + "upvotes": 2621, + "upvoterUsernames": [], + "downvotes": 1240, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ff50", + "creator": "Pascal MARTIN", + "createdAt": 1251303339000, + "text": "

Update for ES6 modules

\n

Inside native ECMAScript modules (with import and export statements) and ES6 classes, strict mode is always enabled and cannot be disabled.

\n

Original answer

\n

This article about Javascript Strict Mode might interest you: John Resig - ECMAScript 5 Strict Mode, JSON, and More

\n

To quote some interesting parts:

\n
\n

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a "strict" operating context. This strict context prevents certain actions from being taken and throws more exceptions.

\n
\n

And:

\n
\n

Strict mode helps out in a couple ways:

\n\n
\n

Also note you can apply "strict mode" to the whole file... Or you can use it only for a specific function (still quoting from John Resig's article):

\n
// Non-strict code...\n\n(function(){\n  "use strict";\n\n  // Define your library strictly...\n})();\n\n// Non-strict code...\n
\n

Which might be helpful if you have to mix old and new code ;-)

\n

So, I suppose it's a bit like the "use strict" you can use in Perl (hence the name?): it helps you make fewer errors, by detecting more things that could lead to breakages.

\n

Strict mode is now supported by all major browsers.

\n", + "upvotes": 6558, + "upvoterUsernames": [], + "downvotes": 1206, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1193313, + "uvac": 1193341 + } + }, + { + "_id": "62f321bb082fcc3049e8fef4", + "title": "Generate random string/characters in JavaScript", + "title-lowercase": "generate random string/characters in javascript", + "creator": "Tom Lehman", + "createdAt": 1251494081000, + "status": "open", + "text": "

I want a 5 character string composed of characters picked randomly from the set [a-zA-Z0-9].

\n\n

What's the best way to do this with JavaScript?

\n", + "upvotes": 2889, + "upvoterUsernames": [], + "downvotes": 376, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2321267, + "answers": 86, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90cf1", + "creator": "Vignesh", + "createdAt": 1259830086000, + "text": "

This works for sure

\n\n
<script language=\"javascript\" type=\"text/javascript\">\nfunction randomString() {\n var chars = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz\";\n var string_length = 8;\n var randomstring = '';\n for (var i=0; i<string_length; i++) {\n  var rnum = Math.floor(Math.random() * chars.length);\n  randomstring += chars.substring(rnum,rnum+1);\n }\n document.randform.randomfield.value = randomstring;\n}\n</script>\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cf4", + "creator": "Andy", + "createdAt": 1325453057000, + "text": "

You can loop through an array of items and recursively add them to a string variable, for instance if you wanted a random DNA sequence:

\n\n

\r\n
\r\n
function randomDNA(len) {\r\n  len = len || 100\r\n  var nuc = new Array(\"A\", \"T\", \"C\", \"G\")\r\n  var i = 0\r\n  var n = 0\r\n  s = ''\r\n  while (i <= len - 1) {\r\n    n = Math.floor(Math.random() * 4)\r\n    s += nuc[n]\r\n    i++\r\n  }\r\n  return s\r\n}\r\n\r\nconsole.log(randomDNA(5));
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cef", + "creator": "CaffGeek", + "createdAt": 1251494900000, + "text": "

Something like this should work

\n\n
function randomString(len, charSet) {\n    charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    var randomString = '';\n    for (var i = 0; i < len; i++) {\n        var randomPoz = Math.floor(Math.random() * charSet.length);\n        randomString += charSet.substring(randomPoz,randomPoz+1);\n    }\n    return randomString;\n}\n
\n\n

Call with default charset [a-zA-Z0-9] or send in your own:

\n\n
var randomValue = randomString(5);\n\nvar randomValue = randomString(5, 'PICKCHARSFROMTHISSET');\n
\n", + "upvotes": 158, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32841082fcc3049e9275b", + "creator": "drzaus", + "createdAt": 1362581959000, + "text": "might as well just decrement len directly in a while loop", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9275c", + "creator": "Fattie", + "createdAt": 1651773619000, + "text": "note that on a Node server, it's this simple: crypto.randomBytes(3).toString('hex') (3 bytes gives 6 chars, for example)", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cf0", + "creator": "kennebec", + "createdAt": 1251513678000, + "text": "

\r\n
\r\n
function randomstring(L) {\r\n  var s = '';\r\n  var randomchar = function() {\r\n    var n = Math.floor(Math.random() * 62);\r\n    if (n < 10) return n; //1-10\r\n    if (n < 36) return String.fromCharCode(n + 55); //A-Z\r\n    return String.fromCharCode(n + 61); //a-z\r\n  }\r\n  while (s.length < L) s += randomchar();\r\n  return s;\r\n}\r\nconsole.log(randomstring(5));
\r\n
\r\n
\r\n

\n", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32841082fcc3049e9275f", + "creator": "vsync", + "createdAt": 1403008243000, + "text": "Also while(L--) will do it", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cf2", + "creator": "Roderick ", + "createdAt": 1304521230000, + "text": "

I know everyone has got it right already, but i felt like having a go at this one in the most lightweight way possible(light on code, not CPU):

\n\n

\r\n
\r\n
function rand(length, current) {\r\n  current = current ? current : '';\r\n  return length ? rand(--length, \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz\".charAt(Math.floor(Math.random() * 60)) + current) : current;\r\n}\r\n\r\nconsole.log(rand(5));
\r\n
\r\n
\r\n

\n\n

It takes a bit of time to wrap your head around, but I think it really shows how awesome javascript's syntax is.

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cf5", + "creator": "Gajus", + "createdAt": 1357781985000, + "text": "
function randomString (strLength, charSet) {\n    var result = [];\n    \n    strLength = strLength || 5;\n    charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    \n    while (strLength--) { // (note, fixed typo)\n        result.push(charSet.charAt(Math.floor(Math.random() * charSet.length)));\n    }\n    \n    return result.join('');\n}\n
\n

This is as clean as it will get. It is fast too, http://jsperf.com/ay-random-string.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32841082fcc3049e92762", + "creator": "AlexGrafe", + "createdAt": 1385120585000, + "text": "For me this always generates random strings with strLength - 1 :-/", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92764", + "creator": "AlexGrafe", + "createdAt": 1385120639000, + "text": "Switching from --strLength to strLength--fixes it for me.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92765", + "creator": "Fattie", + "createdAt": 1651771870000, + "text": "@Gajus I took the liberty of fixing the obvious typo. it is strLength-- postfix, not prefix.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92766", + "creator": "Sagive", + "createdAt": 1659777883000, + "text": "thank you sir. Wrapping it in a func make it easy to copy/paste ❤️", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cee", + "creator": "csharptest.net", + "createdAt": 1251494463000, + "text": "

I think this will work for you:

\n

\r\n
\r\n
function makeid(length) {\n    var result           = '';\n    var characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    var charactersLength = characters.length;\n    for ( var i = 0; i < length; i++ ) {\n      result += characters.charAt(Math.floor(Math.random() * \n charactersLength));\n   }\n   return result;\n}\n\nconsole.log(makeid(5));
\r\n
\r\n
\r\n

\n", + "upvotes": 4723, + "upvoterUsernames": [], + "downvotes": 1375, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32841082fcc3049e92769", + "creator": "vbullinger", + "createdAt": 1399489212000, + "text": "Thanks! I used this but randomized the length. Instead of 5, I got another random number for length: Math.floor(Math.random() * maxLength)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9276a", + "creator": "vsync", + "createdAt": 1403007425000, + "text": "@dan_waterworth - what you've said is true in other languages, not in javascript.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9276b", + "creator": "Janus Troelsen", + "createdAt": 1466591730000, + "text": "@Hydrothermal what about getRandomValues?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9276c", + "creator": "Eugene", + "createdAt": 1469780788000, + "text": "Is there any easy way to have those chars unique always?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9276d", + "creator": "csharptest.net", + "createdAt": 1507831545000, + "text": "@HaiPhan lmao, too right. Didn't matter to me 8 years ago, damn sure doesn't now 😆", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9276f", + "creator": "notrota", + "createdAt": 1524795591000, + "text": "the v8 engine optimises the creation of strings based on older ones and so it isn't O(n^2)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92771", + "creator": "Gerben Rampaart", + "createdAt": 1550230399000, + "text": "I like this because it's really concise, easy to understand and doesn't use any libraries or advanced ecmascript features.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92773", + "creator": "Pawel", + "createdAt": 1608071352000, + "text": "@dan_waterworth it only matters if you generate like 10 million character long strings lol", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92774", + "creator": "Omar bakhsh", + "createdAt": 1619390766000, + "text": "you can your pass : console.log("MZ-"+makeid(2)+"15");", + "upvotes": 165, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cf3", + "creator": "doubletap", + "createdAt": 1320948724000, + "text": "

\r\n
\r\n
//Can change 7 to 2 for longer results.\nlet r = (Math.random() + 1).toString(36).substring(7);\nconsole.log(\"random\", r);
\r\n
\r\n
\r\n

\n

Note: The above algorithm has the following weaknesses:

\n\n", + "upvotes": 5160, + "upvoterUsernames": [], + "downvotes": 2225, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32841082fcc3049e92775", + "creator": "jberryman", + "createdAt": 1329929229000, + "text": "love it, but he needs [a-zA-Z]", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92777", + "creator": "dragon", + "createdAt": 1336378422000, + "text": "Math.random().toString(36).substr(2, 5), because .substring(7) causes it to be longer than 5 characters. Full points, still!", + "upvotes": 401, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92779", + "creator": "Nux", + "createdAt": 1343647202000, + "text": "And an ActionScript 2 compatible version: Math.round(Math.random()*Math.pow(36,5)).toString(36)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9277b", + "creator": "gertas", + "createdAt": 1363377353000, + "text": "Looks beautiful but in few cases this generates empty string! If random returns 0, 0.5, 0.25, 0.125... will result in empty or short string.", + "upvotes": 129, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9277d", + "creator": "Michael Deal", + "createdAt": 1365321908000, + "text": "This produces a longer random number: (Math.PI * Math.max(0.01, Math.random())).toString(36).substr(2, 5);", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e9277f", + "creator": "George Reith", + "createdAt": 1376241435000, + "text": "@gertas This can be avoided by (Math.random() + 1).toString(36).substring(7);", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92781", + "creator": "Briguy37", + "createdAt": 1379972528000, + "text": ""0" fix: ('0000'+Math.random().toString(36).replace('.', '')).substr(-5);", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92783", + "creator": "user1902830", + "createdAt": 1394704075000, + "text": "(+new Date * Math.random()).toString(36).substring(0,5)", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92785", + "creator": "Константин Ван", + "createdAt": 1468208303000, + "text": "length => (Math.random().toString(36).substring(2, 2 + length) + '0'.repeat(length)).substring(0, length)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92786", + "creator": "Mehdi Dehghani", + "createdAt": 1584362000000, + "text": "Not really helpful answer, yet too many up votes! it has too many issues.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32841082fcc3049e92788", + "creator": "Kamil Kiełczewski", + "createdAt": 1592767375000, + "text": "If Math.random returns 0.5 then we get invalid result", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cf6", + "creator": "ropsiU", + "createdAt": 1357901670000, + "text": "

Generate 10 characters long string. Length is set by parameter (default 10).

\n\n
function random_string_generator(len) {\nvar len = len || 10;\nvar str = '';\nvar i = 0;\n\nfor(i=0; i<len; i++) {\n    switch(Math.floor(Math.random()*3+1)) {\n        case 1: // digit\n            str += (Math.floor(Math.random()*9)).toString();\n        break;\n\n        case 2: // small letter\n            str += String.fromCharCode(Math.floor(Math.random()*26) + 97); //'a'.charCodeAt(0));\n        break;\n\n        case 3: // big letter\n            str += String.fromCharCode(Math.floor(Math.random()*26) + 65); //'A'.charCodeAt(0));\n        break;\n\n        default:\n        break;\n    }\n}\nreturn str;\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cf7", + "creator": "Martijn de Milliano", + "createdAt": 1361217405000, + "text": "

In case anyone is interested in a one-liner (although not formatted as such for your convenience) that allocates the memory at once (but note that for small strings it really does not matter) here is how to do it:

\n\n
Array.apply(0, Array(5)).map(function() {\n    return (function(charset){\n        return charset.charAt(Math.floor(Math.random() * charset.length))\n    }('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));\n}).join('')\n
\n\n

You can replace 5 by the length of the string you want. Thanks to @AriyaHidayat in this post for the solution to the map function not working on the sparse array created by Array(5).

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32842082fcc3049e9278b", + "creator": "hacklikecrack", + "createdAt": 1385909810000, + "text": "Every javascript program is a 'one-liner' if you format it as such", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cf8", + "creator": "Steven DAmico", + "createdAt": 1363481050000, + "text": "

Expanding on Doubletap's elegant example by answering the issues Gertas and Dragon brought up. Simply add in a while loop to test for those rare null circumstances, and limit the characters to five.

\n\n
function rndStr() {\n    x=Math.random().toString(36).substring(7).substr(0,5);\n    while (x.length!=5){\n        x=Math.random().toString(36).substring(7).substr(0,5);\n    }\n    return x;\n}\n
\n\n

Here's a jsfiddle alerting you with a result: \nhttp://jsfiddle.net/pLJJ7/

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cfa", + "creator": "qubyte", + "createdAt": 1364911748000, + "text": "

Also based upon doubletap's answer, this one handles any length of random required characters (lower only), and keeps generating random numbers until enough characters have been collected.

\n\n
function randomChars(len) {\n    var chars = '';\n\n    while (chars.length < len) {\n        chars += Math.random().toString(36).substring(2);\n    }\n\n    // Remove unnecessary additional characters.\n    return chars.substring(0, len);\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cf9", + "creator": "nclu", + "createdAt": 1363799612000, + "text": "
\"12345\".split('').map(function(){return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.charAt(Math.floor(62*Math.random()));}).join('');\n\n//or\n\nString.prototype.rand = function() {return this.split('').map(function(){return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.charAt(Math.floor(62*Math.random()));}).join('');};\n
\n\n

will generate a random alpha-numeric string with the length of the first/calling string

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cfb", + "creator": "qubyte", + "createdAt": 1375949068000, + "text": "

How about this compact little trick?

\n\n
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\nvar stringLength = 5;\n\nfunction pickRandom() {\n    return possible[Math.floor(Math.random() * possible.length)];\n}\n\nvar randomString = Array.apply(null, Array(stringLength)).map(pickRandom).join('');\n
\n\n

You need the Array.apply there to trick the empty array into being an array of undefineds.

\n\n

If you're coding for ES2015, then building the array is a little simpler:

\n\n
var randomString = Array.from({ length: stringLength }, pickRandom).join('');\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cfc", + "creator": "Adam", + "createdAt": 1377701411000, + "text": "

Here's the method I created.
\nIt will create a string containing both uppercase and lowercase characters.
\nIn addition I've included the function that will created an alphanumeric string too.

\n\n

Working examples:
\nhttp://jsfiddle.net/greatbigmassive/vhsxs/ (alpha only)
\nhttp://jsfiddle.net/greatbigmassive/PJwg8/ (alphanumeric)

\n\n
function randString(x){\n    var s = \"\";\n    while(s.length<x&&x>0){\n        var r = Math.random();\n        s+= String.fromCharCode(Math.floor(r*26) + (r>0.5?97:65));\n    }\n    return s;\n}\n
\n\n

Upgrade July 2015
\nThis does the same thing but makes more sense and includes all letters.

\n\n
var s = \"\";\nwhile(s.length<x&&x>0){\n    v = Math.random()<0.5?32:0;\n    s += String.fromCharCode(Math.round(Math.random()*((122-v)-(97-v))+(97-v)));\n}\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32842082fcc3049e92791", + "creator": "erkanyildiz", + "createdAt": 1421850092000, + "text": "With this function, you can never get an uppercase letter greater than 'M' or a lowercase letter lesser than 'n'.", + "upvotes": 2069, + "upvoterUsernames": [], + "downvotes": 2069, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cfd", + "creator": "guest271314", + "createdAt": 1379750661000, + "text": "
\",,,,,\".replace(/,/g,function (){return \"AzByC0xDwEv9FuGt8HsIrJ7qKpLo6MnNmO5lPkQj4RiShT3gUfVe2WdXcY1bZa\".charAt(Math.floor(Math.random()*62))});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32842082fcc3049e92794", + "creator": "brunoais", + "createdAt": 1409734137000, + "text": "too much regex. Too slow.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cff", + "creator": "Slavik Meltser", + "createdAt": 1383236478000, + "text": "

Another nice way to randomize a string from the characters A-Za-z0-9:

\n\n
function randomString(length) {\n    if ( length <= 0 ) return \"\";\n    var getChunk = function(){\n        var i, //index iterator\n            rand = Math.random()*10e16, //execute random once\n            bin = rand.toString(2).substr(2,10), //random binary sequence\n            lcase = (rand.toString(36)+\"0000000000\").substr(0,10), //lower case random string\n            ucase = lcase.toUpperCase(), //upper case random string\n            a = [lcase,ucase], //position them in an array in index 0 and 1\n            str = \"\"; //the chunk string\n        b = rand.toString(2).substr(2,10);\n        for ( i=0; i<10; i++ )\n            str += a[bin[i]][i]; //gets the next character, depends on the bit in the same position as the character - that way it will decide what case to put next\n        return str;\n    },\n    str = \"\"; //the result string\n    while ( str.length < length  )\n        str += getChunk();\n    str = str.substr(0,length);\n    return str;\n}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cfe", + "creator": "Andrew Plank", + "createdAt": 1379879725000, + "text": "

This is what I used. A combination of a couple here. I use it in a loop, and each ID it produces is unique. It might not be 5 characters, but it's guaranteed unique.

\n\n
var newId =\n    \"randomid_\" +\n    (Math.random() / +new Date()).toString(36).replace(/[^a-z]+/g, '');\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d00", + "creator": "mindrones", + "createdAt": 1397579373000, + "text": "

If a library is a possibility, Chance.js might be of help: http://chancejs.com/#string

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d01", + "creator": "Collin Anderson", + "createdAt": 1404154469000, + "text": "

This stores 5 alphanumeric characters in variable c.

\n\n
for(var c = ''; c.length < 5;) c += Math.random().toString(36).substr(2, 1)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d02", + "creator": "bendytree", + "createdAt": 1405619265000, + "text": "

Here are some easy one liners. Change new Array(5) to set the length.

\n

Including 0-9a-z

\n
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36);})\n
\n

Including 0-9a-zA-Z

\n
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36)[Math.random()<.5?"toString":"toUpperCase"]();});\n
\n

Codegolfed for ES6 (0-9a-z)

\n
Array(5).fill().map(()=>((Math.random()*36)|0).toString(36)).join('')\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d04", + "creator": "Dinis Cruz", + "createdAt": 1414786773000, + "text": "

Here is an example in CoffeeScript:

\n\n
String::add_Random_Letters   = (size )->\n                                         charSet = 'abcdefghijklmnopqrstuvwxyz'\n                                         @ + (charSet[Math.floor(Math.random() * charSet.length)]  for i in [1..size]).join('')\n
\n\n

which can be used

\n\n
value = \"abc_\"\nvalue_with_exta_5_random_letters = value.add_Random_Letters(5)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d03", + "creator": "James Harrington", + "createdAt": 1407377335000, + "text": "

Here is a test script for the #1 answer (thank you @csharptest.net)

\n\n

the script runs makeid() 1 million times and as you can see 5 isnt a very unique. running it with a char length of 10 is quite reliable. I've ran it about 50 times and haven't seen a duplicate yet :-)

\n\n

note: node stack size limit exceeds around 4 million so you cant run this 5 million times it wont ever finish.

\n\n
function makeid()\n{\n    var text = \"\";\n    var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n    for( var i=0; i < 5; i++ )\n        text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n    return text;\n}\n\nids ={}\ncount = 0\nfor (var i = 0; i < 1000000; i++) {\n    tempId = makeid();\n    if (typeof ids[tempId] !== 'undefined') {\n        ids[tempId]++;\n        if (ids[tempId] === 2) {\n            count ++;\n        }\n        count++;\n    }else{\n        ids[tempId] = 1;\n    }\n}\nconsole.log(\"there are \"+count+ ' duplicate ids');\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d05", + "creator": "EricP", + "createdAt": 1415226708000, + "text": "

I loved the brievety of doubletap's Math.random().toString(36).substring(7) answer, but not that it had so many collisions as hacklikecrack correctly pointed out. It generated 11-chacter strings but has a duplicate rate of 11% in a sample size of 1 million.

\n\n

Here's a longer (but still short) and slower alternative that had only 133 duplicates in a sample space of 1 million. In rare cases the string will still be shorter than 11 chars:

\n\n
Math.abs(Math.random().toString().split('')\n    .reduce(function(p,c){return (p<<5)-p+c})).toString(36).substr(0,11);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d06", + "creator": "Mulan", + "createdAt": 1420226293000, + "text": "

Math.random is bad for this kind of thing

\n

Option 1

\n

If you're able to do this server-side, just use the crypto module -

\n
var crypto = require("crypto");\nvar id = crypto.randomBytes(20).toString('hex');\n\n// "bb5dc8842ca31d4603d6aa11448d1654"\n
\n

The resulting string will be twice as long as the random bytes you generate; each byte encoded to hex is 2 characters. 20 bytes will be 40 characters of hex.

\n
\n

Option 2

\n

If you have to do this client-side, perhaps try the uuid module -

\n
var uuid = require("uuid");\nvar id = uuid.v4();\n\n// "110ec58a-a0f2-4ac4-8393-c866d813b8d1"\n
\n
\n

Option 3

\n

If you have to do this client-side and you don't have to support old browsers, you can do it without dependencies -

\n

\r\n
\r\n
// dec2hex :: Integer -> String\n// i.e. 0-255 -> '00'-'ff'\nfunction dec2hex (dec) {\n  return dec.toString(16).padStart(2, \"0\")\n}\n\n// generateId :: Integer -> String\nfunction generateId (len) {\n  var arr = new Uint8Array((len || 40) / 2)\n  window.crypto.getRandomValues(arr)\n  return Array.from(arr, dec2hex).join('')\n}\n\nconsole.log(generateId())\n// \"82defcf324571e70b0521d79cce2bf3fffccd69\"\n\nconsole.log(generateId(20))\n// \"c1a050a4cd1556948d41\"
\r\n
\r\n
\r\n

\n
\n

For more information on crypto.getRandomValues -

\n
\n

The crypto.getRandomValues() method lets you get cryptographically strong random values. The array given as the parameter is filled with random numbers (random in its cryptographic meaning).

\n
\n

Here's a little console example -

\n
> var arr = new Uint8Array(4) # make array of 4 bytes (values 0-255)\n> arr\nUint8Array(4) [ 0, 0, 0, 0 ]\n\n> window.crypto\nCrypto { subtle: SubtleCrypto }\n\n> window.crypto.getRandomValues()\nTypeError: Crypto.getRandomValues requires at least 1 argument, but only 0 were passed\n\n> window.crypto.getRandomValues(arr)\nUint8Array(4) [ 235, 229, 94, 228 ]\n
\n

For IE11 support you can use -

\n
(window.crypto || window.msCrypto).getRandomValues(arr)\n
\n

For browser coverage see https://caniuse.com/#feat=getrandomvalues

\n", + "upvotes": 982, + "upvoterUsernames": [], + "downvotes": 197, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32843082fcc3049e9279c", + "creator": "Ronaldo Bahia", + "createdAt": 1473875291000, + "text": "Safari 9.3 shows this error: SyntaxError: Unexpected token '>'", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e9279e", + "creator": "Mulan", + "createdAt": 1473875842000, + "text": "@RonaldoBahia, Safari doesn't support arrow functions because it's a noob. Thanks for the notice.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927a0", + "creator": "mythicalcoder", + "createdAt": 1493932207000, + "text": "crypto is the way to go if you are using Node.js. This deserves more upvotes.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927a2", + "creator": "Abhinav Gauniyal", + "createdAt": 1496647974000, + "text": "should mention that synchronous version throws an Error unlike the async version which follows callback(err, data) convention.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927a4", + "creator": "Esko", + "createdAt": 1534852057000, + "text": "You should mention in the answer that option 2 also needs node.js to work, it's not pure javascript.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927a6", + "creator": "Rockallite", + "createdAt": 1535536592000, + "text": "Also notice that IE 11 doesn't support Array.from, but it does support window.crypto.getRandomValues.", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927a8", + "creator": "Amadan", + "createdAt": 1570424013000, + "text": "('0' + dec.toString(16)).substr(-2) now has a nicer form: dec.toString(16).padStart(2, '0')", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927aa", + "creator": "jerrymouse", + "createdAt": 1593626256000, + "text": "What if I need a random string in the set [a-zA-Z0-9] of length N?", + "upvotes": 1238, + "upvoterUsernames": [], + "downvotes": 1238, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927ac", + "creator": "jerrymouse", + "createdAt": 1593681137000, + "text": "@thank-you But as you suggested, I wanted to use cryptographic uniqueness.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d07", + "creator": "user1115652", + "createdAt": 1420757561000, + "text": "

This is for firefox chrome code (addons and the like)

\n\n

It can save you a few hours of research.

\n\n
function randomBytes( amount )\n{\n    let bytes = Cc[ '@mozilla.org/security/random-generator;1' ]\n\n        .getService         ( Ci.nsIRandomGenerator )\n        .generateRandomBytes( amount, ''            )\n\n    return bytes.reduce( bytes2Number )\n\n\n    function bytes2Number( previousValue, currentValue, index, array )\n    {\n      return Math.pow( 256, index ) * currentValue + previousValue\n    }\n}\n
\n\n

Use it as:

\n\n
let   strlen   = 5\n    , radix    = 36\n    , filename = randomBytes( strlen ).toString( radix ).splice( - strlen )\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d08", + "creator": "MasqueradeCircus", + "createdAt": 1426110212000, + "text": "

The simplest way is:

\n\n
(new Date%9e6).toString(36)\n
\n\n

This generate random strings of 5 characters based on the current time. Example output is 4mtxj or 4mv90 or 4mwp1

\n\n

The problem with this is that if you call it two times on the same second, it will generate the same string.

\n\n

The safer way is:

\n\n
(0|Math.random()*9e6).toString(36)\n
\n\n

This will generate a random string of 4 or 5 characters, always diferent. Example output is like 30jzm or 1r591 or 4su1a

\n\n

In both ways the first part generate a random number. The .toString(36) part cast the number to a base36 (alphadecimal) representation of it.

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32843082fcc3049e927ae", + "creator": "seniorpreacher", + "createdAt": 1436968074000, + "text": "If you use Date, why don't you just use it like: (+new Date).toString(36)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32843082fcc3049e927b0", + "creator": "user.friendly", + "createdAt": 1507760209000, + "text": "one qualm I have with this answer is that it will not use [A-Z] which happens to be in the original question.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d09", + "creator": "tiktak", + "createdAt": 1431949705000, + "text": "

Assuming you use underscorejs it's possible to elegantly generate random string in just two lines:

\n\n
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\nvar random = _.sample(possible, 5).join('');\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32843082fcc3049e927b3", + "creator": "Yefu", + "createdAt": 1479692243000, + "text": "This will make the returned value have all unique chars. Also, the length of the string is limited up to the the length of var possible.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d0a", + "creator": "Valentin Podkamennyi", + "createdAt": 1444907507000, + "text": "

The most compact solution, because slice is shorter than substring. Subtracting from the end of the string allows to avoid floating point symbol generated by the random function:

\n\n
Math.random().toString(36).slice(-5);\n
\n\n

or even

\n\n
(+new Date).toString(36).slice(-5);\n
\n\n

Update: Added one more approach using btoa method:

\n\n
btoa(Math.random()).slice(0, 5);\nbtoa(+new Date).slice(-7, -2);\nbtoa(+new Date).substr(-7, 5);\n
\n\n

\r\n
\r\n
// Using Math.random and Base 36:\r\nconsole.log(Math.random().toString(36).slice(-5));\r\n\r\n// Using new Date and Base 36:\r\nconsole.log((+new Date).toString(36).slice(-5));\r\n\r\n// Using Math.random and Base 64 (btoa):\r\nconsole.log(btoa(Math.random()).slice(0, 5));\r\n\r\n// Using new Date and Base 64 (btoa):\r\nconsole.log(btoa(+new Date).slice(-7, -2));\r\nconsole.log(btoa(+new Date).substr(-7, 5));
\r\n
\r\n
\r\n

\n", + "upvotes": 244, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927b6", + "creator": "x-ray", + "createdAt": 1562947221000, + "text": "Math.random().toString(36).slice(-5); - What if Math.random() returns 0.0?", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 84, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927b8", + "creator": "Valentin Podkamennyi", + "createdAt": 1562991184000, + "text": "@x-ray, you will get "0" ;)", + "upvotes": 356, + "upvoterUsernames": [], + "downvotes": 356, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d0b", + "creator": "user1506145", + "createdAt": 1448285325000, + "text": "

Put the characters as the thisArg in the map function will create a \"one-liner\":

\n\n
Array.apply(null, Array(5))\n.map(function(){ \n    return this[Math.floor(Math.random()*this.length)];\n}, \"abcdefghijklmnopqrstuvwxyz\")\n.join('');\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d0c", + "creator": "vineet", + "createdAt": 1461130758000, + "text": "

If you are using Lodash or Underscore, then it so simple:

\n\n
var randomVal = _.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 5).join('');\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927ba", + "creator": "marlo", + "createdAt": 1472011724000, + "text": "Lodash uses _.sampleSize('asdfgh',5).join('')", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d0d", + "creator": "Alston", + "createdAt": 1467366639000, + "text": "

Here's Coffeescript version one line of code

\n\n
genRandomString = (length,set) -> [0...length].map( -> set.charAt Math.floor(Math.random() * set.length)).join('')\n
\n\n

Usage:

\n\n
genRandomString 5, 'ABCDEFTGHIJKLMNOPQRSTUVWXYZ'\n
\n\n

Output:

\n\n
'FHOOV' # random string of length 5 in possible set A~Z\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d0e", + "creator": "Mulan", + "createdAt": 1467407001000, + "text": "

A functional approach. This answer is only practical if the functional prerequisites can be leveraged in other parts of your app. The performance is probably junk, but it was super fun to write.

\n\n

\r\n
\r\n
// functional prerequisites\r\nconst U = f=> f (f)\r\nconst Y = U (h=> f=> f (x=> h (h) (f) (x)))\r\nconst comp = f=> g=> x=> f (g (x))\r\nconst foldk = Y (h=> f=> y=> ([x, ...xs])=>\r\n  x === undefined ? y : f (y) (x) (y=> h (f) (y) (xs)))\r\nconst fold = f=> foldk (y=> x=> k=> k (f (y) (x)))\r\nconst map = f=> fold (y=> x=> [...y, f (x)]) ([])\r\nconst char = x=> String.fromCharCode(x)\r\nconst concat = x=> y=> y.concat(x)\r\nconst concatMap = f=> comp (fold (concat) ([])) (map (f))\r\nconst irand = x=> Math.floor(Math.random() * x)\r\nconst sample = xs=> xs [irand (xs.length)]\r\n\r\n// range : make a range from x to y; [x...y]\r\n// Number -> Number -> [Number]\r\nconst range = Y (f=> r=> x=> y=>\r\n  x > y ? r : f ([...r, x]) (x+1) (y)\r\n) ([])\r\n\r\n// srand : make random string from list or ascii code ranges\r\n// [(Range a)] -> Number -> [a]\r\nconst srand = comp (Y (f=> z=> rs=> x=>\r\n  x === 0 ? z : f (z + sample (rs)) (rs) (x-1)\r\n) ([])) (concatMap (map (char)))\r\n\r\n// idGenerator : make an identifier of specified length\r\n// Number -> String\r\nconst idGenerator = srand ([\r\n  range (48) (57),  // include 0-9\r\n  range (65) (90),  // include A-Z\r\n  range (97) (122)  // include a-z\r\n])\r\n\r\nconsole.log (idGenerator (6))  //=> TT688X\r\nconsole.log (idGenerator (10)) //=> SzaaUBlpI1\r\nconsole.log (idGenerator (20)) //=> eYAaWhsfvLDhIBID1xRh
\r\n
\r\n
\r\n

\n\n

In my opinion, it's hard to beat the clarity of idGenerator without adding magical, do-too-many-things functions.

\n\n

A slight improvement could be

\n\n
// ord : convert char to ascii code\n// Char -> Number\nconst ord = x => x.charCodeAt(0)\n\n// idGenerator : make an identifier of specified length\n// Number -> String\nconst idGenerator = srand ([\n  range (ord('0')) (ord('9')),\n  range (ord('A')) (ord('Z')),\n  range (ord('a')) (ord('z'))\n])\n
\n\n

Have fun with it. Let me know what you like/learn ^_^

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927bd", + "creator": "user6445533", + "createdAt": 1469269144000, + "text": "Great solution and I like that you derive fold from foldk :D", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d0f", + "creator": "Silver Ringvee", + "createdAt": 1469651053000, + "text": "

Short, easy and reliable

\n

Returns exactly 5 random characters, as opposed to some of the top rated answers found here.

\n
Math.random().toString(36).slice(2, 7);\n
\n", + "upvotes": 290, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927bf", + "creator": "Michael Litvin", + "createdAt": 1484653375000, + "text": "What if Math.random().toString(36) returns a number with less than 5 characters?", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927c1", + "creator": "user.friendly", + "createdAt": 1507759452000, + "text": "This method does seem to be limited to (a max) of 10 characters.... still cool though.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927c2", + "creator": "rinogo", + "createdAt": 1559936697000, + "text": "This does not always return 5 characters. For example, (0.5).toString(36).substr(2, 5) yields "i".", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927c4", + "creator": "Gooz", + "createdAt": 1588937789000, + "text": "Note that substr is a 'legacy function' in Node.js, so I used .slice(2, 5).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927c6", + "creator": "Silver Ringvee", + "createdAt": 1589182245000, + "text": "@Gooz I don't think the question mentioned Node.js anywhere.", + "upvotes": 509, + "upvoterUsernames": [], + "downvotes": 509, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927c8", + "creator": "RyanNerd", + "createdAt": 1643968257000, + "text": "@SilverRingvee Modern IDEs will mark substr() as deprecated in both Node and vanilla JS/TypeScript so @Gooz has a point.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d12", + "creator": "Lukasz Wiktor", + "createdAt": 1483973661000, + "text": "

You can use coderain. It's a library to generate random codes according to given pattern. Use # as a placeholder for upper and lowercase characters as well as digits:

\n\n
var cr = new CodeRain(\"#####\");\nconsole.log(cr.next());\n
\n\n

There are other placeholders like A for uppercase letters or 9 for digits.

\n\n

What may be useful is that calling .next() will always give you a unique result so you don't have to worry about duplicates.

\n\n

Here is a demo application that generates a list of unique random codes.

\n\n

Full disclosure: I'm the author of coderain.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927ca", + "creator": "user285594", + "createdAt": 1484661893000, + "text": "NO NO NO - use native methods.", + "upvotes": 8848, + "upvoterUsernames": [], + "downvotes": 8848, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d10", + "creator": "aleung", + "createdAt": 1470374860000, + "text": "

The npm module anyid provides flexible API to generate various kinds of string ID / code.

\n\n
const id = anyid().encode('Aa0').length(5).random().id();\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d11", + "creator": "yaroslav", + "createdAt": 1474043762000, + "text": "

Fast and improved algorithm. Does not guarantee uniform (see comments).

\n\n
function getRandomId(length) {\n    if (!length) {\n        return '';\n    }\n\n    const possible =\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    let array;\n\n    if ('Uint8Array' in self && 'crypto' in self && length <= 65536) {\n        array = new Uint8Array(length);\n        self.crypto.getRandomValues(array);\n    } else {\n        array = new Array(length);\n\n        for (let i = 0; i < length; i++) {\n            array[i] = Math.floor(Math.random() * 62);\n        }\n    }\n\n    let result = '';\n\n    for (let i = 0; i < length; i++) {\n        result += possible.charAt(array[i] % 62);\n    }\n\n    return result;\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927ce", + "creator": "cowbert", + "createdAt": 1513295750000, + "text": "adding - and _ is a great idea since it will get you the complete url-safe base64 set", + "upvotes": 448, + "upvoterUsernames": [], + "downvotes": 448, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d13", + "creator": "CMS", + "createdAt": 1490823105000, + "text": "

I use var randId = 'rand' + new Date().getTime();

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32844082fcc3049e927d1", + "creator": "CMS", + "createdAt": 1500578203000, + "text": "it really depends for what you need it in my case i needed to add id to line when user adds row and you cant click twice in 1 ms,", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927d3", + "creator": "CMS", + "createdAt": 1501708006000, + "text": "i would like to see a demo of that, in my opinion that's not possible, unless you click with a bot 🤖", + "upvotes": 609, + "upvoterUsernames": [], + "downvotes": 609, + "downvoterUsernames": [] + }, + { + "_id": "62f32844082fcc3049e927d4", + "creator": "CMS", + "createdAt": 1502601903000, + "text": "got you, yes you need to know the case,", + "upvotes": 172, + "upvoterUsernames": [], + "downvotes": 172, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d14", + "creator": "Ratan Uday Kumar", + "createdAt": 1498030329000, + "text": "

in below code i am generating random code for 8 characters

\n\n
function RandomUnique(){\n                    var charBank = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789\";\n                    var random= '';\n                    var howmanycharacters = 8;\n                    for (var i = 0; i < howmanycharacters ; i++) {\n                        random+= charBank[parseInt(Math.random() * charBank.lenght)];\n                    }\n                    return random;\n                }\n        var random = RandomUnique();\n        console.log(random);\n
\n", + "upvotes": 379, + "upvoterUsernames": [], + "downvotes": 379, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d15", + "creator": "anil kumar Dyavanapalli", + "createdAt": 1498300176000, + "text": "

Try this, what i use every time :

\n\n

\r\n
\r\n
function myFunction() {\r\n        var hash = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789\";\r\n        var random8 = '';\r\n        for(var i = 0; i < 5; i++){\r\n            random8 += hash[parseInt(Math.random()*hash.length)];\r\n        }\r\n        console.log(random8);\r\n    document.getElementById(\"demo\").innerHTML = \"Your 5 character string ===> \"+random8;\r\n}        \r\n        
\r\n
<!DOCTYPE html>\r\n<html>\r\n<body>\r\n\r\n<p>Click the button to genarate 5 character random string .</p>\r\n\r\n<button onclick=\"myFunction()\">Click me</button>\r\n\r\n<p id=\"demo\"></p>\r\n\r\n\r\n\r\n</body>\r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d16", + "creator": "Behnam Mohammadi", + "createdAt": 1499087347000, + "text": "

very simple

\n\n
function getRandomColor(){\n  var color='';\n  while(color.length<6){\n    color=Math.floor(Math.random()*16777215).toString(16);\n  }\n  return '#'+color;\n}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d19", + "creator": "miodus", + "createdAt": 1506951098000, + "text": "

This one combines many of the answers give.

\n\n

\r\n
\r\n
var randNo = Math.floor(Math.random() * 100) + 2 + \"\" + new Date().getTime() +  Math.floor(Math.random() * 100) + 2 + (Math.random().toString(36).replace(/[^a-zA-Z]+/g, '').substr(0, 5));\r\n\r\nconsole.log(randNo);
\r\n
\r\n
\r\n

\n\n

I have been using it for 1 month with great results.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d18", + "creator": "K-Gun", + "createdAt": 1505332014000, + "text": "

Here is a different approach with fixed length by base, without RegExp replace lack (based on @bendytree's answer);

\n\n
function rand(base) {\n    // default base 10\n    base = (base >= 2 && base <= 36) ? base : 10;\n    for (var i = 0, ret = []; i < base; i++) {\n        ret[i] = ((Math.random() * base) | 0).toString(base)\n            // include 0-9a-zA-Z?\n            // [Math.random() < .5 ? 'toString' : 'toUpperCase']();\n    }\n    return ret.join('');\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d17", + "creator": "artnikpro", + "createdAt": 1501240725000, + "text": "

Random numeric value (up to 16 digits)

\n\n
/**\n * Random numeric value (up to 16 digits)\n * @returns {String}\n */\nfunction randomUid () {\n  return String(Math.floor(Math.random() * 9e15))\n}\n\n// randomUid() -> \"3676724552601324\"\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d1a", + "creator": "Swergas", + "createdAt": 1510842071000, + "text": "

How about something like this: Date.now().toString(36)\nNot very random, but short and quite unique every time you call it.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d1b", + "creator": "Sparw", + "createdAt": 1511260590000, + "text": "

I have made a String prototype which can generate a random String with a given length.

\n\n

You also can secify if you want special chars and you can avoid some.

\n\n
/**\n * STRING PROTOTYPE RANDOM GENERATOR\n * Used to generate a random string\n * @param {Boolean} specialChars\n * @param {Number} length\n * @param {String} avoidChars\n */\nString.prototype.randomGenerator = function (specialChars = false, length = 1, avoidChars = '') {\n    let _pattern = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    _pattern += specialChars === true ? '(){}[]+-*/=' : '';\n    if (avoidChars && avoidChars.length) {\n        for (let char of avoidChars) {\n            _pattern = _pattern.replace(char, '');\n        }\n    }\n    let _random = '';\n    for (let element of new Array(parseInt(length))) {\n        _random += _pattern.charAt(Math.floor(Math.random() * _pattern.length));\n    }\n    return _random;\n};\n
\n\n

You can use like this :

\n\n
// Generate password with specialChars which contains 10 chars and avoid iIlL chars\nvar password = String().randomGenerator(true, 10, 'iIlL');\n
\n\n

Hope it helps.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d1d", + "creator": "ravishi", + "createdAt": 1517865436000, + "text": "

I did not find a clean solution for supporting both lowercase and uppercase characters.

\n\n

Lowercase only support:

\n\n

Math.random().toString(36).substr(2, 5)

\n\n

Building on that solution to support lowercase and uppercase:

\n\n

Math.random().toString(36).substr(2, 5).split('').map(c => Math.random() < 0.5 ? c.toUpperCase() : c).join('');

\n\n

Change the 5 in substr(2, 5) to adjust to the length you need.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d1c", + "creator": "Or Duan", + "createdAt": 1511702239000, + "text": "

A newer version with es6 spread operator:

\n

[...Array(30)].map(() => Math.random().toString(36)[2]).join('')

\n\n", + "upvotes": 192, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32845082fcc3049e927dc", + "creator": "vuza", + "createdAt": 1512471343000, + "text": "Could you explain? Especially why you pass 36 to toString() and why you choose the 3rd element?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32845082fcc3049e927de", + "creator": "Nahuel Greco", + "createdAt": 1583240568000, + "text": "nice, but this solution doesn't include [A-Z] characters as question requested, only [a-z0-9]", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32845082fcc3049e927e0", + "creator": "tam.teixeira", + "createdAt": 1595206562000, + "text": "@NahuelGreco you can do [...Array(30)].map(() => Math.random().toString(36)[2]).join('').toUpperCase()", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 55, + "downvoterUsernames": [] + }, + { + "_id": "62f32845082fcc3049e927e2", + "creator": "Hoxz", + "createdAt": 1649574377000, + "text": "Array(30).fill().map(() => Math.random().toString(36).slice(2)).join('') to generate a huge random string", + "upvotes": 1814, + "upvoterUsernames": [], + "downvotes": 1814, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d1e", + "creator": "user1300214", + "createdAt": 1523177639000, + "text": "

You could use base64:

\n\n
function randomString(length)\n{\n    var rtn = \"\";\n\n    do {\n        rtn += btoa(\"\" + Math.floor(Math.random() * 100000)).substring(0, length);\n    }\n    while(rtn.length < length);\n\n    return rtn;\n}\n
\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d20", + "creator": "Sergio Cabral", + "createdAt": 1529286621000, + "text": "

To meet requirement [a-zA-Z0-9] and length=5 use

\n

For Browser:

\n
btoa(Math.random().toString()).substr(10, 5);\n
\n

For NodeJS:

\n
Buffer.from(Math.random().toString()).toString("base64").substr(10, 5);\n
\n

Lowercase letters, uppercase letters, and numbers will occur.

\n

(it's typescript compatible)

\n", + "upvotes": 105, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32846082fcc3049e927e6", + "creator": "Leif", + "createdAt": 1529913466000, + "text": "I guess, Math.random() could lead to a base64-string with too few characters.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32846082fcc3049e927e8", + "creator": "Wojciech Bednarski", + "createdAt": 1641737612000, + "text": "Why do we need toString in the browser example?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d1f", + "creator": "Automatico", + "createdAt": 1527838237000, + "text": "

Random unicode string

\n\n

This method will return a random string with any of the supported unicode characters, which is not 100% what OP asks for, but what I was looking for:

\n\n
function randomUnicodeString(length){\n    return Array.from({length: length}, ()=>{\n        return String.fromCharCode(Math.floor(Math.random() * (65536)))\n    }).join('')\n}\n
\n\n

Rationale

\n\n

This is the top result of google when searching for \"random string javascript\", but OP asks for a-zA-Z0-9 only.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d21", + "creator": "Pascual Muñoz", + "createdAt": 1534457274000, + "text": "

recursive solution:

\n\n
function generateRamdomId (seedStr) {\nconst len = seedStr.length\nconsole.log('possibleStr', seedStr , ' len ', len)\nif(len <= 1){\n    return seedStr\n}\nconst randomValidIndex  = Math.floor(Math.random() * len)\nconst randomChar = seedStr[randomValidIndex]\nconst chunk1 = seedStr.slice(0, randomValidIndex)\nconst chunk2 = seedStr.slice(randomValidIndex +1)\nconst possibleStrWithoutRandomChar = chunk1.concat(chunk2)\n\nreturn randomChar + generateRamdomId(possibleStrWithoutRandomChar)\n
\n\n

}

\n\n

you can use with the seed you want , dont repeat chars if you dont rea. Example

\n\n
generateRandomId(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\") \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d22", + "creator": "Waruyama", + "createdAt": 1537381877000, + "text": "

For a string with upper- and lowercase letters and digits (0-9a-zA-Z), this may be the version that minifies best:

\n\n
function makeId(length) {\n  var id = '';\n  var rdm62;\n  while (length--) {\n   // Generate random integer between 0 and 61, 0|x works for Math.floor(x) in this case \n   rdm62 = 0 | Math.random() * 62; \n   // Map to ascii codes: 0-9 to 48-57 (0-9), 10-35 to 65-90 (A-Z), 36-61 to 97-122 (a-z)\n   id += String.fromCharCode(rdm62 + (rdm62 < 10 ? 48 : rdm62 < 36 ? 55 : 61)) \n  }\n  return id;\n}\n
\n\n

The content of this function minifies to 97 bytes, while the top answer needs 149 bytes (because of the characters list).

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d23", + "creator": "Ali", + "createdAt": 1537692387000, + "text": "

Case Insensitive Alphanumeric Chars:

\n\n

\r\n
\r\n
function randStr(len) {\r\n  let s = '';\r\n  while (s.length < len) s += Math.random().toString(36).substr(2, len - s.length);\r\n  return s;\r\n}\r\n\r\n// usage\r\nconsole.log(randStr(50));
\r\n
\r\n
\r\n

\n\n

The benefit of this function is that you can get different length random string and it ensures the length of the string.

\n\n

Case Sensitive All Chars:

\n\n

\r\n
\r\n
function randStr(len) {\r\n  let s = '';\r\n  while (len--) s += String.fromCodePoint(Math.floor(Math.random() * (126 - 33) + 33));\r\n  return s;\r\n}\r\n\r\n// usage\r\nconsole.log(randStr(50));
\r\n
\r\n
\r\n

\n\n

Custom Chars

\n\n

\r\n
\r\n
function randStr(len, chars='abc123') {\r\n  let s = '';\r\n  while (len--) s += chars[Math.floor(Math.random() * chars.length)];\r\n  return s;\r\n}\r\n\r\n// usage\r\nconsole.log(randStr(50));\r\nconsole.log(randStr(50, 'abc'));\r\nconsole.log(randStr(50, 'aab')); // more a than b
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d24", + "creator": "ceving", + "createdAt": 1542704282000, + "text": "

This is a slightly improved version of doubletap's answer. It considers gertas's comment about the case, when Math.random() returns 0, 0.5, 0.25, 0.125, etc.

\n\n
((Math.random()+3*Number.MIN_VALUE)/Math.PI).toString(36).slice(-5)\n
\n\n
    \n
  1. It prevents that zero gets passed to toString my adding the smallest float to Math.random().
  2. \n
  3. It ensures that the number passed to toString has enough digits by dividing through an almost irrational number.
  4. \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d25", + "creator": "Elias Zamaria", + "createdAt": 1547600661000, + "text": "

As several people here have pointed out, passing the result of Math.random() directly to .string(36) has several problems.

\n\n

It has poor randomness. The number of characters generated varies, and on average depends on the tricky details of how floating-point numbers work in Javascript. It seems to work if I am trying to generate 11 characters or fewer, but not with more than 11. And it is not flexible. There is no easy way to allow or prohibit certain characters.

\n\n

I have a compact solution, which doesn't have these problems, for anyone using lodash:

\n\n
_.range(11).map(i => _.sample(\"abcdefghijklmnopqrstuvwxyz0123456789\")).join('')\n
\n\n

If you want to allow certain characters (such as uppercase letters) or prohibit certain characters (like ambiguous characters such as l and 1), modify the string above.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d26", + "creator": "Alireza", + "createdAt": 1548766114000, + "text": "

How about this below... this will produce the really random values:

\n\n
function getRandomStrings(length) {\n  const value = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n  const randoms = [];\n  for(let i=0; i < length; i++) {\n     randoms.push(value[Math.floor(Math.random()*value.length)]);\n  }\n  return randoms.join('');\n}\n
\n\n

But if you looking for a shorter syntax one in ES6:

\n\n
const getRandomStrings = length => Math.random().toString(36).substr(-length);\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d27", + "creator": "Parth Raval", + "createdAt": 1555937829000, + "text": "

Above All answers are perfect. but I am adding which is very good and rapid to generate any random string value

\n\n

\r\n
\r\n
function randomStringGenerator(stringLength) {\r\n  var randomString = \"\"; // Empty value of the selective variable\r\n  const allCharacters = \"'`~!@#$%^&*()_+-={}[]:;\\'<>?,./|\\\\ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\"; // listing of all alpha-numeric letters\r\n  while (stringLength--) {\r\n    randomString += allCharacters.substr(Math.floor((Math.random() * allCharacters.length) + 1), 1); // selecting any value from allCharacters varible by using Math.random()\r\n  }\r\n  return randomString; // returns the generated alpha-numeric string\r\n}\r\n\r\nconsole.log(randomStringGenerator(10));//call function by entering the random string you want
\r\n
\r\n
\r\n

\n\n

or

\n\n

\r\n
\r\n
console.log(Date.now())// it will produce random thirteen numeric character value every time.\r\nconsole.log(Date.now().toString().length)// print length of the generated string
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32846082fcc3049e927ee", + "creator": "Molomby", + "createdAt": 1579578404000, + "text": "Date.now() most certainly does not produce thirteen "random" digits", + "upvotes": 651, + "upvoterUsernames": [], + "downvotes": 651, + "downvoterUsernames": [] + }, + { + "_id": "62f32846082fcc3049e927ef", + "creator": "Parth Raval", + "createdAt": 1579926875000, + "text": "@Molomby, brother, I appreciate your time but please write the logic in which case it will not produce thirteen "random" digits.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d28", + "creator": "Adam Pietrasiak", + "createdAt": 1556133446000, + "text": "

Here is my approach (with TypeScript).

\n\n

I've decided to write yet another response because I didn't see any simple solution using modern js and clean code.

\n\n
const DEFAULT_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nfunction getRandomCharFromAlphabet(alphabet: string): string {\n  return alphabet.charAt(Math.floor(Math.random() * alphabet.length));\n}\n\nfunction generateId(idDesiredLength: number, alphabet = DEFAULT_ALPHABET): string {\n  /**\n   * Create n-long array and map it to random chars from given alphabet.\n   * Then join individual chars as string\n   */\n  return Array.from({length: idDesiredLength}).map(() => {\n    return getRandomCharFromAlphabet(alphabet);\n  }).join('');\n}\n\ngenerateId(5); // jNVv7\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d29", + "creator": "Sibevin Wang", + "createdAt": 1558084509000, + "text": "

I just write a simple package to generate a random token with given size, seed and mask. FYI.

\n\n

@sibevin/random-token - https://www.npmjs.com/package/@sibevin/random-token

\n\n
import { RandomToken } from '@sibevin/random-token'\n\nRandomToken.gen({ length: 32 })\n// JxpwdIA37LlHan4otl55PZYyyZrEdsQT\n\nRandomToken.gen({ length: 32, seed: 'alphabet' })\n// NbbtqjmHWJGdibjoesgomGHulEJKnwcI\n\nRandomToken.gen({ length: 32, seed: 'number' })\n// 33541506785847193366752025692500\n\nRandomToken.gen({ length: 32, seed: 'oct' })\n// 76032641643460774414624667410327\n\nRandomToken.gen({ length: 32, seed: 'hex' })\n// 07dc6320bf1c03811df7339dbf2c82c3\n\nRandomToken.gen({ length: 32, seed: 'abc' })\n// bcabcbbcaaabcccabaabcacbcbbabbac\n\nRandomToken.gen({ length: 32, mask: '123abcABC' })\n// vhZp88dKzRZGxfQHqfx7DOL8jKTkWUuO\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d2a", + "creator": "Nahuel Greco", + "createdAt": 1559324263000, + "text": "
const c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\nconst s = [...Array(5)].map(_ => c[~~(Math.random()*c.length)]).join('')\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d2b", + "creator": "Nixinova", + "createdAt": 1580536773000, + "text": "

Simple method:

\n\n
function randomString(length) {\n    let chars = [], output = '';\n    for (let i = 32; i < 127; i ++) {\n        chars.push(String.fromCharCode(i));\n    }\n    for (let i = 0; i < length; i ++) {\n        output += chars[Math.floor(Math.random() * chars.length )];\n    }\n    return output;\n}\n
\n\n

If you want more or less characters change the \"127\" to something else.

\n", + "upvotes": 711, + "upvoterUsernames": [], + "downvotes": 711, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d2c", + "creator": "iamarkadyt", + "createdAt": 1584742159000, + "text": "

One liner:

\n\n
Array(15).fill(null).map(() => Math.random().toString(36).substr(2)).join('')\n// Outputs: 0h61cbpw96y83qtnunwme5lxk1i70a6o5r5lckfcyh1dl9fffydcfxddd69ada9tu9jvqdx864xj1ul3wtfztmh2oz2vs3mv6ej0fe58ho1cftkjcuyl2lfkmxlwua83ibotxqc4guyuvrvtf60naob26t6swzpil\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32847082fcc3049e927f2", + "creator": "iamarkadyt", + "createdAt": 1585939238000, + "text": "To make it shorter change argument in Array(15) to a smaller value. E.g.: Array(4).", + "upvotes": 2768, + "upvoterUsernames": [], + "downvotes": 2768, + "downvoterUsernames": [] + }, + { + "_id": "62f32847082fcc3049e927f4", + "creator": "Nahuel Greco", + "createdAt": 1627748452000, + "text": "erroneous solution, uppercase characters aren't generated.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d2d", + "creator": "Olowu Abayomi", + "createdAt": 1587835567000, + "text": "

//creates a random code which is 10 in lenght,you can change it to yours at your will

\n\n
function createRandomCode(length) {\n    let randomCodes = '';\n    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    let charactersLength = characters.length;\n    for (let i = 0; i < length; i++ ) {\n        randomCodes += characters.charAt(Math.floor(Math.random() * charactersLength))\n    }\n    console.log(\"your reference code is: \".toLocaleUpperCase() + randomCodes);\n };\n createRandomCode(10)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32847082fcc3049e927f5", + "creator": "Rocky Kev", + "createdAt": 1591854895000, + "text": "This is just one of the top answers with very minor modifications.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32847082fcc3049e927f7", + "creator": "Olowu Abayomi", + "createdAt": 1615533529000, + "text": "i just tried making it more easier and readable.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d2e", + "creator": "Marc", + "createdAt": 1589869411000, + "text": "

Generate any number of hexadecimal character (e.g. 32):

\n\n
(function(max){let r='';for(let i=0;i<max/13;i++)r+=(Math.random()+1).toString(16).substring(2);return r.substring(0,max).toUpperCase()})(32);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d2f", + "creator": "Kamil Kiełczewski", + "createdAt": 1592772871000, + "text": "

Crypto-Strong

\n

If you want to get crypto-strong string which meets your requirements (I see answer which use this but gives non valid answers) use

\n
let pass = n=> [...crypto.getRandomValues(new Uint8Array(n))]\n   .map((x,i)=>(i=x/255*61|0,String.fromCharCode(i+(i>9?i>35?61:55:48)))).join``\n
\n

\r\n
\r\n
let pass = n=> [...crypto.getRandomValues(new Uint8Array(n))]\n   .map((x,i)=>(i=x/255*61|0,String.fromCharCode(i+(i>9?i>35?61:55:48)))).join``\n\nconsole.log(pass(5));
\r\n
\r\n
\r\n

\n

Update: thanks to Zibri comment I update code to get arbitrary-long password

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d30", + "creator": "Kamil Kiełczewski", + "createdAt": 1592775396000, + "text": "

Review

\n

Many answers base on trick Math.random().toString(36) but the problem of this approach is that Math.random not always produce number which has at least 5 characters in base 36 e.g.

\n

\r\n
\r\n
let testRnd = n => console.log(`num dec: ${n}, num base36: ${n.toString(36)}, string: ${n.toString(36).substr(2, 5)}`);\n\n\n[\n  Math.random(),\n  // and much more less than 0.5...\n  0.5,\n  0.50077160493827161,\n  0.5015432098765432,\n  0.5023148148148148,\n  0.5030864197530864,\n  // and much more....\n  0.9799597050754459\n].map(n=>testRnd(n));\n\nconsole.log('... and so on');
\r\n
Each of below example (except first) numbers result with less than 5 characters (which not meet OP question requirements)
\r\n
\r\n
\r\n

\n

Here is "generator" which allows manually find such numbers

\n

\r\n
\r\n
function base36Todec(hex) {\n  hex = hex.split(/\\./);\n  return (parseInt(hex[1],36))*(36**-hex[1].length)+ +(parseInt(hex[0],36));\n}\n\nfunction calc(hex) {\n  let dec = base36Todec(hex);\n  msg.innerHTML = `dec: <b>${dec}</b><br>hex test: <b>${dec.toString(36)}</b>`\n} \n\nfunction calc2(dec) {\n  msg2.innerHTML = `dec: <b>${dec}</b><br>hex test: <b>${(+dec).toString(36)}</b>`\n} \n\nlet init=\"0.za1\";\ninp.value=init;\ncalc(init);
\r\n
Type number in range 0-1 using base 36 (0-9,a-z) with less than 5 digits after dot<br>\n<input oninput=\"calc(this.value)\" id=\"inp\" /><div id=\"msg\"></div>\n<br>\nIf above <i>hex test</i> give more digits than 5 after dot - then you can try to copy dec number to below field and join some digit to dec num right side and/or change last digit - it sometimes also produce hex with less digits<br>\n<input oninput=\"calc2(this.value)\" /><br><div id=\"msg2\"></div>
\r\n
\r\n
\r\n

\n

I already give answer here so I will not put here another solution

\n", + "upvotes": 4017, + "upvoterUsernames": [], + "downvotes": 4017, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d31", + "creator": "jerrymouse", + "createdAt": 1593625900000, + "text": "

The following code will produce a cryptographically secured random string of size containing [a-zA-Z0-9], using an npm package crypto-random-string. Install it using:

\n
npm install crypto-random-string\n
\n

To get a random string of 30 characters in the set [a-zA-Z0-9]:

\n
const cryptoRandomString = require('crypto-random-string');\ncryptoRandomString({length: 100, type: 'base64'}).replace(/[/+=]/g,'').substr(-30);\n
\n

Summary: We are replacing /, +, = in a large random base64 string and getting the last N characters.

\n

PS: Use -N in substr

\n", + "upvotes": 1743, + "upvoterUsernames": [], + "downvotes": 1743, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d32", + "creator": "Nirvana", + "createdAt": 1593845628000, + "text": "

In case you cannot type out a charset, using String.fromCharCode and a ranged Math.random allows you to create random strings in any Unicode codepoint range. For example, if you want 17 random Tibetan characters, you can input ranstr(17,0xf00,0xfff), where (0xf00,0xfff) corresponds to the Tibetan Unicode block. In my implementation, the generator will spit out ASCII text if you do not specify a codepoint range.\n

\r\n
\r\n
    function ranchar(a,b) {\n       a = (a === undefined ? 0 : a);\n       b = (b === undefined ? 127 : b);\n       return String.fromCharCode(Math.floor(Math.random() * (b - a) + a)); \n    }\n    \n    function ranstr(len,a,b) {\n      a = a || 32;\n      var result = '';\n      for(var i = 0; i < len; i++) {\n       result += ranchar(a,b)\n      }\n      return result;\n    }\n\n\n//Here are some examples from random Unicode blocks\nconsole.log('In Latin Basic block: '+ ranstr(10,0x0000,0x007f))\nconsole.log('In Latin-1 Supplement block: '+ ranstr(10,0x0080,0x0ff))\nconsole.log('In Currency Symbols block: ' + ranstr(10,0x20a0,0x20cf))\nconsole.log('In Letterlike Symbols block: ' + ranstr(10,0x2100,0x214f))\nconsole.log('In Dingbats block:' + ranstr(10,0x2700,0x27bf))
\r\n
\r\n
\r\n

\n", + "upvotes": 129, + "upvoterUsernames": [], + "downvotes": 129, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d33", + "creator": "user7793758", + "createdAt": 1594917651000, + "text": "

How about extending the String object like so.

\n
String.prototype.random = function(length) {\n   var result = '';\n   for (var i = 0; i < length; i++) {\n      result += this.charAt(Math.floor(Math.random() * this.length));\n   }\n\n   return result;\n};\n\n
\n

using it:

\n
console.log("ABCDEFG".random(5));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d34", + "creator": "Zee", + "createdAt": 1595626356000, + "text": "

Improved @Andrew's answer above :

\n
Array.from({ length : 1 }, () => Math.random().toString(36)[2]).join('');\n
\n

Base 36 conversion of the random number is inconsistent, so selecting a single indice fixes that. You can change the length for a string with the exact length desired.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d35", + "creator": "Dominicentek Gaming", + "createdAt": 1596930620000, + "text": "
function generate(length) {\n  var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9"];\n  var IDtext = "";\n  var i = 0;\n  while (i < length) {\n    var letterIndex = Math.floor(Math.random() * letters.length);\n    var letter = letters[letterIndex];\n    IDtext = IDtext + letter;\n    i++;\n  }\n  console.log(IDtext)\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d36", + "creator": "gogo", + "createdAt": 1597167954000, + "text": "

Generate a secure random alphanumeric Base-62 string:

\n

\r\n
\r\n
function generateUID(length)\n{\n    return window.btoa(Array.from(window.crypto.getRandomValues(new Uint8Array(length * 2))).map((b) => String.fromCharCode(b)).join(\"\")).replace(/[+/]/g, \"\").substring(0, length);\n}\n\nconsole.log(generateUID(22)); // \"yFg3Upv2cE9cKOXd7hHwWp\"\nconsole.log(generateUID(5)); // \"YQGzP\"
\r\n
\r\n
\r\n

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d37", + "creator": "Don Dilanga", + "createdAt": 1609364401000, + "text": "

This is not a perfect solution, but it should work. If you ever get any error, then increase the value given in Uint8Array() constructor. The advantage of this method is it uses getRandomValues() method that generates cryptographically strong random values.

\n

\r\n
\r\n
var array = new Uint8Array(20);\ncrypto.getRandomValues(array);\nvar arrayEncoded =  btoa(String.fromCharCode(...array)).split('');\nvar arrayFiltered = arrayEncoded.filter(value => {\n    switch (value){\n    case \"+\" :\n        return false;\n    case \"/\" :\n        return false;\n    case \"=\" :\n        return false;\n    default :\n      return true;\n   }\n});\nvar password = arrayFiltered.slice(0,5).join('');\nconsole.log(password);
\r\n
\r\n
\r\n

\n

A compact Version

\n

\r\n
\r\n
var array = new Uint8Array(20);\ncrypto.getRandomValues(array);\nvar password = btoa(String.fromCharCode(...array)).split('').filter(value => {\n        return !['+', '/' ,'='].includes(value);\n}).slice(0,5).join('');\nconsole.log(password);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d38", + "creator": "Denys Vitali", + "createdAt": 1614445011000, + "text": "

If you just want uppercase letters (A-Z):

\n
randomAZ(n: number): string {\n      return Array(n)\n        .fill(null)\n        .map(() => Math.random()*100%25 + 'A'.charCodeAt(0))\n        .map(a => String.fromCharCode(a))\n        .join('')\n }\n
\n

If you just want the first letter to be uppercase (A-Z), and the rest to be lower case (a-z):

\n
function RandomWord(n: number): string {\n    return Array(n)\n      .fill(null)\n      .map(() => Math.random()*100%25 + 'A'.charCodeAt(0))\n      .map((a, i) => i === 0? String.fromCharCode(a) : String.fromCharCode(a+32))\n      .join('')\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32848082fcc3049e927ff", + "creator": "Filip Seman", + "createdAt": 1634156480000, + "text": "The set should be [a-zA-Z0-9]", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d39", + "creator": "TheBotlyNoob", + "createdAt": 1621612872000, + "text": "

One liner [a-z]:

\n
String.fromCharCode(97 + Math.floor(Math.random() * 26))\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3b", + "creator": "Siarhei Dudko", + "createdAt": 1623230261000, + "text": "

If you are developing on node js, it is better to use crypto. Here is an example of implementing the randomStr() function

\n
const crypto = require('crypto');\nconst charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';\nconst randomStr = (length = 5) => new Array(length)\n    .fill(null)\n    .map(() => charset.charAt(crypto.randomInt(charset.length)))\n    .join('');\n
\n

If you are not working in a server environment, just replace the random number generator:

\n
const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';\nconst randomStr = (length = 5) => new Array(length)\n    .fill(null)\n    .map(() => charset.charAt(Math.floor(Math.random() * charset.length)))\n    .join('');\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3a", + "creator": "denik1981", + "createdAt": 1622572379000, + "text": "

Love this SO question and their answers. So cleaver and creative solutions were proposed. I came up with mine that is wrapped inside a function that receives the length of the string you want to obtain plus a mode argument to decide how you want it to be composed.

\n

Mode is a 3 length string that accepts only '1s' and '0s' that define what subsets of characters you want to include in the final string. It is grouped by 3 different subset( [0-9], [A-B], [a-b])

\n
'100': [0-9]\n'010': [A-B]\n'101': [0-9] + [a-b]\n'111': [0-9] + [A-B] + [a-b]\n
\n

There are 8 possible combinations (2^N, witn N:#subsets).\nThe '000' mode return an empty string.

\n
function randomStr(l = 1, mode = '111') {\n    if (mode === '000') return '';\n    const r = (n) => Math.floor(Math.random() * n);\n    const m = [...mode].map((v, i) => parseInt(v, 10) * (i + 1)).filter(Boolean).map((v) => v - 1);\n    return [...new Array(l)].reduce((a) => a + String.fromCharCode([(48 + r(10)), (65 + r(26)), (97 + r(26))][m[r(m.length)]]), '')\n}\n
\n

A simple use case will be:

\n
random = randomStr(50, '101')\n// ii3deu9i4jk6dp4gx43g3059vss9uf7w239jl4itv0cth5tj3e\n// Will give you a String[50] composed of [0-9] && [a-b] chars only.\n
\n

The main idea here is to use the UNICODE table instead of randomizing hexadecimals as I saw in many answers. THe power of this approach is that you can extend it very easily to include others subsets of the UNICODE table with litte extra code in there that a random int(16) can't do.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3c", + "creator": "dud3", + "createdAt": 1626272705000, + "text": "
[..."abcdefghijklmnopqrsuvwxyz0123456789"].map((e, i, a) => a[Math.floor(Math.random() * a.length)]).join('')\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3d", + "creator": "francis", + "createdAt": 1626873949000, + "text": "

One-liner using map that gives you full control on the length and characters.

\n
const rnd = (len, chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') => [...Array(len)].map(() => chars.charAt(Math.floor(Math.random() * chars.length))).join('')\n\nconsole.log(rnd(12))\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3e", + "creator": "Filip Seman", + "createdAt": 1634046293000, + "text": "

Just a simple map or reduce implementation should suffice:

\n
const charset: string =\n  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";\n\nconst random1: string = [...Array(5)]\n  .map((_) => charset[Math.floor(Math.random() * charset.length)])\n  .join("");\n\nconst random2: string = [...Array(5)]\n  .reduce<string>(\n    (acc) => acc += charset[Math.floor(Math.random() * charset.length)],\n    "",\n  );\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d3f", + "creator": "Rick", + "createdAt": 1634754315000, + "text": "

Posting an ES6-compatible version for posterity. If this is called a lot, be sure to store the .length values into constant variables.

\n
// USAGE:\n//      RandomString(5);\n//      RandomString(5, 'all');\n//      RandomString(5, 'characters', '0123456789');\nconst RandomString = (length, style = 'frictionless', characters = '') => {\n    const Styles = {\n        'all':          allCharacters,\n        'frictionless': frictionless,\n        'characters':   provided\n    }\n\n    let result              = '';\n    const allCharacters     = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n    const frictionless      = 'ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';\n    const provided          = characters;\n\n    const generate = (set) => {\n        return set.charAt(Math.floor(Math.random() * set.length));\n    };\n\n    for ( let i = 0; i < length; i++ ) {\n        switch(Styles[style]) {\n            case Styles.all:\n                result += generate(allCharacters);\n                break;\n            case Styles.frictionless:\n                result += generate(frictionless);\n                break;\n            case Styles.characters:\n                result += generate(provided);\n                break;\n        }\n    }\n    return result;\n}\n\nexport default RandomString;\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d40", + "creator": "Victor", + "createdAt": 1636723265000, + "text": "

Generate random strings with aA-zZ and 0-9 charachters collection.\nJust call this function with length parameter.

\n

So to answer to this question: generateRandomString(5)

\n
generateRandomString(length){\n    let result = "", seeds\n\n    for(let i = 0; i < length - 1; i++){\n        //Generate seeds array, that will be the bag from where randomly select generated char\n        seeds = [\n            Math.floor(Math.random() * 10) + 48,\n            Math.floor(Math.random() * 25) + 65,\n            Math.floor(Math.random() * 25) + 97\n        ]\n        \n        //Choise randomly from seeds, convert to char and append to result\n        result += String.fromCharCode(seeds[Math.floor(Math.random() * 3)])\n    }\n\n    return result\n}\n
\n

Version that generates strings without numbers:

\n
generateRandomString(length){\n    let result = "", seeds\n\n    for(let i = 0; i < length - 1; i++){\n        seeds = [\n            Math.floor(Math.random() * 25) + 65,\n            Math.floor(Math.random() * 25) + 97\n        ]\n        result += String.fromCharCode(seeds[Math.floor(Math.random() * 2)])\n    }\n\n    return result\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d41", + "creator": "NVRM", + "createdAt": 1648322616000, + "text": "

To generate a hash from an array as a salt, [0,1,2,3] in this example, by this way we may be able to retrieve the hash later to populate a condition.

\n

Simply feed a random array, or use as extra safe and fast finger-printing of arrays.

\n

\r\n
\r\n
/* This method is very fast and is suitable into intensive loops */\n/* Return a mix of uppercase and lowercase chars */\n\n/* This will always output the same hash, since the salt array is the same */\nconsole.log(\n  btoa(String.fromCharCode(...new Uint8Array( [0,1,2,3] )))\n)\n\n/* Always output a random hex hash of here: 30 chars  */\nconsole.log(\n  btoa(String.fromCharCode(...new Uint8Array( Array(30).fill().map(() => Math.round(Math.random() * 30)) )))\n)
\r\n
\r\n
\r\n

\n

Use HMAC from crypto API, for more: https://stackoverflow.com/a/56416039/2494754

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d42", + "creator": "Arunprasanth M", + "createdAt": 1652271201000, + "text": "

//To return a random letter

\n
let alpha = "ABCDEFGHIGKLMNOPQRSTUVWXYZ";\nconsole.log(alpha.charAt(Math.floor(Math.random() * alpha.length)));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32849082fcc3049e92807", + "creator": "Asons", + "createdAt": 1652416497000, + "text": "Please avoid posting solutions that already exists.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d43", + "creator": "Tiago Rangel de Sousa", + "createdAt": 1659111833000, + "text": "

You can use Web Crypto's API:

\n

\r\n
\r\n
console.log(self.crypto.getRandomValues(new Uint32Array(1))[0])
\r\n
\r\n
\r\n

\n

(original answer here)

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 7, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90c60", + "creator": "Muaz Khan", + "createdAt": 1379170032000, + "text": "Math.random().toString(36).replace(/[^a-z]+/g, '')", + "upvotes": 122, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c61", + "creator": "chryss", + "createdAt": 1379997394000, + "text": "Please put the solution in a solution.", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c62", + "creator": "Friedrich", + "createdAt": 1408094905000, + "text": "Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);", + "upvotes": 539, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c63", + "creator": "Déjà vu", + "createdAt": 1423122448000, + "text": "@MuazKhan @frieder the result range is [a-zA-Z0-9] so why the [^a-z]? Also, what about random uppercase letters?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c64", + "creator": "bohem.be", + "createdAt": 1432900450000, + "text": "@ring0 thats quite basic no? .toUpperCase() on the end", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c65", + "creator": "Dan", + "createdAt": 1565723136000, + "text": "If anyone is here looking to generate an id or unique identifier, let your DB do that job or use a UUID library instead.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90c66", + "creator": "Kevin Connors", + "createdAt": 1646551123000, + "text": "@Friedrich this answer is incorrect. The OP asked for a-z, A-Z, 0-9, this does not include numbers.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2324163, + "uvac": 2324249 + } + }, + { + "_id": "62f321bb082fcc3049e8fed6", + "title": "event.preventDefault() vs. return false", + "title-lowercase": "event.preventdefault() vs. return false", + "creator": "RaYell", + "createdAt": 1251719911000, + "status": "open", + "text": "

When I want to prevent other event handlers from executing after a certain event is fired, I can use one of two techniques. I'll use jQuery in the examples, but this applies to plain-JS as well:

\n\n

1. event.preventDefault()

\n\n
$('a').click(function (e) {\n    // custom handling here\n    e.preventDefault();\n});\n
\n\n

2. return false

\n\n
$('a').click(function () {\n    // custom handling here\n    return false;\n});\n
\n\n

Is there any significant difference between those two methods of stopping event propagation?

\n\n

For me, return false; is simpler, shorter and probably less error prone than executing a method. With the method, you have to remember about correct casing, parenthesis, etc.

\n\n

Also, I have to define the first parameter in callback to be able to call the method. Perhaps, there are some reasons why I should avoid doing it like this and use preventDefault instead? What's the better way?

\n", + "upvotes": 4087, + "upvoterUsernames": [], + "downvotes": 906, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 913923, + "answers": 13, + "answerItems": [ + { + "_id": "62f321c3082fcc3049e90790", + "creator": "Eldar Djafarov", + "createdAt": 1251720151000, + "text": "

You can hang a lot of functions on the onClick event for one element. How can you be sure the false one will be the last one to fire? preventDefault on the other hand will definitely prevent only the default behavior of the element.

\n", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90791", + "creator": "rahul", + "createdAt": 1251720264000, + "text": "

I think

\n\n

event.preventDefault()

\n\n

is the w3c specified way of canceling events.

\n\n

You can read this in the W3C spec on Event cancelation.

\n\n

Also you can't use return false in every situation. When giving a javascript function in the href attribute and if you return false then the user will be redirected to a page with false string written.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e7d", + "creator": "mplungjan", + "createdAt": 1329310389000, + "text": "Not in any browser I have ever met. Perhaps you confuse that with <a href="javascript:return false" or something?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f325de082fcc3049e91e7e", + "creator": "Mark Amery", + "createdAt": 1453590647000, + "text": "-1; the claim that a href that returns false will generate a text page with the word "false" written on it is complete nonsense.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f325de082fcc3049e91e7f", + "creator": "Phil", + "createdAt": 1509984103000, + "text": "(minus)1; broken link + complete nonsense", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90792", + "creator": "karim79", + "createdAt": 1251720319000, + "text": "

return false from within a jQuery event handler is effectively the same as calling both e.preventDefault and e.stopPropagation on the passed jQuery.Event object.

\n\n

e.preventDefault() will prevent the default event from occuring, e.stopPropagation() will prevent the event from bubbling up and return false will do both. Note that this behaviour differs from normal (non-jQuery) event handlers, in which, notably, return false does not stop the event from bubbling up.

\n\n

Source: John Resig

\n\n

Any benefit to using event.preventDefault() over \"return false\" to cancel out an href click?

\n", + "upvotes": 4179, + "upvoterUsernames": [], + "downvotes": 1230, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e81", + "creator": "Govinda Sakhare", + "createdAt": 1471431541000, + "text": "what is really mean by bubbling? example?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90793", + "creator": "Garrett", + "createdAt": 1277069489000, + "text": "

This is not, as you've titled it, a \"JavaScript\" question; it is a question regarding the design of jQuery.

\n\n

jQuery and the previously linked citation from John Resig (in karim79's message) seem to be the source misunderstanding of how event handlers in general work.

\n\n

Fact: An event handler that returns false prevents the default action for that event. It does not stop the event propagation. Event handlers have always worked this way, since the old days of Netscape Navigator.

\n\n

The documentation from MDN explains how return false in an event handler works

\n\n

What happens in jQuery is not the same as what happens with event handlers. DOM event listeners and MSIE \"attached\" events are a different matter altogether.

\n\n

For further reading, see attachEvent on MSDN and the W3C DOM 2 Events documentation.

\n", + "upvotes": 168, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e83", + "creator": "Alexander Derck", + "createdAt": 1476221162000, + "text": "I hate how every javascript search result in google is actually about jQuery, +1", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f325de082fcc3049e91e85", + "creator": "ProfK", + "createdAt": 1580189205000, + "text": "He has tagged it as both JavaScript and jQuery, and both are correct. jQuery is merely a sub-set of JavaScript, not its own language.", + "upvotes": 1638, + "upvoterUsernames": [], + "downvotes": 1638, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90795", + "creator": "JAAulde", + "createdAt": 1302373953000, + "text": "

Generally, your first option (preventDefault()) is the one to take, but you have to know what context you're in and what your goals are.

\n\n

Fuel Your Coding has a great article on return false; vs event.preventDefault() vs event.stopPropagation() vs event.stopImmediatePropagation().

\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e86", + "creator": "JAAulde", + "createdAt": 1501688694000, + "text": "@VisruthCV See my added note with link to archive version", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90794", + "creator": "Jeff Poulton", + "createdAt": 1295735829000, + "text": "

From my experience, there is at least one clear advantage when using event.preventDefault() over using return false. Suppose you are capturing the click event on an anchor tag, otherwise which it would be a big problem if the user were to be navigated away from the current page. If your click handler uses return false to prevent browser navigation, it opens the possibility that the interpreter will not reach the return statement and the browser will proceed to execute the anchor tag's default behavior.

\n\n
$('a').click(function (e) {\n  // custom handling here\n\n  // oops...runtime error...where oh where will the href take me?\n\n  return false;\n});\n
\n\n

The benefit to using event.preventDefault() is that you can add this as the first line in the handler, thereby guaranteeing that the anchor's default behavior will not fire, regardless if the last line of the function is not reached (eg. runtime error).

\n\n
$('a').click(function (e) {\n  e.preventDefault();\n\n  // custom handling here\n\n  // oops...runtime error, but at least the user isn't navigated away.\n});\n
\n", + "upvotes": 881, + "upvoterUsernames": [], + "downvotes": 429, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e88", + "creator": "Fabio Martins", + "createdAt": 1580763645000, + "text": "Both are ways to block the default behaviour of an event, it's just a matter of what problem you're trying to solve.", + "upvotes": 196, + "upvoterUsernames": [], + "downvotes": 196, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90796", + "creator": "James Drinkard", + "createdAt": 1361289277000, + "text": "

When using jQuery, return false is doing 3 separate things when you call it:

\n\n
    \n
  1. event.preventDefault();
  2. \n
  3. event.stopPropagation();
  4. \n
  5. Stops callback execution and returns immediately when called.
  6. \n
\n\n

See jQuery Events: Stop (Mis)Using Return False for more information and examples.

\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325de082fcc3049e91e8b", + "creator": "Fabio Martins", + "createdAt": 1580763757000, + "text": "If other parts of the code are listening to this event, event.stopPropagation(); will cancel their callback?", + "upvotes": 708, + "upvoterUsernames": [], + "downvotes": 708, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90797", + "creator": "Naga Srinu Kapusetti", + "createdAt": 1362857254000, + "text": "

I think the best way to do this is to use event.preventDefault() because if some exception is raised in the handler, then the return false statement will be skipped and the behavior will be opposite to what you want.

\n\n

But if you are sure that the code won't trigger any exceptions, then you can go with any of the method you wish.

\n\n

If you still want to go with the return false, then you can put your entire handler code in a try catch block like below:

\n\n
$('a').click(function (e) {\n  try{\n      your code here.........\n  }\n   catch(e){}\n  return false;\n});\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90798", + "creator": "Dilip0165", + "createdAt": 1411990088000, + "text": "

My opinion from my experience saying, that it is always better to use

\n\n
event.preventDefault() \n
\n\n

Practically\n to stop or prevent submit event, whenever we required rather than return false\n event.preventDefault() works fine.

\n", + "upvotes": 489, + "upvoterUsernames": [], + "downvotes": 489, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325df082fcc3049e91e8e", + "creator": "Mark Amery", + "createdAt": 1453590458000, + "text": "Better why? You've literally given zero justification whatsoever here. -1.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f325df082fcc3049e91e8f", + "creator": "Taiger", + "createdAt": 1503442223000, + "text": "In the case there are multiple event bindings, e.preventDefault() is more targeted than return false.", + "upvotes": 1449, + "upvoterUsernames": [], + "downvotes": 1449, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90799", + "creator": "Abdullah Shoaib", + "createdAt": 1518164062000, + "text": "

The main difference between return false and event.preventDefault() is that your code below return false will not be executed and in event.preventDefault() case your code will execute after this statement.

\n\n

When you write return false it do the following things for you behind the scenes.

\n\n
* Stops callback execution and returns immediately when called.\n* event.stopPropagation();\n* event.preventDefault();\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9079a", + "creator": "Parth Raval", + "createdAt": 1524812106000, + "text": "

e.preventDefault();

\n\n

It simply stops the default action of an element.

\n\n
\n

Instance Ex.:-

\n
\n\n

prevents the hyperlink from following the URL, prevents the submit button to submit the form. When you have many event handlers and you just want to prevent default event from occuring, & occuring from many times,\nfor that we need to use in the top of the function().

\n\n
\n

Reason:-

\n
\n\n

The reason to use e.preventDefault(); is that in our code so something goes wrong in the code, then it will allow to execute the link or form to get submitted or allow to execute or allow whatever action you need to do. & link or submit button will get submitted & still allow further propagation of the event.

\n\n

\r\n
\r\n
<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"ltr\">\r\n   <head>\r\n      <meta charset=\"utf-8\">\r\n      <title></title>\r\n   </head>\r\n   <body>\r\n      <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n      <a href=\"https://www.google.com\" onclick=\"doSomethingElse()\">Preventsss page from redirect</a>\r\n      <script type=\"text/javascript\">\r\n         function doSomethingElse(){\r\n           console.log(\"This is Test...\");\r\n         }\r\n         $(\"a\").click(function(e){\r\n          e.preventDefault(); \r\n         });\r\n      </script>\r\n   </body>\r\n</html>
\r\n
\r\n
\r\n

\n\n

return False;

\n\n

It simply stops the execution of the function().

\n\n

\"return false;\" will end the whole execution of process.

\n\n
\n

Reason:-

\n
\n\n

The reason to use return false; is that you don't want to execute the function any more in strictly mode.

\n\n

\r\n
\r\n
<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"ltr\">\r\n   <head>\r\n      <meta charset=\"utf-8\">\r\n      <title></title>\r\n   </head>\r\n   <body>\r\n      <a href=\"#\" onclick=\"returnFalse();\">Blah</a>\r\n      <script type=\"text/javascript\">\r\n         function returnFalse(){\r\n         console.log(\"returns false without location redirection....\")\r\n             return false;\r\n             location.href = \"http://www.google.com/\";\r\n         \r\n         }\r\n      </script>\r\n   </body>\r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9079b", + "creator": "Alireza", + "createdAt": 1531912077000, + "text": "

Basically, this way you combine things because jQuery is a framework which mostly focuses on HTML elements, you basically preventing the default, but at the same time, you stop propagation to bubble up.

\n\n

So we can simply say, return false in jQuery is equal to:

\n\n

return false is e.preventDefault AND e.stopPropagation

\n\n

But also don't forget it's all in jQuery or DOM related functions, when you run it on the element, basically, it prevents everything from firing including the default behaviour and propagation of the event.

\n\n

Basically before starting using return false;, first understand what e.preventDefault(); and e.stopPropagation(); do, then if you think you need both at the same time, then simply use it.

\n\n

So basically this code below:

\n\n
$('div').click(function () {\n  return false;\n});\n
\n\n

is equal to this code:

\n\n
$('div').click(function (event) {\n  event.preventDefault();\n  event.stopPropagation();\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e9079c", + "creator": "Sathish Shajahan", + "createdAt": 1557810585000, + "text": "

From my experience event.stopPropagation() is mostly used in CSS effect or animation works, for instance when you have hover effect for both card and button element, when you hover on the button both card and buttons hover effect will be triggered in this case, you can use event.stopPropagation() stop bubbling actions, and event.preventDefault() is for prevent default behaviour of browser actions. For instance, you have form but you only defined click event for the submit action, if the user submits the form by pressing enter, the browser triggered by keypress event, not your click event here you should use event.preventDefault() to avoid inappropriate behavior. I don't know what the hell is return false; sorry.For more clarification visit this link and play around with line #33 https://www.codecademy.com/courses/introduction-to-javascript/lessons/requests-i/exercises/xhr-get-request-iv

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321c3082fcc3049e9078e", + "creator": "Adrien Be", + "createdAt": 1397632840000, + "text": "This answer has a table explaining it all stackoverflow.com/a/5302939/759452", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9078f", + "creator": "javad bat", + "createdAt": 1528810604000, + "text": "you must consider differentation between vanilla js and jquery", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 918012, + "uvac": 918025 + } + }, + { + "_id": "62f321bb082fcc3049e8fef6", + "title": "Disable/enable an input with jQuery?", + "title-lowercase": "disable/enable an input with jquery?", + "creator": "omg", + "createdAt": 1252732914000, + "status": "open", + "text": "
$input.disabled = true;\n
\n\n

or

\n\n
$input.disabled = \"disabled\";\n
\n\n

Which is the standard way? And, conversely, how do you enable a disabled input?

\n", + "upvotes": 4955, + "upvoterUsernames": [], + "downvotes": 2477, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2498269, + "answers": 19, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90c8e", + "creator": "Harini Sekar", + "createdAt": 1396866436000, + "text": "
    // Disable #x\n    $( \"#x\" ).prop( \"disabled\", true );\n    // Enable #x\n    $( \"#x\" ).prop( \"disabled\", false );\n
\n\n

Sometimes you need to disable/enable the form element like input or textarea. Jquery helps you to easily make this with setting disabled attribute to \"disabled\".\nFor e.g.:

\n\n
  //To disable \n  $('.someElement').attr('disabled', 'disabled');\n
\n\n

To enable disabled element you need to remove \"disabled\" attribute from this element or empty it's string. For e.g:

\n\n
//To enable \n$('.someElement').removeAttr('disabled');\n\n// OR you can set attr to \"\" \n$('.someElement').attr('disabled', '');\n
\n\n

refer :http://garmoncheg.blogspot.fr/2011/07/how-to-disableenable-element-with.html

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c90", + "creator": "daVe", + "createdAt": 1410539305000, + "text": "

If you just want to invert the current state (like a toggle button behaviour):

\n\n
$(\"input\").prop('disabled', ! $(\"input\").prop('disabled') );\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fb082fcc3049e926b7", + "creator": "Floww", + "createdAt": 1588239066000, + "text": "thanks i have a same thing for the toggle it is; $("input").prop('disabled', function(i, v) { return !v; });", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c8d", + "creator": "geekbuntu", + "createdAt": 1342612314000, + "text": "

Just for the sake of new conventions && making it adaptable going forward (unless things change drastically with ECMA6(????):

\n\n
$(document).on('event_name', '#your_id', function() {\n    $(this).removeAttr('disabled');\n});\n
\n\n

and

\n\n
$(document).off('event_name', '#your_id', function() {\n    $(this).attr('disabled','disabled');   \n});\n
\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fb082fcc3049e926ba", + "creator": "crazymykl", + "createdAt": 1381424163000, + "text": "The former works for elements inserted into the DOM at any point, the latter only for those extant at that moment.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926bc", + "creator": "SepehrM", + "createdAt": 1460036807000, + "text": "@crazymykl Correct but you shouldn't add elements with an id already present on your page.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c8f", + "creator": "Sajjad Shirazy", + "createdAt": 1410015220000, + "text": "
$(\"input\")[0].disabled = true;\n
\n\n

or

\n\n
$(\"input\")[0].disabled = false;\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fb082fcc3049e926bf", + "creator": "basic6", + "createdAt": 1413904577000, + "text": "Of course the question asked for jQuery and this is changing the state in plain JavaScript, but it works.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926c1", + "creator": "cjsimon", + "createdAt": 1525210867000, + "text": "This changes the state in JavaScript, but it still uses a jQuery selector to get the first input.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926c3", + "creator": "Sajjad Shirazy", + "createdAt": 1525243703000, + "text": "But i don't think we are making encyclopedia of jquery here, if an answer works, it's good", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c91", + "creator": "Nicu Surdu", + "createdAt": 1413639759000, + "text": "

You can put this somewhere global in your code:

\n\n
$.prototype.enable = function () {\n    $.each(this, function (index, el) {\n        $(el).removeAttr('disabled');\n    });\n}\n\n$.prototype.disable = function () {\n    $.each(this, function (index, el) {\n        $(el).attr('disabled', 'disabled');\n    });\n}\n
\n\n

And then you can write stuff like:

\n\n
$(\".myInputs\").enable();\n$(\"#otherInput\").disable();\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fb082fcc3049e926c5", + "creator": "Nicu Surdu", + "createdAt": 1432133761000, + "text": "@TrueBlueAussie What is the downside of using attr ? I use the above code in some projects and as far as I remember it works ok", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c8c", + "creator": "gnarf", + "createdAt": 1252733014000, + "text": "

jQuery 1.6+

\n\n

To change the disabled property you should use the .prop() function.

\n\n
$(\"input\").prop('disabled', true);\n$(\"input\").prop('disabled', false);\n
\n\n

jQuery 1.5 and below

\n\n

The .prop() function doesn't exist, but .attr() does similar:

\n\n

Set the disabled attribute.

\n\n
$(\"input\").attr('disabled','disabled');\n
\n\n

To enable again, the proper method is to use .removeAttr()

\n\n
$(\"input\").removeAttr('disabled');\n
\n\n

In any version of jQuery

\n\n

You can always rely on the actual DOM object and is probably a little faster than the other two options if you are only dealing with one element:

\n\n
// assuming an event handler thus 'this'\nthis.disabled = true;\n
\n\n

The advantage to using the .prop() or .attr() methods is that you can set the property for a bunch of selected items.

\n\n
\n\n

Note: In 1.6 there is a .removeProp() method that sounds a lot like removeAttr(), but it SHOULD NOT BE USED on native properties like 'disabled' Excerpt from the documentation:

\n\n
\n

Note: Do not use this method to remove native properties such as checked, disabled, or selected. This will remove the property completely and, once removed, cannot be added again to element. Use .prop() to set these properties to false instead.

\n
\n\n

In fact, I doubt there are many legitimate uses for this method, boolean props are done in such a way that you should set them to false instead of \"removing\" them like their \"attribute\" counterparts in 1.5

\n", + "upvotes": 6562, + "upvoterUsernames": [], + "downvotes": 2515, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327fb082fcc3049e926c7", + "creator": "OneChillDude", + "createdAt": 1367509554000, + "text": "Does this just prevent the user from accessing it, or does it actually remove it from the web request?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926c9", + "creator": "nullability", + "createdAt": 1377011220000, + "text": "@bwheeler96 It does both. A disabled input element will not be submitted, and the user will be unable to change its value.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926cb", + "creator": "dbrin", + "createdAt": 1399593461000, + "text": "Also remember to use true/false booleans and not strings to enable/disable properties", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926cd", + "creator": "wings", + "createdAt": 1479350101000, + "text": "@JeffLowery You are right. I have to use them both to disable an anchor element.", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 47, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926cf", + "creator": "Henrik Erlandsson", + "createdAt": 1523015395000, + "text": "var o=$("#elem");o.disabled=true; does not work here. It would be nice if it did, any thoughts?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926d1", + "creator": "quemeful", + "createdAt": 1537993689000, + "text": "the funniest part of this answer is the use of single quotes and double quotes in the same line of JavaScript", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fb082fcc3049e926d3", + "creator": "Jerry King", + "createdAt": 1620380768000, + "text": "what should be done for dynamically adding elements.?!", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c92", + "creator": "user3831708", + "createdAt": 1443262958000, + "text": "
<html>\n<body>\n\nName: <input type=\"text\" id=\"myText\">\n\n\n\n<button onclick=\"disable()\">Disable Text field</button>\n<button onclick=\"enable()\">Enable Text field</button>\n\n<script>\nfunction disable() {\n    document.getElementById(\"myText\").disabled = true;\n}\nfunction enable() {\n    document.getElementById(\"myText\").disabled = false;\n}\n</script>\n\n</body>\n</html>\n
\n", + "upvotes": 285, + "upvoterUsernames": [], + "downvotes": 285, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c93", + "creator": "Atif Hussain", + "createdAt": 1465902741000, + "text": "

In jQuery Mobile:

\n\n

For disable

\n\n
$('#someselectElement').selectmenu().selectmenu('disable').selectmenu('refresh', true);\n$('#someTextElement').textinput().textinput('disable');\n
\n\n

For enable

\n\n
$('#someselectElement').selectmenu().selectmenu('enable').selectmenu('refresh', true);\n$('#someTextElement').textinput('enable');\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c94", + "creator": "Imants Volkovs", + "createdAt": 1510827899000, + "text": "

I used @gnarf answer and added it as function

\n\n
   $.fn.disabled = function (isDisabled) {\n     if (isDisabled) {\n       this.attr('disabled', 'disabled');\n     } else {\n       this.removeAttr('disabled');\n     }\n   };\n
\n\n

Then use like this

\n\n
$('#myElement').disable(true);\n
\n", + "upvotes": 5571, + "upvoterUsernames": [], + "downvotes": 5571, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c96", + "creator": "Pawel", + "createdAt": 1518373789000, + "text": "

Update for 2018:

\n\n

Now there's no need for jQuery and it's been a while since document.querySelector or document.querySelectorAll (for multiple elements) do almost exactly same job as $, plus more explicit ones getElementById, getElementsByClassName, getElementsByTagName

\n\n

Disabling one field of \"input-checkbox\" class

\n\n
document.querySelector('.input-checkbox').disabled = true;\n
\n\n

or multiple elements

\n\n
document.querySelectorAll('.input-checkbox').forEach(el => el.disabled = true);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c9c", + "creator": "Siddhartha", + "createdAt": 1550843458000, + "text": "

this works for me

\n\n
$(\"#values:input\").attr(\"disabled\",true);\n$(\"#values:input\").attr(\"disabled\",false);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c9d", + "creator": "Kamil Kiełczewski", + "createdAt": 1578595858000, + "text": "

Approach 4 (this is extension of wild coder answer)

\n\n

\r\n
\r\n
txtName.disabled=1     // 0 for enable
\r\n
<input id=\"txtName\">
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c9e", + "creator": "mukhtar alam", + "createdAt": 1614676742000, + "text": "

An alternate way to disable the input field is by using jQuery and css like this:

\n
jQuery("#inputFieldId").css({"pointer-events":"none"})\n
\n

and to enable the same input the code is as follows:

\n
jQuery("#inputFieldId").css({"pointer-events":""})\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c98", + "creator": "SPARTAN", + "createdAt": 1533205585000, + "text": "

You can use the jQuery prop() method to disable or enable form element or control dynamically using jQuery. The prop() method require jQuery 1.6 and above.

\n\n

Example:

\n\n
<script type=\"text/javascript\">\n        $(document).ready(function(){\n            $('form input[type=\"submit\"]').prop(\"disabled\", true);\n            $(\".agree\").click(function(){\n                if($(this).prop(\"checked\") == true){\n                    $('form input[type=\"submit\"]').prop(\"disabled\", false);\n                }\n                else if($(this).prop(\"checked\") == false){\n                    $('form input[type=\"submit\"]').prop(\"disabled\", true);\n                }\n            });\n        });\n    </script>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c97", + "creator": "rap-2-h", + "createdAt": 1528119816000, + "text": "

2018, without JQuery (ES6)

\n\n

Disable all input:

\n\n
[...document.querySelectorAll('input')].map(e => e.disabled = true);\n
\n\n

Disable input with id=\"my-input\"

\n\n
document.getElementById('my-input').disabled = true;\n
\n\n

The question is with JQuery, it's just FYI.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c95", + "creator": "Hasib Kamal Chowdhury", + "createdAt": 1513701188000, + "text": "

Disable true for input type :

\n\n
\n

In case of a specific input type (Ex. Text type input)

\n
\n\n
$(\"input[type=text]\").attr('disabled', true);\n
\n\n
\n

For all type of input type

\n
\n\n
$(\"input\").attr('disabled', true);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c4082fcc3049e92e87", + "creator": "Harry Bosh", + "createdAt": 1556098205000, + "text": "Thanks this helped me isolate to an input name. \t\t\t\t$("input[name=method]").prop('disabled', true);", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c9a", + "creator": "Tomer Ben David", + "createdAt": 1537513469000, + "text": "

Disable:

\n\n
$('input').attr('readonly', true); // Disable it.\n$('input').addClass('text-muted'); // Gray it out with bootstrap.\n
\n\n

Enable:

\n\n
$('input').attr('readonly', false); // Enable it.\n$('input').removeClass('text-muted'); // Back to normal color with bootstrap.\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c99", + "creator": "wild coder", + "createdAt": 1533207578000, + "text": "

There are many ways using them you can enable/disable any element :

\n\n

Approach 1

\n\n
$(\"#txtName\").attr(\"disabled\", true);\n
\n\n

Approach 2

\n\n
$(\"#txtName\").attr(\"disabled\", \"disabled\");\n
\n\n

If you are using jQuery 1.7 or higher version then use prop(), instead of attr().

\n\n
$(\"#txtName\").prop(\"disabled\", \"disabled\");\n
\n\n

If you wish to enable any element then you just have to do opposite of what you did to make it disable. However jQuery provides another way to remove any attribute.

\n\n

Approach 1

\n\n
$(\"#txtName\").attr(\"disabled\", false);\n
\n\n

Approach 2

\n\n
$(\"#txtName\").attr(\"disabled\", \"\");\n
\n\n

Approach 3

\n\n
$(\"#txtName\").removeAttr(\"disabled\");\n
\n\n

Again, if you are using jQuery 1.7 or higher version then use prop(), instead of attr(). That's is. This is how you enable or disable any element using jQuery.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c9b", + "creator": "Abdus Salam Azad", + "createdAt": 1540975116000, + "text": "

Use like this,

\n\n
 $( \"#id\" ).prop( \"disabled\", true );\n\n $( \"#id\" ).prop( \"disabled\", false );\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90c8b", + "creator": "Onshop", + "createdAt": 1404467538000, + "text": "I found the DependsOn plugin which you might find useful", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2503225, + "uvac": 2503244 + } + }, + { + "_id": "62f321bb082fcc3049e8fefd", + "title": "Generating random whole numbers in JavaScript in a specific range", + "title-lowercase": "generating random whole numbers in javascript in a specific range", + "creator": "zacharyliu", + "createdAt": 1254859541000, + "status": "open", + "text": "

How can I generate random whole numbers between two specified variables in JavaScript, e.g. x = 4 and y = 8 would output any of 4, 5, 6, 7, 8?

\n", + "upvotes": 2443, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1762255, + "answers": 35, + "answerItems": [ + { + "_id": "62f321cc082fcc3049e90e6d", + "creator": "Chris", + "createdAt": 1254859768000, + "text": "
function getRandomInt(lower, upper)\n{\n    //to create an even sample distribution\n    return Math.floor(lower + (Math.random() * (upper - lower + 1)));\n\n    //to produce an uneven sample distribution\n    //return Math.round(lower + (Math.random() * (upper - lower)));\n\n    //to exclude the max value from the possible values\n    //return Math.floor(lower + (Math.random() * (upper - lower)));\n}\n
\n\n

To test this function, and variations of this function, save the below HTML/JavaScript to a file and open with a browser. The code will produce a graph showing the distribution of one million function calls. The code will also record the edge cases, so if the the function produces a value greater than the max, or less than the min, you.will.know.about.it.

\n\n
<html>\n    <head>\n        <script type=\"text/javascript\">\n        function getRandomInt(lower, upper)\n        {\n            //to create an even sample distribution\n            return Math.floor(lower + (Math.random() * (upper - lower + 1)));\n\n            //to produce an uneven sample distribution\n            //return Math.round(lower + (Math.random() * (upper - lower)));\n\n            //to exclude the max value from the possible values\n            //return Math.floor(lower + (Math.random() * (upper - lower)));\n        }\n\n        var min = -5;\n        var max = 5;\n\n        var array = new Array();\n\n        for(var i = 0; i <= (max - min) + 2; i++) {\n          array.push(0);\n        }\n\n        for(var i = 0; i < 1000000; i++) {\n            var random = getRandomInt(min, max);\n            array[random - min + 1]++;\n        }\n\n        var maxSample = 0;\n        for(var i = 0; i < max - min; i++) {\n            maxSample = Math.max(maxSample, array[i]);\n        }\n\n        //create a bar graph to show the sample distribution\n        var maxHeight = 500;\n        for(var i = 0; i <= (max - min) + 2; i++) {\n            var sampleHeight = (array[i]/maxSample) * maxHeight;\n\n            document.write('<span style=\"display:inline-block;color:'+(sampleHeight == 0 ? 'black' : 'white')+';background-color:black;height:'+sampleHeight+'px\">&nbsp;[' + (i + min - 1) + ']:&nbsp;'+array[i]+'</span>&nbsp;&nbsp;');\n        }\n        document.write('<hr/>');\n        </script>\n    </head>\n    <body>\n\n    </body>\n</html>\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e6c", + "creator": "Gordon Gustafson", + "createdAt": 1254859700000, + "text": "

Use:

\n
function getRandomizer(bottom, top) {\n    return function() {\n        return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;\n    }\n}\n
\n

Usage:

\n
var rollDie = getRandomizer( 1, 6 );\n\nvar results = ""\nfor ( var i = 0; i<1000; i++ ) {\n    results += rollDie() + " ";    // Make a string filled with 1000 random numbers in the range 1-6.\n}\n
\n

Breakdown:

\n

We are returning a function (borrowing from functional programming) that when called, will return a random integer between the the values bottom and top, inclusive. We say 'inclusive' because we want to include both bottom and top in the range of numbers that can be returned. This way, getRandomizer( 1, 6 ) will return either 1, 2, 3, 4, 5, or 6.

\n

('bottom' is the lower number, and 'top' is the greater number)

\n
Math.random() * ( 1 + top - bottom )\n
\n

Math.random() returns a random double between 0 and 1, and if we multiply it by one plus the difference between top and bottom, we'll get a double somewhere between 0 and 1+b-a.

\n
Math.floor( Math.random() * ( 1 + top - bottom ) )\n
\n

Math.floor rounds the number down to the nearest integer. So we now have all the integers between 0 and top-bottom. The 1 looks confusing, but it needs to be there because we are always rounding down, so the top number will never actually be reached without it. The random decimal we generate needs to be in the range 0 to (1+top-bottom) so we can round down and get an integer in the range 0 to top-bottom:

\n
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom\n
\n

The code in the previous example gave us an integer in the range 0 and top-bottom, so all we need to do now is add bottom to that result to get an integer in the range bottom and top inclusive. :D

\n
\n

NOTE: If you pass in a non-integer value or the greater number first you'll get undesirable behavior, but unless anyone requests it I am not going to delve into the argument checking code as it’s rather far from the intent of the original question.

\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929ce", + "creator": "ajax333221", + "createdAt": 1328296876000, + "text": "@some, It could be worse, I am 2½ years + 1 day later ^^", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929cf", + "creator": "Chris", + "createdAt": 1334085032000, + "text": "+1, I tested your code, it appears to create a correct value. Creative structure to handle fixed scenarios that might be repeated a lot in the code.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929d0", + "creator": "Slava", + "createdAt": 1496921250000, + "text": "Why do you have a function within a function for this?", + "upvotes": 107, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e6f", + "creator": "user1764199", + "createdAt": 1351203341000, + "text": "

For a random integer with a range, try:

\n\n
function random(minimum, maximum) {\n  var bool = true;\n\n  while (bool) {\n    var number = (Math.floor(Math.random() * maximum + 1) + minimum);\n    if (number > 20) {\n      bool = true;\n    } else {\n      bool = false;\n    }\n  }\n\n  return number;\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e6e", + "creator": "Darin Dimitrov", + "createdAt": 1254859796000, + "text": "
var randomnumber = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;\n
\n", + "upvotes": 633, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929d3", + "creator": "Ismael Miguel", + "createdAt": 1425186338000, + "text": "I know this is a VERY old answer, but using (Math.random() * (maximum - minimum + 1) ) << 0 is faster.", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e70", + "creator": "Codler", + "createdAt": 1360439466000, + "text": "
function randomRange(min, max) {\n  return ~~(Math.random() * (max - min + 1)) + min\n}\n
\n\n

Alternative if you are using Underscore.js you can use

\n\n
_.random(min, max)\n
\n", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929d5", + "creator": "originalhat", + "createdAt": 1433171419000, + "text": "Underscore actually provides a _.uniqueId() function you can call for client side models.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e71", + "creator": "Learner.js", + "createdAt": 1374472600000, + "text": "

To get a random number say between 1 and 6, first do:

\n
0.5 + (Math.random() * ((6 - 1) + 1))\n
\n

This multiplies a random number by 6 and then adds 0.5 to it. Next round the number to a positive integer by doing:

\n
Math.round(0.5 + (Math.random() * ((6 - 1) + 1))\n
\n

This round the number to the nearest whole number.

\n

Or to make it more understandable do this:

\n
var value = 0.5 + (Math.random() * ((6 - 1) + 1))\nvar roll = Math.round(value);\nreturn roll;\n
\n

In general, the code for doing this using variables is:

\n
var value = (Min - 0.5) + (Math.random() * ((Max - Min) + 1))\nvar roll = Math.round(value);\nreturn roll;\n
\n

The reason for taking away 0.5 from the minimum value is because using the minimum value alone would allow you to get an integer that was one more than your maximum value. By taking away 0.5 from the minimum value you are essentially preventing the maximum value from being rounded up.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929d8", + "creator": "ILMostro_7", + "createdAt": 1452099151000, + "text": "Make sense if you're excluding 0, then no need to "round down" range from 0 to 0.5.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e72", + "creator": "brooklynsweb", + "createdAt": 1380501864000, + "text": "

Random whole number between lowest and highest:

\n
function randomRange(low, high) {\n  var range = (high-low);\n  var random = Math.floor(Math.random()*range);\n  if (random === 0) {\n    random += 1;\n  }\n  return low + random;\n}\n
\n

It is not the most elegant solution, but something quick.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929db", + "creator": "Peter Mortensen", + "createdAt": 1651230041000, + "text": "Considering the special case (if), what can be said about the distribution of the random numbers?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e73", + "creator": "Prasobh.Kollattu", + "createdAt": 1396006285000, + "text": "

Return a random number between 1 and 10:

\n\n
Math.floor((Math.random()*10) + 1); \n
\n\n

Return a random number between 1 and 100:

\n\n
Math.floor((Math.random()*100) + 1)\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929dd", + "creator": "evandrix", + "createdAt": 1448593103000, + "text": "is your "between" inclusive or exclusive? i.e. is it [1,10], [1,10), (1,10], or (1,10)?", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929df", + "creator": "Ivan Z", + "createdAt": 1452078884000, + "text": "It is partialy inclusive: [1, *)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929e1", + "creator": "Shachi", + "createdAt": 1507640994000, + "text": "what is the need of + 1 at the end of the function? It works perfectly I guess.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929e3", + "creator": "Peter Mortensen", + "createdAt": 1651238496000, + "text": "@Shachi: It is the lower bound (a badly chosen example). 4, as in the question, would be better.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929e4", + "creator": "Peter Mortensen", + "createdAt": 1651238586000, + "text": "1 is too special. This will break down for other numbers, e.g., 4 and 8 as in the question (the range will (approximately) be [4;12], not [4;8]).", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e74", + "creator": "Starkers", + "createdAt": 1405960935000, + "text": "

The other answers don't account for the perfectly reasonable parameters of 0 and 1. Instead you should use the round instead of ceil or floor:

\n\n
function randomNumber(minimum, maximum){\n    return Math.round( Math.random() * (maximum - minimum) + minimum);\n}\n\nconsole.log(randomNumber(0,1));  # 0 1 1 0 1 0\nconsole.log(randomNumber(5,6));  # 5 6 6 5 5 6\nconsole.log(randomNumber(3,-1)); # 1 3 1 -1 -1 -1\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929e6", + "creator": "Peter Mortensen", + "createdAt": 1651230584000, + "text": "The last example could be considered an empty range. For example, invalid input parameters. With an empty result, an error thrown, or similar.", + "upvotes": 316, + "upvoterUsernames": [], + "downvotes": 316, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e75", + "creator": "Travis", + "createdAt": 1420342756000, + "text": "

Here's what I use to generate random numbers.

\n
function random(min,max) {\n    return Math.floor((Math.random())*(max-min+1))+min;\n}\n
\n

Math.random() returns a number between 0 (inclusive) and 1 (exclusive). We multiply this number by the range (max-min). This results in a number between 0 (inclusive), and the range.

\n

For example, take random(2,5)0. We multiply the random number 0≤x<1 by the range (5-2=3), so we now have a number, x where 0≤x<3.

\n

In order to force the function to treat both the max and min as inclusive, we add 1 to our range calculation: Math.random()*(max-min+1). Now, we multiply the random number by the (5-2+1=4), resulting in an number, x, such that 0≤x<4. If we floor this calculation, we get an integer: 0≤x≤3, with an equal likelihood of each result (1/4).

\n

Finally, we need to convert this into an integer between the requested values. Since we already have an integer between 0 and the (max-min), we can simply map the value into the correct range by adding the minimum value. In our example, we add 2 our integer between 0 and 3, resulting in an integer between 2 and 5.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dc082fcc3049e929e8", + "creator": "Peter Mortensen", + "createdAt": 1651230685000, + "text": "random(2,5)0 doesn't appear to be valid.", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + }, + { + "_id": "62f328dc082fcc3049e929e9", + "creator": "Travis", + "createdAt": 1656356780000, + "text": "@PeterMortensen Syntax Error. random(2,5) works fine. What is the 0 for?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e77", + "creator": "Yusuf", + "createdAt": 1440002619000, + "text": "

Using the following code, you can generate an array of random numbers, without repeating, in a given range.

\n
function genRandomNumber(how_many_numbers, min, max) {\n\n    // Parameters\n    //\n    //   how_many_numbers: How many numbers you want to\n    //                     generate. For example, it is 5.\n    //\n    //   min (inclusive):  Minimum/low value of a range. It\n    //                     must be any positive integer, but\n    //                     less than max. I.e., 4.\n    //\n    //   max (inclusive):  Maximum value of a range. it must\n    //                     be any positive integer. I.e., 50\n    //\n    //   Return type: array\n\n    var random_number = [];\n    for (var i = 0; i < how_many_numbers; i++) {\n        var gen_num = parseInt((Math.random() * (max-min+1)) + min);\n        do {\n            var is_exist = random_number.indexOf(gen_num);\n            if (is_exist >= 0) {\n                gen_num = parseInt((Math.random() * (max-min+1)) + min);\n            }\n            else {\n                random_number.push(gen_num);\n                is_exist = -2;\n            }\n        }\n        while (is_exist > -1);\n    }\n    document.getElementById('box').innerHTML = random_number;\n}\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e76", + "creator": "Lior Elrom", + "createdAt": 1427248714000, + "text": "

Math.random()

\n

Returns an integer random number between min (included) and max (included):

\n
function randomInteger(min, max) {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n
\n

Or any random number between min (included) and max (not included):

\n
function randomNumber(min, max) {\n  return Math.random() * (max - min) + min;\n}\n
\n

Useful examples (integers):

\n
// 0 -> 10\nMath.floor(Math.random() * 11);\n\n// 1 -> 10\nMath.floor(Math.random() * 10) + 1;\n\n// 5 -> 20\nMath.floor(Math.random() * 16) + 5;\n\n// -10 -> (-2)\nMath.floor(Math.random() * 9) - 10;\n
\n

** And always nice to be reminded (Mozilla):

\n
\n

Math.random() does not provide cryptographically secure random\nnumbers. Do not use them for anything related to security. Use the Web\nCrypto API instead, and more precisely the\nwindow.crypto.getRandomValues() method.

\n
\n", + "upvotes": 289, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929eb", + "creator": "Alexander Farber", + "createdAt": 1605276779000, + "text": "If you take Math.ceil, then +1 can be spared", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e78", + "creator": "Goran B.", + "createdAt": 1444194326000, + "text": "

This is my take on a random number in a range, as in I wanted to get a random number within a range of base to exponent. E.g., base = 10, exponent = 2, gives a random number from 0 to 100, ideally, and so on.

\n

If it helps using it, here it is:

\n
// Get random number within provided base + exponent\n// By Goran Biljetina --> 2012\n\nfunction isEmpty(value) {\n    return (typeof value === "undefined" || value === null);\n}\n\nvar numSeq = new Array();\n\nfunction add(num, seq) {\n    var toAdd = new Object();\n    toAdd.num = num;\n    toAdd.seq = seq;\n    numSeq[numSeq.length] = toAdd;\n}\n\nfunction fillNumSeq (num, seq) {\n    var n;\n    for(i=0; i<=seq; i++) {\n        n = Math.pow(num, i);\n        add(n, i);\n    }\n}\n\nfunction getRandNum(base, exp) {\n    if (isEmpty(base)) {\n        console.log("Specify value for base parameter");\n    }\n    if (isEmpty(exp)) {\n        console.log("Specify value for exponent parameter");\n    }\n\n    fillNumSeq(base, exp);\n\n    var emax;\n    var eseq;\n    var nseed;\n    var nspan;\n    emax = (numSeq.length);\n    eseq = Math.floor(Math.random()*emax) + 1;\n    nseed = numSeq[eseq].num;\n    nspan = Math.floor((Math.random())*(Math.random()*nseed)) + 1;\n    return Math.floor(Math.random()*nspan) + 1;\n}\n\nconsole.log(getRandNum(10, 20), numSeq);\n\n//Testing:\n//getRandNum(-10, 20);\n//console.log(getRandNum(-10, 20), numSeq);\n//console.log(numSeq);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e79", + "creator": "happy", + "createdAt": 1449964060000, + "text": "

Use:

\n
<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset="utf-8" />\n    </head>\n\n    <body>\n        <script>\n            /*\n                Assuming that window.crypto.getRandomValues\n                is available, the real range would be from\n                0 to 1,998 instead of 0 to 2,000.\n\n                See the JavaScript documentation\n                for an explanation:\n\n                  https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues\n            */\n            var array = new Uint8Array(2);\n            window.crypto.getRandomValues(array);\n            console.log(array[0] + array[1]);\n        </script>\n    </body>\n</html>\n
\n

Uint8Array creates an array filled with a number up to three digits which would be a maximum of 999. This code is very short.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929ee", + "creator": "Peter Mortensen", + "createdAt": 1651233085000, + "text": "(The syntax highlighting of "Uint8Array" is truly weird.)", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7a", + "creator": "NutCracker", + "createdAt": 1461513117000, + "text": "

I found this simple method on W3Schools:

\n
Math.floor((Math.random() * max) + min);\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929f0", + "creator": "madprops", + "createdAt": 1493427020000, + "text": "Math.floor((Math.random() * 1) + 0); always gives 0", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328dd082fcc3049e929f1", + "creator": "NutCracker", + "createdAt": 1493452121000, + "text": "@madprops Because max number is exclusive. To get 0 or 1, you should set 2 as max number.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328dd082fcc3049e929f3", + "creator": "Pietro Coelho", + "createdAt": 1507854537000, + "text": "or you just add + 1 in the function that calls this method", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7b", + "creator": "Nilesh Pawar", + "createdAt": 1502694936000, + "text": "

Here is an example of a JavaScript function that can generate a random number of any specified length without using Math.random():

\n
function genRandom(length)\n{\n  const t1 = new Date().getMilliseconds();\n  var min = "1", max = "9";\n  var result;\n  var numLength = length;\n  if (numLength != 0)\n  {\n     for (var i = 1; i < numLength; i++)\n     {\n        min = min.toString() + "0";\n        max = max.toString() + "9";\n     }\n  }\n  else\n  {\n     min = 0;\n     max = 0;\n     return;\n  }\n\n  for (var i = min; i <= max; i++)\n  {\n       // Empty Loop\n  }\n\n  const t2 = new Date().getMilliseconds();\n  console.log(t2);\n  result = ((max - min)*t1)/t2;\n  console.log(result);\n  return result;\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929f5", + "creator": "Sunil B N", + "createdAt": 1520241995000, + "text": "check the jsbin url.. you will see the output yourself", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7d", + "creator": "Prakhar Mittal", + "createdAt": 1528438183000, + "text": "

A function called randUpTo that accepts a number and returns a random whole number between 0 and that number:

\n
var randUpTo = function(num) {\n    return Math.floor(Math.random() * (num - 1) + 0);\n};\n
\n

A function called randBetween that accepts two numbers representing a range and returns a random whole number between those two numbers:

\n
var randBetween = function (min, max) {\n    return Math.floor(Math.random() * (max - min - 1)) + min;\n};\n
\n

A function called randFromTill that accepts two numbers representing a range and returns a random number between min (inclusive) and max (exclusive)

\n
var randFromTill = function (min, max) {\n    return Math.random() * (max - min) + min;\n};\n
\n

A function called randFromTo that accepts two numbers representing a range and returns a random integer between min (inclusive) and max (inclusive):

\n
var randFromTo = function (min, max) {\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929f8", + "creator": "650aa6a2", + "createdAt": 1529787709000, + "text": "Beautiful, could you also note that randBetween is (exclusive) (exclusive)?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7c", + "creator": "Stanislav Vincent", + "createdAt": 1506933049000, + "text": "

If you need a variable between 0 and max, you can use:

\n
Math.floor(Math.random() *  max);\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929fb", + "creator": "Tree", + "createdAt": 1535483878000, + "text": "Is max inclusive or exclusive>?", + "upvotes": 1267, + "upvoterUsernames": [], + "downvotes": 1267, + "downvoterUsernames": [] + }, + { + "_id": "62f328dd082fcc3049e929fd", + "creator": "Luke", + "createdAt": 1554718789000, + "text": "@Tree using Math.floor max is exclusive. If you want max to be inclusive you could use Math.round.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7e", + "creator": "Aylian Craspa", + "createdAt": 1534394550000, + "text": "

Use this function to get random numbers in a given range:

\n
function rnd(min, max) {\n    return Math.floor(Math.random()*(max - min + 1) + min);\n}\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328dd082fcc3049e929ff", + "creator": "Peter Mortensen", + "createdAt": 1651233919000, + "text": "How is this different from previous answers?", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e7f", + "creator": "madprops", + "createdAt": 1555102963000, + "text": "

I made this function which takes into account options like min, max, exclude (a list of ints to exclude), and seed (in case you want a seeded random generator).

\n\n
get_random_int = function(args={})\n{\n    let def_args =\n    {\n        min: 0,\n        max: 1,\n        exclude: false,\n        seed: Math.random\n    }\n\n    args = Object.assign(def_args, args)\n\n    let num = Math.floor(args.seed() * (args.max - args.min + 1) + args.min)\n\n    if(args.exclude)\n    {\n        let diff = args.max - args.min\n        let n = num\n\n        for(let i=0; i<diff*2; i++)\n        {\n            if(args.exclude.includes(n))\n            {\n                if(n + 1 <= args.max)\n                {\n                    n += 1\n                }\n\n                else\n                {\n                    n = args.min\n                }\n            }\n\n            else\n            {\n                num = n\n                break\n            }\n        }\n    }\n\n    return num\n}\n
\n\n

It can be used like:

\n\n
let n = get_random_int\n(\n    {\n        min: 0,\n        max: some_list.length - 1,\n        exclude: [3, 6, 5],\n        seed: my_seed_function\n    }\n)\n
\n\n

Or more simply:

\n\n
let n = get_random_int\n(\n    {\n        min: 0,\n        max: some_list.length - 1\n    }\n)\n
\n\n

Then you can do:

\n\n
let item = some_list[n]\n
\n\n

Gist: https://gist.github.com/madprops/757deb000bdec25776d5036dae58ee6e

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e80", + "creator": "Achintha Isuru", + "createdAt": 1557429475000, + "text": "

You can you this code snippet,

\n
let randomNumber = function(first, second) {\n    let number = Math.floor(Math.random()*Math.floor(second));\n    while(number < first) {\n\n        number = Math.floor(Math.random()*Math.floor(second));\n    }\n    return number;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a01", + "creator": "Javi Marzán", + "createdAt": 1574945706000, + "text": "There is an unnecessary duplicated line here. Just use do - while instead of while", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e81", + "creator": "Normajean", + "createdAt": 1567456106000, + "text": "\n

So to generate a random integer between 4 and 8 inclusive, call the above function with the following arguments:

\n
generateRandomInteger(4, 9)\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a04", + "creator": "Peter Mortensen", + "createdAt": 1651234455000, + "text": "How is this different from previous answers?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e82", + "creator": "Kamil Kiełczewski", + "createdAt": 1568628589000, + "text": "

Cryptographically strong

\n

To get a cryptographically strong random integer number in the range [x,y], try:

\n

\r\n
\r\n
let cs = (x,y) => x + (y - x + 1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32 | 0\n\nconsole.log(cs(4, 8))
\r\n
\r\n
\r\n

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a05", + "creator": "Akin Hwan", + "createdAt": 1572234623000, + "text": "this one is fascinating", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328de082fcc3049e92a07", + "creator": "user10294268", + "createdAt": 1593870011000, + "text": "I'd recommend this", + "upvotes": 290, + "upvoterUsernames": [], + "downvotes": 290, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e83", + "creator": "Dani Amsalem", + "createdAt": 1570400877000, + "text": "

Ionuț G. Stan wrote a great answer, but it was a bit too complex for me to grasp. So, I found an even simpler explanation of the same concepts at Math.floor( Math.random () * (max - min + 1)) + min) Explanation by Jason Anello.

\n

Note: The only important thing you should know before reading Jason's explanation is a definition of "truncate". He uses that term when describing Math.floor(). Oxford dictionary defines "truncate" as:

\n
\n

Shorten (something) by cutting off the top or end.

\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e84", + "creator": "Nitin Jadhav", + "createdAt": 1572497362000, + "text": "

My method of generating a random number between 0 and n, where n <= 10 (n excluded):

\n
Math.floor((Math.random() * 10) % n)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a0a", + "creator": "Peter Mortensen", + "createdAt": 1650292123000, + "text": "But the question was "in a specific range".", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e86", + "creator": "Aaron Plocharczyk", + "createdAt": 1579778987000, + "text": "

Problems with the accepted answer

\n

It's worth noting that the accepted answer does not properly handle cases where min is greater than max. Here's an example of that:

\n

\r\n
\r\n
min = Math.ceil(2);\nmax = Math.floor(1);\nfor(var i = 0; i < 25; i++) {\n  console.log(Math.floor(Math.random() * (max - min + 1)) + min);\n}
\r\n
\r\n
\r\n

\n

In addition, it's a bit wordy and unclear to read if you're unfamiliar with this little algorithm.

\n

Why is Randojs a better solution?

\n

Randojs handles cases where min is greater than max automatically (and it's cryptographically secure):

\n

\r\n
\r\n
for(var i = 0; i < 25; i++) console.log(rando(2, 1));
\r\n
<script src=\"https://randojs.com/1.0.0.js\"></script>
\r\n
\r\n
\r\n

\n

It also handles negatives, zeros, and everything else you'd expect. If you need to do floats or use other variable types, there are options for that as well, but I won't talk about them here. They're on the site so you can delve deeper there if needed. The final reason is pretty obvious. Stylistically, it's is much cleaner and easier to read.

\n
\n

TL;DR. Just give me the solution...

\n

randojs.com makes this and a ton of other common randomness stuff robust, reliable, as simple/readable as this:

\n

\r\n
\r\n
console.log(rando(20, 30));
\r\n
<script src=\"https://randojs.com/1.0.0.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a0c", + "creator": "Max Carroll", + "createdAt": 1603402900000, + "text": "They also have an npm package I believe", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e85", + "creator": "Koby Douek", + "createdAt": 1579767166000, + "text": "

For best performance, you can simply use:

\n\n
var r = (Math.random() * (maximum - minimum + 1) ) << 0\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a0f", + "creator": "Koby Douek", + "createdAt": 1651241872000, + "text": "@PeterMortensen That's what I did.. 2 variables called maximum and minimum", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e88", + "creator": "Shams Ansari", + "createdAt": 1589026568000, + "text": "

All these solutions are using way too much firepower. You only need to call one function: Math.random();

\n
Math.random() * max | 0;\n
\n

This returns a random integer between 0 (inclusive) and max (non-inclusive).

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328de082fcc3049e92a11", + "creator": "Moritz Schmidt", + "createdAt": 1612755205000, + "text": "This is so clean. Thanks!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f328de082fcc3049e92a13", + "creator": "avalanche1", + "createdAt": 1642023632000, + "text": "The OP was asking about a RANGE between 4 & 8, not 8 and 0", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328de082fcc3049e92a14", + "creator": "avalanche1", + "createdAt": 1642153670000, + "text": "Then it doesn't work. Math.random() * 10 | 5 outputs only 5 | 7 | 13", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328de082fcc3049e92a15", + "creator": "Nishant Ghodke", + "createdAt": 1642411797000, + "text": "Beware: This answer is not reliable. Max: 5 & Min: 1 returns: 1, 3, 5.", + "upvotes": 577, + "upvoterUsernames": [], + "downvotes": 577, + "downvoterUsernames": [] + }, + { + "_id": "62f328de082fcc3049e92a17", + "creator": "Peter Mortensen", + "createdAt": 1651236774000, + "text": "@Moritz Schmidt: It may be clean, but it doesn't answer the question.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e8b", + "creator": "NewBieCoder", + "createdAt": 1616241730000, + "text": "

I wanted to explain using an example:

\n

Function to generate random whole numbers in JavaScript within a range of 5 to 25

\n
\n

General Overview:

\n

(i) First convert it to the range - starting from 0.

\n

(ii) Then convert it to your desired range ( which then will be very\neasy to complete).

\n
\n

So basically, if you want to generate random whole numbers from 5 to 25 then:

\n

First step: Converting it to range - starting from 0

\n

Subtract "lower/minimum number" from both "max" and "min". i.e

\n

(5-5) - (25-5)

\n

So the range will be:

\n

0-20 ...right?

\n

Step two

\n

Now if you want both numbers inclusive in range - i.e "both 0 and 20", the equation will be:

\n

Mathematical equation: Math.floor((Math.random() * 21))

\n

General equation: Math.floor((Math.random() * (max-min +1)))

\n

Now if we add subtracted/minimum number (i.e., 5) to the range - then automatically we can get range from 0 to 20 => 5 to 25

\n

Step three

\n

Now add the difference you subtracted in equation (i.e., 5) and add "Math.floor" to the whole equation:

\n

Mathematical equation: Math.floor((Math.random() * 21) + 5)

\n

General equation: Math.floor((Math.random() * (max-min +1)) + min)

\n

So finally the function will be:

\n
function randomRange(min, max) {\n   return Math.floor((Math.random() * (max - min + 1)) + min);\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e8a", + "creator": "Vince", + "createdAt": 1612158820000, + "text": "

This implementation works when both inputs are integers.

\n
function randomRange(myMin, myMax) {\n  return Math.floor(\n    Math.random() * (Math.ceil(myMax) - Math.floor(myMin) + 1) + myMin\n  );\n}\n
\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e8c", + "creator": "Mr Ernest", + "createdAt": 1616747062000, + "text": "

This I guess, is the most simplified of all the contributions.

\n
maxNum = 8,\nminNum = 4\n\nconsole.log(Math.floor(Math.random() * (maxNum - minNum) + minNum))\n\nconsole.log(Math.floor(Math.random() * (8 - 4) + 4))\n
\n

This will log random numbers between 4 and 8 into the console, 4 and 8 inclusive.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e8d", + "creator": "Nirvana", + "createdAt": 1617262189000, + "text": "

Here's something I found on a webpage:

\n
function randomInt(e,t){return Math.floor(Math.random()*(t-e+1)+e)}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e8e", + "creator": "epix", + "createdAt": 1644053954000, + "text": "

Here is a function that generates a random number between min and max, both inclusive.

\n
const randomInt = (max, min) => Math.round(Math.random() * (max - min)) + min;\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291b082fcc3049e92a1d", + "creator": "Peter Mortensen", + "createdAt": 1650292278000, + "text": "How is that different from the previous answers? Does it work?", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f3291b082fcc3049e92a1e", + "creator": "epix", + "createdAt": 1650620914000, + "text": "Yes, it works with both min and max being inclusive in the range using Math.round function.", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cc082fcc3049e90e89", + "creator": "mattias", + "createdAt": 1611154668000, + "text": "

Using modern JavaScript + Lodash:

\n
const generateRandomNumbers = (max, amount) => {\n  const numbers = [...Array(max).keys()];\n  const randomNumbers = sampleSize(numbers, amount);\n\n  return randomNumbers.sort((a, b) => a - b);\n};\n
\n

Also, a TypeScript version:

\n
const generateRandomNumbers = (max: number, amount: number) => {\n  const numbers = [...Array(max).keys()];\n  const randomNumbers: number[] = sampleSize(numbers, amount);\n\n  return randomNumbers.sort((a: number, b: number) => a - b);\n};\n
\n", + "upvotes": 3111, + "upvoterUsernames": [], + "downvotes": 3111, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cc082fcc3049e90e87", + "creator": "samin ", + "createdAt": 1579948052000, + "text": "
// Example\nfunction ourRandomRange(ourMin, ourMax) {\n    return Math.floor(Math.random() * (ourMax - ourMin + 1)) + ourMin;\n}\n\nourRandomRange(1, 9);\n\n// Only change code below this line.\nfunction randomRange(myMin, myMax) {\n    var a = Math.floor(Math.random() * (myMax - myMin + 1)) + myMin;\n    return a; // Change this line\n}\n\n// Change these values to test your function\nvar myRandom = randomRange(5, 15);\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321cb082fcc3049e90e49", + "creator": "Dan K.K.", + "createdAt": 1384789006000, + "text": "here is a useful gist: gist.github.com/kerimdzhanov/7529623", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1764699, + "uvac": 1764734 + } + }, + { + "_id": "62f321bb082fcc3049e8fee6", + "title": "How do I get the current date in JavaScript?", + "title-lowercase": "how do i get the current date in javascript?", + "creator": "Suresh", + "createdAt": 1254915542000, + "status": "open", + "text": "

How do I get the current date in JavaScript?

\n", + "upvotes": 3456, + "upvoterUsernames": [], + "downvotes": 619, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3755068, + "answers": 57, + "answerItems": [ + { + "_id": "62f321c7082fcc3049e90a56", + "creator": "Samuel Meddows", + "createdAt": 1297140179000, + "text": "

Use new Date() to generate a new Date object containing the current date and time.

\n\n

\r\n
\r\n
var today = new Date();\r\nvar dd = String(today.getDate()).padStart(2, '0');\r\nvar mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!\r\nvar yyyy = today.getFullYear();\r\n\r\ntoday = mm + '/' + dd + '/' + yyyy;\r\ndocument.write(today);
\r\n
\r\n
\r\n

\n\n

This will give you today's date in the format of mm/dd/yyyy.

\n\n

Simply change today = mm +'/'+ dd +'/'+ yyyy; to whatever format you wish.

\n", + "upvotes": 3596, + "upvoterUsernames": [], + "downvotes": 313, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32712082fcc3049e9230c", + "creator": "nnnnnn", + "createdAt": 1398292601000, + "text": "@MounaCheikhna - How could we be in the year 999?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e9230d", + "creator": "Mark Micallef", + "createdAt": 1402459894000, + "text": "Swap around the month and date if you're not in north America.", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e9230f", + "creator": "Niko Bellic", + "createdAt": 1411057907000, + "text": "@MounaCheikhna why are you adding 1900? If we were in the year 999, this would make it 2899 (999 + 1900)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92311", + "creator": "cee", + "createdAt": 1460408053000, + "text": "Today I found a pretty neat trick. ('0' + today.getDate()).slice(-2) This returns the current date always as 2 digits.", + "upvotes": 1792, + "upvoterUsernames": [], + "downvotes": 1792, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92313", + "creator": "Sakshi Nagpal", + "createdAt": 1529862270000, + "text": "date.toLocaleDateString('en-GB')", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92315", + "creator": "Aprillion", + "createdAt": 1565014254000, + "text": "after the edit from March 2019, this answer is using ES2017 features (padStart) that will NOT work in Internet Explorer (any version)", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32712082fcc3049e92317", + "creator": "DSLuminary", + "createdAt": 1581374470000, + "text": "@Aprillion just use polyfills such as corejs", + "upvotes": 4132, + "upvoterUsernames": [], + "downvotes": 4132, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a57", + "creator": "Jimmy M", + "createdAt": 1329295682000, + "text": "

Try this:

\n\n

\r\n
\r\n
var currentDate = new Date()\r\nvar day = currentDate.getDate()\r\nvar month = currentDate.getMonth() + 1\r\nvar year = currentDate.getFullYear()\r\ndocument.write(\"<b>\" + day + \"/\" + month + \"/\" + year + \"</b>\")
\r\n
\r\n
\r\n

\n\n

The result will be like

\n\n
15/2/2012\n
\n", + "upvotes": 211, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a58", + "creator": "eomeroff", + "createdAt": 1340715615000, + "text": "

You can use Date.js library which extens Date object, thus you can have .today() method.

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e9231b", + "creator": "Peter Munnings", + "createdAt": 1351059078000, + "text": "if you are using jquery ui with a datepicker, you can use $.datepicker.formatDate('yy/mm/dd', new Date())", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a59", + "creator": "benjamin.keen", + "createdAt": 1355274208000, + "text": "

If you're looking for a lot more granular control over the date formats, I thoroughly recommend checking out momentjs. Terrific library - and only 5KB.\nhttp://momentjs.com/

\n", + "upvotes": 109, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e9231d", + "creator": "Risadinha", + "createdAt": 1377592410000, + "text": "Supports localization like a charm.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e9231f", + "creator": "Freewalker", + "createdAt": 1496784100000, + "text": "These days we use date-fns - it treats dates as Immutable (Moment mutates dates), is faster and is modular (just import what you need).", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a5d", + "creator": "roshan", + "createdAt": 1383501796000, + "text": "

This works every time:

\n\n

\r\n
\r\n
    var now = new Date();\r\n    var day = (\"0\" + now.getDate()).slice(-2);\r\n    var month = (\"0\" + (now.getMonth() + 1)).slice(-2);\r\n    var today = now.getFullYear() + \"-\" + (month) + \"-\" + (day);\r\n    \r\n    console.log(today);
\r\n
\r\n
\r\n

\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a5a", + "creator": "Roger", + "createdAt": 1360325267000, + "text": "

You can use this

\n\n
<script>\nfunction my_curr_date() {      \n    var currentDate = new Date()\n    var day = currentDate.getDate();\n    var month = currentDate.getMonth() + 1;\n    var year = currentDate.getFullYear();\n    var my_date = month+\"-\"+day+\"-\"+year;\n    document.getElementById(\"dateField\").value=my_date;    \n}\n</script>\n
\n\n

The HTML is

\n\n
<body onload='return my_curr_date();'>\n    <input type='text' name='dateField' id='dateField' value='' />\n</body>\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a5b", + "creator": "Rishabh Marya", + "createdAt": 1372458758000, + "text": "

\r\n
\r\n
var d = (new Date()).toString().split(' ').splice(1,3).join(' ');\r\n\r\ndocument.write(d)
\r\n
\r\n
\r\n

\n\n

To break it down into steps:

\n\n
    \n
  1. (new Date()).toString() gives \"Fri Jun 28 2013 15:30:18 GMT-0700 (PDT)\"

  2. \n
  3. (new Date()).toString().split(' ') divides the above string on each space and returns an array as follows: [\"Fri\", \"Jun\", \"28\", \"2013\", \"15:31:14\", \"GMT-0700\", \"(PDT)\"]

  4. \n
  5. (new Date()).toString().split(' ').splice(1,3).join(' ') takes the second, third and fourth values from the above array, joins them with spaces, and returns a string \"Jun 28 2013\"

  6. \n
\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e92323", + "creator": "panhandel", + "createdAt": 1379484965000, + "text": "I needed a time in 00:00:00 and didn't want to rebuild it manually; step 2 gets me there perfectly. Kudos!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92325", + "creator": "hyde", + "createdAt": 1425332524000, + "text": "You can save some bytes by doing this: Date().split(' ').splice(1,3).join(' ')", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a5e", + "creator": "Andrea", + "createdAt": 1398158650000, + "text": "
(function() { var d = new Date(); return new Date(d - d % 86400000); })()\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e92328", + "creator": "drusepth", + "createdAt": 1428003351000, + "text": "Where did 86400000 come from?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e9232a", + "creator": "mickmackusa", + "createdAt": 1588591029000, + "text": "The point is: There should be an explanation with every answer (in the answer) on Stack Overflow.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a5c", + "creator": "Varun Natraaj", + "createdAt": 1380463993000, + "text": "

\r\n
\r\n
var utc = new Date().toJSON().slice(0,10).replace(/-/g,'/');\r\ndocument.write(utc);
\r\n
\r\n
\r\n

\n\n

Use the replace option if you're going to reuse the utc variable, such as new Date(utc), as Firefox and Safari don't recognize a date with dashes.

\n", + "upvotes": 841, + "upvoterUsernames": [], + "downvotes": 327, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e9232c", + "creator": "Varun Natraaj", + "createdAt": 1392201624000, + "text": "I dont think so :) Seems pretty straightforward!", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e9232e", + "creator": "Andy N", + "createdAt": 1392534626000, + "text": "toJSON() returns as utc datetime", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92330", + "creator": "Varun Natraaj", + "createdAt": 1392714537000, + "text": "It returns a JSON datetime. toUTCString() returns as utc datetime.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92331", + "creator": "user3374348", + "createdAt": 1432649649000, + "text": "In that case you can use moment().startOf("day")", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92333", + "creator": "Spacemancraig", + "createdAt": 1445534664000, + "text": "Works but I had enclose (new Date()).toJSON().slice(0,10)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92335", + "creator": "Álvaro González", + "createdAt": 1450805989000, + "text": ".toJSON() includes time zone information; .slice() strips it.", + "upvotes": 4684, + "upvoterUsernames": [], + "downvotes": 4684, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92336", + "creator": "Varun Natraaj", + "createdAt": 1452194494000, + "text": ".toJSON() gives the current datetime in UTC. Since the question was with respect to current date alone, slice answers it.", + "upvotes": 90, + "upvoterUsernames": [], + "downvotes": 90, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92337", + "creator": "ptomato", + "createdAt": 1594424770000, + "text": "This will start failing in the year 10000.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92339", + "creator": "elon", + "createdAt": 1596280666000, + "text": "This does not answer ISO. So it's not a full answer..", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a5f", + "creator": "Dunaevsky Maxim", + "createdAt": 1404889298000, + "text": "
var date = new Date().toLocaleDateString("en-US");\n
\n

Also, you can call method toLocaleDateString with two parameters:

\n
var date = new Date().toLocaleDateString("en-US", {\n    "year": "numeric",\n    "month": "numeric"\n});\n
\n

More about this method on MDN.

\n", + "upvotes": 100, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e9233b", + "creator": "chris", + "createdAt": 1461341074000, + "text": "Nice, works on Chrome. Unfortunately doesn't work on PhantomJS as of 22/4/2016", + "upvotes": 298, + "upvoterUsernames": [], + "downvotes": 298, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e9233d", + "creator": "user3772108", + "createdAt": 1557150002000, + "text": "cool solution. should be on the top. new Date().toLocaleDateString("de-de") does the trick for me.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a60", + "creator": "Morad", + "createdAt": 1408190172000, + "text": "

You can use moment.js: http://momentjs.com/

\n\n

\r\n
\r\n
var m = moment().format(\"DD/MM/YYYY\");\r\n\r\ndocument.write(m);
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.14.1/moment.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32713082fcc3049e92340", + "creator": "Dan Dascalescu", + "createdAt": 1414408516000, + "text": "Moment is overkill for just getting the current date.", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92341", + "creator": "Dunc", + "createdAt": 1492527411000, + "text": "Or moment().format("L") to respect the current locale.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92343", + "creator": "Kind Contributor", + "createdAt": 1529243230000, + "text": "@DanDascalescu Actually, the Javascript base specification for DateTime is that bad.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32713082fcc3049e92345", + "creator": "Adil H. Raza", + "createdAt": 1546944276000, + "text": "If you already have momentjs imported in your project then this is the cleanest simplest answer.", + "upvotes": 164, + "upvoterUsernames": [], + "downvotes": 164, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a62", + "creator": "Brock Davis", + "createdAt": 1421706107000, + "text": "

I think this is an old question but the easiest way would be the following:

\n\n
var date = new Date();\nvar TimeStamp = date.toLocaleString();\n\nfunction CurrentTime(){\n  alert(TimeStamp);\n}\n
\n\n

This will grab the current time, pass it to a string based on location and then you can call the function CurrentTime to display the time. This would be, to me, the most effective way to get a time stamp for something.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32714082fcc3049e92348", + "creator": "Vlad Bezden", + "createdAt": 1523046342000, + "text": "This will return data+time, eg. '2018-4-6 16:20:22', and the question is how to get date only.", + "upvotes": 1167, + "upvoterUsernames": [], + "downvotes": 1167, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a61", + "creator": "Marshal", + "createdAt": 1409638475000, + "text": "

If you just want a date without time info, use:

\n\n

\r\n
\r\n
var today = new Date();\r\n    today.setHours(0, 0, 0, 0);\r\n\r\ndocument.write(today);
\r\n
\r\n
\r\n

\n", + "upvotes": 213, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32714082fcc3049e9234a", + "creator": "Inrego", + "createdAt": 1443616740000, + "text": "This seems to be the only answer that actually answers the question. Everyone else answers on how to format a date as string.", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32714082fcc3049e9234c", + "creator": "McKay", + "createdAt": 1581725286000, + "text": "This sets hours using UTC, which might not work for all use cases.", + "upvotes": 479, + "upvoterUsernames": [], + "downvotes": 479, + "downvoterUsernames": [] + }, + { + "_id": "62f32714082fcc3049e9234e", + "creator": "JoBaxter", + "createdAt": 1627915373000, + "text": "I also agree. Answers the question perfectly. Thank you.", + "upvotes": 162, + "upvoterUsernames": [], + "downvotes": 162, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a63", + "creator": "Fannon", + "createdAt": 1423580603000, + "text": "

This is my current favorite, because it's both flexible and modular. It's a collection of (at least) three simple functions:

\n\n
/**\n * Returns an array with date / time information\n * Starts with year at index 0 up to index 6 for milliseconds\n * \n * @param {Date} date   date object. If falsy, will take current time.\n * @returns {[]}\n */\ngetDateArray = function(date) {\n    date = date || new Date();\n    return [\n        date.getFullYear(),\n        exports.pad(date.getMonth()+1, 2),\n        exports.pad(date.getDate(), 2),\n        exports.pad(date.getHours(), 2),\n        exports.pad(date.getMinutes(), 2),\n        exports.pad(date.getSeconds(), 2),\n        exports.pad(date.getMilliseconds(), 2)\n    ];\n};\n
\n\n

Here's the pad function:

\n\n
 /**\n * Pad a number with n digits\n *\n * @param {number} number   number to pad\n * @param {number} digits   number of total digits\n * @returns {string}\n */\nexports.pad = function pad(number, digits) {\n    return new Array(Math.max(digits - String(number).length + 1, 0)).join(0) + number;\n};\n
\n\n

Finally I can either build my date string by hand, or use a simple functions to do it for me:

\n\n
/**\n * Returns nicely formatted date-time\n * @example 2015-02-10 16:01:12\n *\n * @param {object} date\n * @returns {string}\n */\nexports.niceDate = function(date) {\n    var d = exports.getDateArray(date);\n    return d[0] + '-' + d[1] + '-' + d[2] + ' ' + d[3] + ':' + d[4] + ':' + d[5];\n};\n\n/**\n * Returns a formatted date-time, optimized for machines\n * @example 2015-02-10_16-00-08\n *\n * @param {object} date\n * @returns {string}\n */\nexports.roboDate = function(date) {\n    var d = exports.getDateArray(date);\n    return d[0] + '-' + d[1] + '-' + d[2] + '_' + d[3] + '-' + d[4] + '-' + d[5];\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a64", + "creator": "Jas", + "createdAt": 1428849397000, + "text": "
var dateTimeToday = new Date();\nvar dateToday = new Date(\n    dateTimeToday.getFullYear(), \n    (dateTimeToday.getMonth() + 1) /*Jan = 0! */, \n    dateTimeToday.getDate(), \n    0, \n    0, \n    0, \n    0);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32714082fcc3049e92352", + "creator": "Artjom B.", + "createdAt": 1428854810000, + "text": "Do we really need another answer like that?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32714082fcc3049e92354", + "creator": "Jas", + "createdAt": 1428862725000, + "text": "I did not find this method in any of the answers so added it", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a65", + "creator": "Jose Rojas", + "createdAt": 1431700802000, + "text": "

You can get the current date call the static method now like this:

\n\n
var now = Date.now()\n
\n\n

reference:

\n\n

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/now

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32714082fcc3049e92356", + "creator": "Perposterer", + "createdAt": 1493411592000, + "text": "This was basically all I needed. var dtToday = new Date(date.now);", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a66", + "creator": "Andrew", + "createdAt": 1433173244000, + "text": "

Pretty Print The Date Like This.

\n\n
\n

June 1st, 2015 11:36:48 AM

\n
\n\n

https://gist.github.com/Gerst20051/7d72693f722bbb0f6b58

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a67", + "creator": "Phil Ricketts", + "createdAt": 1435651836000, + "text": "

If you want a simple DD/MM/YYYY format, I've just come up with this simple solution, although it doesn't prefix missing zeros.

\n\n

\r\n
\r\n
var d = new Date();\r\ndocument.write( [d.getDate(), d.getMonth()+1, d.getFullYear()].join('/') );
\r\n
\r\n
\r\n

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a68", + "creator": "user1338062", + "createdAt": 1439273297000, + "text": "

Varun's answer does not account for TimezoneOffset. Here is a version that does:

\n\n
var d = new Date()\nnew Date(d.getTime() - d.getTimezoneOffset() * 60000).toJSON().slice(0, 10) // 2015-08-11\n
\n\n

The TimezoneOffset is minutes, while the Date constructor takes milliseconds, thus the multiplication by 60000.

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a69", + "creator": "Akhil", + "createdAt": 1439274678000, + "text": "

You can checkout this

\n
var today = new Date();\ntoday = parseInt(today.getMonth()+1)+'/'+today.getDate()+'/'+today.getFullYear()+"\\nTime : "+today.getHours()+":"+today.getMinutes()+":"+today.getSeconds();\ndocument.write(today);\n
\n

And see the documentation for Date() constructor.\nlink

\n

Get Current Date Month Year in React js

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a6a", + "creator": "marverix", + "createdAt": 1443713115000, + "text": "

If by \"current date\" you are thinking about \"today\", then this trick may work for you:

\n\n
> new Date(3600000*Math.floor(Date.now()/3600000))\n2020-05-07T07:00:00.000Z\n
\n\n

This way you are getting today Date instance with time 0:00:00.

\n\n

The principle of operation is very simple: we take the current timestamp and divide it for 1 day expressed in milliseconds. We will get a fraction. By using Math.floor, we get rid of the fraction, so we get an integer. Now if we multiply it back by one day (again - in milliseconds), we get a date timestamp with the time exactly at the beginning of the day.

\n\n
> now = Date.now()\n1588837459929\n> daysInMs = now/3600000\n441343.73886916664\n> justDays = Math.floor(daysInMs)\n441343\n> today = justDays*3600000\n1588834800000\n> new Date(today)\n2020-05-07T07:00:00.000Z\n
\n\n

Clean and simple.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32714082fcc3049e9235c", + "creator": "mickmackusa", + "createdAt": 1588591659000, + "text": "Where is the explanation of how this answer works and why you feel it is good advice?", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a6b", + "creator": "Toucouleur", + "createdAt": 1443716345000, + "text": "
new Date().toISOString().slice(0,10); \n
\n\n

would work too

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a6c", + "creator": "ISONecroMAn", + "createdAt": 1446320367000, + "text": "

What's the big deal with this.. The cleanest way to do this is

\n\n

var currentDate=new Date().toLocaleString().slice(0,10);

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32715082fcc3049e9235f", + "creator": "DevonDahon", + "createdAt": 1520154961000, + "text": "It would return mistakes, like this 3/4/2018, , better to use new Date().toJSON().slice(0,10).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32715082fcc3049e92361", + "creator": "Paul Carlton", + "createdAt": 1522784271000, + "text": "This is perfect for simply getting a date for view or for info on console log or for UI. Better for me without the .slice(0,10)", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a6e", + "creator": "Isayevskiy_Sergey", + "createdAt": 1454501317000, + "text": "
new Date().toDateString();\n
\n\n

Result:

\n\n
\n

\"Wed Feb 03 2016\"

\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a6d", + "creator": "John Slegers", + "createdAt": 1453668091000, + "text": "

The basics

\n\n

If you're happy with the format Sun Jan 24 2016 21:23:07 GMT+0100 (CET), you could just use this code :

\n\n
var today = new Date();\n
\n\n

Date.prototype.toLocaleDateString()

\n\n

If you want to format your output, consider using Date.prototype.toLocaleDateString() :

\n\n
var today = new Date().toLocaleDateString('de-DE', {     \n    weekday: 'long', \n    year: 'numeric',\n    month: 'long',\n    day: 'numeric'\n});\n
\n\n

If you executed that code today (january 24ᵗʰ, 2016) on a modern browser, it would produce the string Sonntag, 24. Januar 2016. Older browsers may generate a different result, though, as eg. IE<11 doesn't support locales or options arguments.

\n\n

Going custom

\n\n

If Date.prototype.toLocaleDateString() isn't flexible enough to fulfill whatever need you may have, you might want to consider creating a custom Date object that looks like this :

\n\n
var DateObject = (function() {\n    var monthNames = [\n      \"January\", \"February\", \"March\",\n      \"April\", \"May\", \"June\", \"July\",\n      \"August\", \"September\", \"October\",\n      \"November\", \"December\"\n    ];\n    var date = function(str) {\n        this.set(str);\n    };\n    date.prototype = {\n        set : function(str) {\n            var dateDef = str ? new Date(str) : new Date();\n            this.day = dateDef.getDate();\n            this.dayPadded = (this.day < 10) ? (\"0\" + this.day) : \"\" + this.day;\n            this.month = dateDef.getMonth() + 1;\n            this.monthPadded = (this.month < 10) ? (\"0\" + this.month) : \"\" + this.month;\n            this.monthName = monthNames[this.month - 1];\n            this.year = dateDef.getFullYear();\n        }\n    };\n    return date;\n})();\n
\n\n

If you included that code and executed new DateObject() today (january 24ᵗʰ, 2016), it would produce an object with the following properties :

\n\n
day: 24\ndayPadded: \"24\"\nmonth: 1\nmonthPadded: \"01\"\nmonthName: \"January\"\nyear: 2016\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a70", + "creator": "Ronnie Royston", + "createdAt": 1462313644000, + "text": "

2.39KB minified. One file. https://github.com/rhroyston/clock-js
\n
\nJust trying to help...\n
\n

\n\n

\"enter

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a6f", + "creator": "Damian Pavlica", + "createdAt": 1457627437000, + "text": "

The shortest possible.

\n\n

To get format like \"2018-08-03\":

\n\n

\r\n
\r\n
let today = new Date().toISOString().slice(0, 10)\r\n\r\nconsole.log(today)
\r\n
\r\n
\r\n

\n\n

To get format like \"8/3/2018\":

\n\n

\r\n
\r\n
let today = new Date().toLocaleDateString()\r\n\r\nconsole.log(today)
\r\n
\r\n
\r\n

\n\n

Also, you can pass locale as argument, for example toLocaleDateString(\"sr\"), etc.

\n", + "upvotes": 561, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32715082fcc3049e92365", + "creator": "LStarky", + "createdAt": 1541557796000, + "text": "This still fails due to time zone shift.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32715082fcc3049e92367", + "creator": "GC_", + "createdAt": 1600177981000, + "text": "Your second answer is the best.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32715082fcc3049e92369", + "creator": "xji", + "createdAt": 1616714408000, + "text": "Note that this returns the day at UTC+0, but not the local day.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a71", + "creator": "Oded Breiner", + "createdAt": 1468862100000, + "text": "

Cleaner, simpler version:

\n
new Date().toLocaleString();\n
\n

Result varies according to the user's locale:

\n
\n

2/27/2017, 9:15:41 AM

\n
\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a72", + "creator": "Jayant Patil", + "createdAt": 1471422790000, + "text": "

This may help you

\n\n
var date = new Date();\nconsole.log(date.getDate()+'/'+(date.getMonth()+1)+'/'+date.getFullYear());\n
\n\n

This will print current date in dd/MM/yyyy format

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a73", + "creator": "Souvik", + "createdAt": 1485949851000, + "text": "

If you are using jQuery. Try this one liner :

\n\n
$.datepicker.formatDate('dd/mm/yy', new Date());\n
\n\n

Here is the convention for formatting the date

\n\n\n\n

Here is the reference for jQuery datepicker

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a74", + "creator": "Blair Anderson", + "createdAt": 1512164604000, + "text": "

The Shortest Answer is: new Date().toJSON().slice(0,10)

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32715082fcc3049e9236e", + "creator": "nirazul", + "createdAt": 1552322671000, + "text": "I was looking for a very simple seed function that changes regularly. This answer is a real treat.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32715082fcc3049e92370", + "creator": "L2_Paver", + "createdAt": 1625205394000, + "text": "don't use this it gets the previous date at 7am", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a75", + "creator": "Aung Htet", + "createdAt": 1520693308000, + "text": "

If you are happy with YYYY-MM-DD format, this will do the job as well.

\n\n

new Date().toISOString().split('T')[0]

\n\n

2018-03-10

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32716082fcc3049e92373", + "creator": "Mika Karjunen", + "createdAt": 1565090970000, + "text": "Since the length is always fixed, we can use substring as well. new Date().toISOString().substring(0, 10); Might be a bit faster as well.", + "upvotes": 1261, + "upvoterUsernames": [], + "downvotes": 1261, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a76", + "creator": "jafarbtech", + "createdAt": 1526628436000, + "text": "

As toISOString() will only return current UTC time , not local time. We have to make a date by using '.toString()' function to get date in yyyy-MM-dd format like

\n\n

\r\n
\r\n
document.write(new Date(new Date().toString().split('GMT')[0]+' UTC').toISOString().split('T')[0]);
\r\n
\r\n
\r\n

\n\n

To get date and time into in yyyy-MM-ddTHH:mm:ss format

\n\n

\r\n
\r\n
document.write(new Date(new Date().toString().split('GMT')[0]+' UTC').toISOString().split('.')[0]);
\r\n
\r\n
\r\n

\n\n

To get date and time into in yyyy-MM-dd HH:mm:ss format

\n\n

\r\n
\r\n
document.write(new Date(new Date().toString().split('GMT')[0]+' UTC').toISOString().split('.')[0].replace('T',' '));
\r\n
\r\n
\r\n

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a77", + "creator": "jyotibisht", + "createdAt": 1541501749000, + "text": "

If you're looking for a lot more granular control over the date formats, I thoroughly recommend checking out date-FNS. Terrific library - much smaller than moment.js and it's function based approach make it much faster then other class based libraries. Provide large number of operations needed over dates.

\n\n

https://date-fns.org/docs/Getting-Started

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a7a", + "creator": "Alex Ivasyuv", + "createdAt": 1557419899000, + "text": "

With ability to render in custom format and using month name in different locales:

\n\n
const locale = 'en-us';\nconst d = new Date(date);\n\nconst day = d.getDate();\nconst month = d.toLocaleString(locale, { month: 'long' });\nconst year = d.getFullYear();\n\nconst time = d.toLocaleString(locale, { hour12: false, hour: 'numeric', minute: 'numeric'});\n\nreturn `${month} ${day}, ${year} @ ${time}`; // May 5, 2019 @ 23:41\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a79", + "creator": "alain.janinm", + "createdAt": 1548260062000, + "text": "

This answer is for people looking for a date with ISO-8601 like format and with the timezone.\nIt's pure JS for those who don't want to include any date library.

\n\n
      var date = new Date();\n      var timeZone = date.toString();\n      //Get timezone ( 'GMT+0200' )\n      var timeZoneIndex = timeZone.indexOf('GMT');\n      //Cut optional string after timezone ( '(heure de Paris)' )\n      var optionalTimeZoneIndex = timeZone.indexOf('(');\n      if(optionalTimeZoneIndex != -1){\n          timeZone = timeZone.substring(timeZoneIndex, optionalTimeZoneIndex);\n      }\n      else{\n          timeZone = timeZone.substring(timeZoneIndex);\n      }\n      //Get date with JSON format ( '2019-01-23T16:28:27.000Z' )\n      var formattedDate = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();\n      //Cut ms\n      formattedDate = formattedDate.substring(0,formattedDate.indexOf('.'));\n      //Add timezone\n      formattedDate = formattedDate + ' ' + timeZone;\n      console.log(formattedDate);\n
\n\n

Print something like this in the console :

\n\n
\n

2019-01-23T17:12:52 GMT+0100

\n
\n\n

JSFiddle : https://jsfiddle.net/n9mszhjc/4/

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a78", + "creator": "Harry Bosh", + "createdAt": 1546907604000, + "text": "

If your looking to format into a string.

\n\n
statusUpdate = \"time \" + new Date(Date.now()).toLocaleTimeString();\n
\n\n

output \"time 11:30:53 AM\"

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32716082fcc3049e9237a", + "creator": "Jeff Lowery", + "createdAt": 1554402258000, + "text": "Why the Date.now()? var x = new Date(Date.now()).toLocaleTimeString(); var y = new Date().toLocaleTimeString(); x === y", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a7b", + "creator": "Yugandhar Chaudhari", + "createdAt": 1562832033000, + "text": "

This is good to get formatted date

\n\n

\r\n
\r\n
let date = new Date().toLocaleDateString(\"en\", {year:\"numeric\", day:\"2-digit\", month:\"2-digit\"});\r\nconsole.log(date);
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a7d", + "creator": "Jerome Hurley", + "createdAt": 1571912514000, + "text": "

My solution uses string literal Find out more...

\n\n

\r\n
\r\n
// Declare Date as d\r\nvar d = new Date()\r\n\r\n// Inline formatting of Date\r\nconst exampleOne = `${d.getDay()}-${d.getMonth() + 1}-${d.getFullYear()}`\r\n// January is 0 so +1 is required\r\n\r\n// With Breaklines and Operators\r\nconst exampleTwo = `+++++++++++\r\nWith Break Lines and Arithmetic Operators Example\r\nYear on newline: ${d.getFullYear()}\r\nYear minus(-) 30 years: ${d.getFullYear() - 30}\r\nYou get the idea...\r\n+++++++++++`\r\n\r\nconsole.log('=============')\r\nconsole.log(exampleOne)\r\nconsole.log('=============')\r\n\r\nconsole.log(exampleTwo)
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a7c", + "creator": "anil shrestha", + "createdAt": 1564654417000, + "text": "

This does a lot;

\n

\r\n
\r\n
    var today = new Date();\n    var date = today.getFullYear()+'/'+(today.getMonth()+1)+'/'+today.getDate();\n    document.write(date);
\r\n
\r\n
\r\n

\n

Where today.getFullYear() gets current year,

\n

today.getMonth()+1 gets current month

\n

and today.getDate() gets today's date.\nAll of this is concatinated with '/'.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32716082fcc3049e9237f", + "creator": "mickmackusa", + "createdAt": 1588591137000, + "text": "Code-only answers are low-value on Stack Overflow because they do a very poor job of educating/empowering the OP and thousands of future researchers.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32716082fcc3049e92380", + "creator": "Rihard Novozhilov", + "createdAt": 1636558382000, + "text": "Why today.getMonth()+1 ?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a7e", + "creator": "agDev", + "createdAt": 1573144490000, + "text": "

To get just the date then it is built in to javascript:

\n\n
new Date();\n
\n\n

If you are looking for date formatting and you are anyways using the Kendo JQuery UI library for your site then I suggest using the built in kendo function:

\n\n
kendo.toString(new Date(), \"yyMMdd\"); //or any other typical date format\n
\n\n

For a full list of supported formats see here

\n", + "upvotes": 414, + "upvoterUsernames": [], + "downvotes": 414, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a7f", + "creator": "Adrian Bartholomew", + "createdAt": 1573156833000, + "text": "

If you only require the string representation, then simply use:

\n\n
Date();\n
\n", + "upvotes": 215, + "upvoterUsernames": [], + "downvotes": 215, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32717082fcc3049e92384", + "creator": "Adrian Bartholomew", + "createdAt": 1605367518000, + "text": "Thats not true. Date() -> "Sat Nov 14 2020 09:23:28 GMT-0600 (Central Standard Time)". Please reverse your negative point penalization.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32717082fcc3049e92386", + "creator": "Adrian Bartholomew", + "createdAt": 1611073965000, + "text": "Thank you. I appreciate that.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a80", + "creator": "Ashish Pathak", + "createdAt": 1580901254000, + "text": "

Try This and you can adjust date formate accordingly:

\n\n
var today = new Date();\n    var dd = today.getDate();\n    var mm = today.getMonth() + 1;\n    var yyyy = today.getFullYear();\n    if (dd < 10) {\n        dd = '0' + dd;\n    }\n    if (mm < 10) {\n        mm = '0' + mm;\n    }\n var myDate= dd + '-' + mm + '-' + yyyy;\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a81", + "creator": "Consta Gorgan", + "createdAt": 1581273550000, + "text": "

TL;DR

\n

Most of the answers found here are correct only if you need the current time that's on your local machine (client) which is a source that often cannot be considered reliable (it will probably differ from another system).

\n

Reliable sources are:

\n\n

Details

\n

A method called on the Date instance will return a value based on the local time of your machine.

\n

Further details can be found in "MDN web docs": JavaScript Date object.

\n

For your convenience, I've added a relevant note from their docs:

\n
\n

(...) the basic methods to fetch the date and time or its components all work in the local (i.e. host system) time zone and offset.

\n
\n

Another source mentioning this is: JavaScript date and time object

\n
\n

it is important to note that if someone's clock is off by a few hours or they are in a different time zone, then the Date object will create a different times from the one created on your own computer.

\n
\n

Some reliable sources that you can use are:

\n\n

But if accuracy is not important for your use case or if you simply need the date to be relative to local machine's time then you can safely use Javascript's Date basic methods like Date.now().

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a82", + "creator": "Caiuby Freitas", + "createdAt": 1582044408000, + "text": "

A straighforward way to pull that off (whilst considering your current time zone it taking advantage of the ISO yyyy-mm-dd format) is:

\n\n
let d = new Date().toISOString().substring(0,19).replace(\"T\",\" \") // \"2020-02-18 16:41:58\"\n
\n\n

Usually, this is a pretty all-purpose compatible date format and you can convert it to pure date value if needed:

\n\n
Date.parse(d); // 1582044297000\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a83", + "creator": "Kamil Kiełczewski", + "createdAt": 1583240450000, + "text": "

Try

\n\n
`${Date()}`.slice(4,15)\n
\n\n

\r\n
\r\n
console.log( `${Date()}`.slice(4,15) )
\r\n
\r\n
\r\n

\n\n

We use here standard JS functionalities: template literals, Date object which is cast to string, and slice. This is probably shortest solution which meet OP requirements (no time, only date)

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32717082fcc3049e9238a", + "creator": "Jaydeep Shil", + "createdAt": 1584544881000, + "text": "Question is for javascript.", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + }, + { + "_id": "62f32717082fcc3049e9238c", + "creator": "Kamil Kiełczewski", + "createdAt": 1584547146000, + "text": "@JaydeepShil - this is javascript solution - if you don't believe run above snippet or copy-paste it to chrome console", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32717082fcc3049e9238d", + "creator": "zardilior", + "createdAt": 1598369326000, + "text": "Great and concise solution!! Thanks", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a84", + "creator": "SpaceX", + "createdAt": 1586417195000, + "text": "

For anyone looking for a date format like this 09-Apr-2020

\n\n
function getDate(){\n  var months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\n  var today = new Date();\n  var dd    = String(today.getDate()).padStart(2, '0');\n  var mm    = months[today.getMonth()];\n  var yyyy  = today.getFullYear();\n\n  today = dd + \"-\" + mm + \"-\" + yyyy;\n  return today;\n}\n\ngetDate();\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a85", + "creator": "Yanir Calisar", + "createdAt": 1596367281000, + "text": "
Date.prototype.toLocalFullDateStringYYYYMMDDHHMMSS = function () {\nif (this != null && this != undefined) {\n    let str = this.getFullYear();\n    str += "-" + round(this.getMonth() + 1);\n    str += "-" + round(this.getDate());\n    str += "T";\n    str += round(this.getHours());\n    str += ":" + round(this.getMinutes());\n    str += ":" + round(this.getSeconds());\n    return str;\n} else {\n    return this;\n}\n\nfunction round(n){\n    if(n < 10){\n        return "0" + n;\n    }\n    else return n;\n}};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a86", + "creator": "Constantin", + "createdAt": 1602185658000, + "text": "

Using JS built in Date.prototype.toLocaleDateString() (more options here MDN docs) :

\n

\r\n
\r\n
const options = { \n  month: '2-digit', \n  day: '2-digit',\n  year: 'numeric', \n};\n\nconsole.log(new Date().toLocaleDateString('en-US', options)); // mm/dd/yyyy
\r\n
\r\n
\r\n

\n

Update: We can get similar behavior using Intl.DateTimeFormat which has decent browser support. Similar to toLocaleDateString(), we can pass an object with options:

\n
const date = new Date('Dec 2, 2021') // Thu Dec 16 2021 15:49:39 GMT-0600\nconst options = {\n  day: '2-digit',\n  month: '2-digit',\n  year: 'numeric',\n}\nnew Intl.DateTimeFormat('en-US', options).format(date) // '12/02/2021'\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a87", + "creator": "IonicMan", + "createdAt": 1610378997000, + "text": "

So many complicated answers...

\n

Just use new Date() and if you need it as a string, simply use new Date().toISOString()

\n

Enjoy!

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a88", + "creator": "Shubham Chadokar", + "createdAt": 1612764764000, + "text": "

Most of the other answers are providing the date with time.
\nIf you only need date.

\n
new Date().toISOString().split("T")[0]\n
\n

Output

\n
[ '2021-02-08', '06:07:44.629Z' ]\n
\n

If you want it in / format use replaceAll.

\n
new Date().toISOString().split("T")[0].replaceAll("-", "/")\n
\n

If you want other formats then best to use momentjs.

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32717082fcc3049e92392", + "creator": "Shubham Chadokar", + "createdAt": 1616036909000, + "text": "Yeah, maybe they had something else in mind.", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 175, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a89", + "creator": "Force Bolt", + "createdAt": 1620743637000, + "text": "
// Try this simple way\n\nconst today = new Date();\nlet date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();\nconsole.log(date);\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a8a", + "creator": "Mansour Alnasser", + "createdAt": 1621011061000, + "text": "

My way

\n
let dateString = new Date().toLocaleString().split(',').find(() => true);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a8b", + "creator": "Parking Master", + "createdAt": 1635967673000, + "text": "

Have you tried Date.js?

\n

Milliseconds

\n
date.js.millisecond(); // 0.00\n
\n

Seconds

\n
date.js.second(); // 58\n
\n

Minutes

\n
date.js.minute(); // 31\n
\n

Hours

\n
date.js.hour(); // 6  (PM)\n
\n

Days

\n
date.js.day(); // Monday\n
\n

Weeks

\n
date.js.week(); // (Week Of the Month / WOM) => 2\n
\n

Month

\n
date.js.month(); // (Month) => November\n
\n

TLM (Three-Letter-Month)

\n
date.js.tlmonth(); // (Month) => Dec\n
\n

Year

\n
date.js.year(); // (Year / String: "") => "2021"\n
\n

Season

\n
date.js.season(); // (Fall / Season: seasons) => "fall"\n
\n

Current Time in AM/PM

\n
date.js.time(); // (Time / Zone: "PDT/EDT etc.") => 10:04 AM\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a8c", + "creator": "jchnxu", + "createdAt": 1646617896000, + "text": "

No library needed, timezone taken into consideration.

\n

Because sometimes you will need to calculate it on the server. This can be server timezone independent.

\n

\r\n
\r\n
const currentTimezoneOffset = 8; // UTC+8:00 timezone, change it\n\nDate.prototype.yyyymmdd = function() {\n    return [\n        this.getFullYear(),\n        (this.getMonth()+1).toString().padStart(2, '0'), // getMonth() is zero-based\n        this.getDate().toString().padStart(2, '0')\n    ].join('-');\n};\n\nfunction getTodayDateStr() {\n  const d = new Date();\n  // console.log(d);\n  const d2 = new Date(d.getTime() + (d.getTimezoneOffset() + currentTimezoneOffset * 60) * 60 * 1000);\n  // console.log(d2, d2.yyyymmdd());\n  return d2.yyyymmdd();\n}\n\nconsole.log(getTodayDateStr());
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a8e", + "creator": "Jorge", + "createdAt": 1657280673000, + "text": "

Importante note: do not use: var today = new Date();\nBUT var dateToday = new Date(); for example as var today does not indicate anything

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a8d", + "creator": "SarjanWebDev", + "createdAt": 1652840299000, + "text": "

In Australia, I prefer to get DD/MM/YYYY using this

\n
(new Date()).toISOString().slice(0, 10).split("-").reverse().join("/")\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32718082fcc3049e92398", + "creator": "Craws", + "createdAt": 1653382935000, + "text": "Actually returns DD/MM/YYYY , without any details or unwanted info.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32718082fcc3049e9239a", + "creator": "imbr", + "createdAt": 1653575421000, + "text": "perfect what I wanted", + "upvotes": 459, + "upvoterUsernames": [], + "downvotes": 459, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c6082fcc3049e909e8", + "creator": "Hendrik", + "createdAt": 1254915580000, + "text": "var currentTime = new Date();", + "upvotes": 760, + "upvoterUsernames": [], + "downvotes": 313, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e9", + "creator": "RBoschini", + "createdAt": 1450805455000, + "text": "use momentJs, this lib is gold for developers.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909ea", + "creator": "Benj", + "createdAt": 1482326607000, + "text": "See this post : stackoverflow.com/a/30245911/1579667", + "upvotes": 832, + "upvoterUsernames": [], + "downvotes": 832, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3758527, + "uvac": 3758584 + } + }, + { + "_id": "62f321bb082fcc3049e8fef8", + "title": "Get selected text from a drop-down list (select box) using jQuery", + "title-lowercase": "get selected text from a drop-down list (select box) using jquery", + "creator": "haddar", + "createdAt": 1256817729000, + "status": "open", + "text": "

How can I get the selected text (not the selected value) from a drop-down list in jQuery?

\n", + "upvotes": 3176, + "upvoterUsernames": [], + "downvotes": 713, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2246009, + "answers": 35, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90d50", + "creator": "kgiannakakis", + "createdAt": 1256817868000, + "text": "

Try this:

\n\n
$(\"#myselect :selected\").text();\n
\n\n

For an ASP.NET dropdown you can use the following selector:

\n\n
$(\"[id*='MyDropDownId'] :selected\")\n
\n", + "upvotes": 407, + "upvoterUsernames": [], + "downvotes": 129, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d51", + "creator": "rahul", + "createdAt": 1256817938000, + "text": "
$(\"#yourdropdownid option:selected\").text();\n
\n", + "upvotes": 5089, + "upvoterUsernames": [], + "downvotes": 1129, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d53", + "creator": "Rafael", + "createdAt": 1313517404000, + "text": "
$(\"option:selected\", $(\"#TipoRecorde\")).text()\n
\n", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d52", + "creator": "Zarni", + "createdAt": 1286446195000, + "text": "
var someName = \"Test\";\n\n$(\"#<%= ddltest.ClientID %>\").each(function () {\n    $('option', this).each(function () {\n        if ($(this).text().toLowerCase() == someName) {\n            $(this).attr('selected', 'selected')\n        };\n    });\n});\n
\n\n

That will help you to get right direction. Above code is fully tested if you need further help let me know.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d54", + "creator": "Neeraj", + "createdAt": 1321262541000, + "text": "

$(\"#DropDownID\").val() will give the selected index value.

\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3284a082fcc3049e9280e", + "creator": "Peter", + "createdAt": 1322491851000, + "text": "Not exactly the answer to the question, but was useful for me. The question wants the selected text.", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d56", + "creator": "JYX", + "createdAt": 1332157265000, + "text": "

The answers posted here, for example,

\n\n
$('#yourdropdownid option:selected').text();\n
\n\n

didn't work for me, but this did:

\n\n
$('#yourdropdownid').find('option:selected').text();\n
\n\n

It is possibly an older version of jQuery.

\n", + "upvotes": 352, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d55", + "creator": "Kirk Liemohn", + "createdAt": 1328332625000, + "text": "

If you already have the dropdownlist available in a variable, this is what works for me:

\n\n
$(\"option:selected\", myVar).text()\n
\n\n

The other answers on this question helped me, but ultimately the jQuery forum thread $(this + \"option:selected\").attr(\"rel\") option selected is not working in IE helped the most.

\n\n

Update: fixed the above link

\n", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d58", + "creator": "Thangamani Palanisamy", + "createdAt": 1366711461000, + "text": "
$(\"select[id=yourDropdownid] option:selected\").text()\n
\n\n

This works fine

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d57", + "creator": "Kamrul", + "createdAt": 1359069766000, + "text": "

For the text of the selected item, use:

\n\n
$('select[name=\"thegivenname\"] option:selected').text();\n
\n\n

For the value of the selected item, use:

\n\n
$('select[name=\"thegivenname\"] option:selected').val();\n
\n", + "upvotes": 76, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d59", + "creator": "Binita Bharati", + "createdAt": 1380094607000, + "text": "

This works for me:

\n\n
$('#yourdropdownid').find('option:selected').text();\n
\n\n

jQuery version: 1.9.1

\n", + "upvotes": 96, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5a", + "creator": "FAA", + "createdAt": 1383687531000, + "text": "

For those who are using SharePoint lists and don't want to use the long generated id, this will work:

\n\n
var e = $('select[title=\"IntenalFieldName\"] option:selected').text();\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d66", + "creator": "Mojtaba", + "createdAt": 1453540051000, + "text": "

For getting selected value use

\n\n
$('#dropDownId').val();\n
\n\n

and for getting selected item text use this line:

\n\n
$(\"#dropDownId option:selected\").text();\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d67", + "creator": "Bhanu Pratap", + "createdAt": 1470762109000, + "text": "

\r\n
\r\n
$(function () {\r\n  alert('.val() = ' + $('#selectnumber').val() + '  AND  html() = ' + $('#selectnumber option:selected').html() + '  AND .text() = ' + $('#selectnumber option:selected').text());\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n  <head runat=\"server\">\r\n    <title></title>\r\n\r\n  </head>\r\n  <body>\r\n    <form id=\"form1\" runat=\"server\">\r\n      <div>\r\n        <select id=\"selectnumber\">\r\n          <option value=\"1\">one</option>\r\n          <option value=\"2\">two</option>\r\n          <option value=\"3\">three</option>\r\n          <option value=\"4\">four</option>\r\n        </select>\r\n\r\n      </div>\r\n    </form>\r\n  </body>\r\n</html>
\r\n
\r\n
\r\n

\n\n

\"Click

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d69", + "creator": "max", + "createdAt": 1484776591000, + "text": "

If you want the result as a list, then use:

\n\n
x=[];\n$(\"#list_id\").children(':selected').each(function(){x.push($(this).text());})\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d68", + "creator": "Kalaivani M", + "createdAt": 1483534428000, + "text": "
var e = document.getElementById(\"dropDownId\");\nvar div = e.options[e.selectedIndex].text;\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6a", + "creator": "Vishal Thakur", + "createdAt": 1489042990000, + "text": "

Try:

\n\n
$var = jQuery(\"#dropdownid option:selected\").val();\n   alert ($var);\n
\n\n

Or to get the text of the option, use text():

\n\n
$var = jQuery(\"#dropdownid option:selected\").text();\n   alert ($var);\n
\n\n

More Info:

\n\n\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6b", + "creator": "Curtis Yallop", + "createdAt": 1521152472000, + "text": "

For multi-selects:

\n\n
$(\"#yourdropdownid :selected\").map(function(i, v) { return $.trim($(v).text()); }\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6c", + "creator": "Manish Singh", + "createdAt": 1526900510000, + "text": "
$(\"#dropdownid option:selected\").text();\n
\n\n

if you use asp.net and write

\n\n
<Asp:dropdownlist id=\"ddl\" runat=\"Server\" />\n
\n\n

then you should use

\n\n
$('#<%=ddl.Clientid%> option:selected').text();\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6d", + "creator": "Muddasir23", + "createdAt": 1542089023000, + "text": "

Simply try the following code.

\n\n
var text= $('#yourslectbox').find(\":selected\").text();\n
\n\n

it returns the text of the selected option.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6e", + "creator": "Shahid Hussain Abbasi", + "createdAt": 1547291561000, + "text": "

Just add the below line

\n\n
$(this).prop('selected', true);\n
\n\n

replaced .att to .prop it worked for all browsers.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d6f", + "creator": "shyamm", + "createdAt": 1576565430000, + "text": "
$(\"#dropdown\").find(\":selected\").text();\n\n\n$(\"#dropdown :selected\").text();\n
\n\n

$(\"#dropdown option:selected\").text();

\n\n
$(\"#dropdown\").children(\":selected\").text();\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d71", + "creator": "Naveenbos", + "createdAt": 1581610629000, + "text": "

This code worked for me.

\n\n
$(\"#yourdropdownid\").children(\"option\").filter(\":selected\").text();\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d70", + "creator": "Kamil Kiełczewski", + "createdAt": 1579281047000, + "text": "

Try

\n\n
dropdown.selectedOptions[0].text\n
\n\n

\r\n
\r\n
function read() {\r\n  console.log( dropdown.selectedOptions[0].text );\r\n}
\r\n
<select id=\"dropdown\">\r\n  <option value=\"1\">First</option>\r\n  <option value=\"2\">Second</option>\r\n</select>\r\n<button onclick=\"read()\">read</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e9281f", + "creator": "Daniel Tonon", + "createdAt": 1587515046000, + "text": "Thanks for providing a pure-JS approach.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90d72", + "creator": "Dhiaa Al-Shalabi", + "createdAt": 1634741288000, + "text": "
$("#dropdownid").change(function(el) {\n    console.log(el.value);\n});\n
\n

Or you can use this

\n
$("#dropdownid").change(function() {\n    console.log($(this).val());\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5d", + "creator": "kishore", + "createdAt": 1406819483000, + "text": "

Use:

\n\n
('#yourdropdownid').find(':selected').text();\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5b", + "creator": "Nikhil Agrawal", + "createdAt": 1391092182000, + "text": "
 $(\"#selectID option:selected\").text();\n
\n\n

Instead of #selectID you can use any jQuery selector, like .selectClass using class.

\n\n

As mentioned in the documentation here.

\n\n

The :selected selector works for <option> elements. It does not work for checkboxes or radio inputs; use :checked for them.

\n\n

.text() As per the documentation here.

\n\n

Get the combined text contents of each element in the set of matched elements, including their descendants.

\n\n

So you can take text from any HTML element using the .text() method.

\n\n

Refer the documentation for a deeper explanation.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5c", + "creator": "124", + "createdAt": 1391234109000, + "text": "
$(\"#dropdownID\").change(function(){\n  alert($('option:selected', $(this)).text());\n});\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5f", + "creator": "vineet", + "createdAt": 1417761214000, + "text": "

In sibling case

\n\n
<a class=\"uibutton confirm addClient\" href=\"javascript:void(0);\">ADD Client</a>\n<input type=\"text\" placeholder=\"Enter client name\" style=\"margin: 5px;float: right\" class=\"clientsearch large\" />\n<select class=\"mychzn-select clientList\">\n  <option value=\"\">Select Client name....</option>\n  <option value=\"1\">abc</option>\n</select>\n\n\n /*jQuery*/\n $(this).siblings('select').children(':selected').text()\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d5e", + "creator": "MaxEcho", + "createdAt": 1407094748000, + "text": "

Various ways

\n\n
1. $(\"#myselect option:selected\").text();\n\n2. $(\"#myselect :selected\").text();\n\n3. $(\"#myselect\").children(\":selected\").text();\n\n4. $(\"#myselect\").find(\":selected\").text();\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d60", + "creator": "Nikul", + "createdAt": 1428745145000, + "text": "
$('#id').find('option:selected').text();\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d61", + "creator": "Mohammed Shaheen MK", + "createdAt": 1431613309000, + "text": "

Use this

\n\n
const select = document.getElementById(\"yourSelectId\");\n\nconst selectedIndex = select.selectedIndex;\nconst selectedValue = select.value;\nconst selectedText = select.options[selectedIndex].text;   \n
\n\n

Then you get your selected value and text inside selectedValue and selectedText.

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d62", + "creator": "Mhandzkie", + "createdAt": 1436407243000, + "text": "

This work for me:

\n\n
$(\"#city :selected\").text();\n
\n\n

I'm using jQuery 1.10.2

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d63", + "creator": "Prabhagaran", + "createdAt": 1439550759000, + "text": "

This works for me

\n\n
$(\"#dropdownid\").change(function() {\n    alert($(this).find(\"option:selected\").text());\n});\n
\n\n

If the element created dynamically

\n\n
$(document).on(\"change\", \"#dropdownid\", function() {\n    alert($(this).find(\"option:selected\").text());\n});\n
\n", + "upvotes": 144, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d65", + "creator": "Sandeep Shekhawat", + "createdAt": 1447911396000, + "text": "

Select Text and selected value on dropdown/select change event in jQuery

\n\n
$(\"#yourdropdownid\").change(function() {\n    console.log($(\"option:selected\", this).text()); //text\n    console.log($(this).val()); //value\n})\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90d64", + "creator": "Mohammad Dayyan", + "createdAt": 1442466803000, + "text": "

the following worked for me:

\n\n
$.trim($('#dropdownId option:selected').html())\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90d44", + "creator": "ankush981", + "createdAt": 1433398801000, + "text": "Just my two cents: An "ASP" drop-down is no special; it's just good old HTML. :-)", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d45", + "creator": "rogerdpack", + "createdAt": 1484767623000, + "text": "for vanilla javascript way, see stackoverflow.com/a/5947/32453", + "upvotes": 1619, + "upvoterUsernames": [], + "downvotes": 1619, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d46", + "creator": "Shahid Hussain Abbasi", + "createdAt": 1547291599000, + "text": "Just $(this).prop('selected', true); replaced .att to .prop it worked for all browsers", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d47", + "creator": "Ali NajafZadeh", + "createdAt": 1628009849000, + "text": "$("#yourdropdownid option:selected").text();", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2249189, + "uvac": 2249224 + } + }, + { + "_id": "62f321bb082fcc3049e8feb2", + "title": "How to check whether a string contains a substring in JavaScript?", + "title-lowercase": "how to check whether a string contains a substring in javascript?", + "creator": "gramm", + "createdAt": 1259067869000, + "status": "open", + "text": "

Usually I would expect a String.contains() method, but there doesn't seem to be one.

\n\n

What is a reasonable way to check for this?

\n", + "upvotes": 14515, + "upvoterUsernames": [], + "downvotes": 7102, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 7288362, + "answers": 3, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e90085", + "creator": "Fabien Ménager", + "createdAt": 1259067936000, + "text": "

ECMAScript 6 introduced String.prototype.includes:

\n

\r\n
\r\n
const string = \"foo\";\nconst substring = \"oo\";\n\nconsole.log(string.includes(substring)); // true
\r\n
\r\n
\r\n

\n

includes doesn’t have Internet Explorer support, though. In ECMAScript 5 or older environments, use String.prototype.indexOf, which returns -1 when a substring cannot be found:

\n

\r\n
\r\n
var string = \"foo\";\nvar substring = \"oo\";\n\nconsole.log(string.indexOf(substring) !== -1); // true
\r\n
\r\n
\r\n

\n", + "upvotes": 22926, + "upvoterUsernames": [], + "downvotes": 7590, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f322a2082fcc3049e911bd", + "creator": "Ry-", + "createdAt": 1632325153000, + "text": "@Aashiq: Yes, an empty string is a substring of every string.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322a2082fcc3049e911bf", + "creator": "Experimenter", + "createdAt": 1649809266000, + "text": "indexOf is also case case-sensitive search, so both includes and indexOf are case-sensitive .", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f322a2082fcc3049e911c1", + "creator": "KWallace", + "createdAt": 1656615897000, + "text": "Why is a discussion of case sensitivity even taking place here?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322a2082fcc3049e911c3", + "creator": "Mitul", + "createdAt": 1657452870000, + "text": "what is the complexity of javascript implementation of string.indexOf()?", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e90086", + "creator": "eliocs", + "createdAt": 1357554196000, + "text": "

There is a String.prototype.includes in ES6:

\n
"potato".includes("to");\n> true\n
\n

Note that this does not work in Internet Explorer or some other old browsers with no or incomplete ES6 support. To make it work in old browsers, you may wish to use a transpiler like Babel, a shim library like es6-shim, or this polyfill from MDN:

\n
if (!String.prototype.includes) {\n  String.prototype.includes = function(search, start) {\n    'use strict';\n    if (typeof start !== 'number') {\n      start = 0;\n    }\n\n    if (start + search.length > this.length) {\n      return false;\n    } else {\n      return this.indexOf(search, start) !== -1;\n    }\n  };\n}\n
\n", + "upvotes": 1167, + "upvoterUsernames": [], + "downvotes": 431, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a2082fcc3049e911c5", + "creator": "gman", + "createdAt": 1612279741000, + "text": "just curious, why do you need to check the length? Does IE fail in that case or something?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e90087", + "creator": "wz366", + "createdAt": 1499293598000, + "text": "

Another alternative is KMP (Knuth–Morris–Pratt).

\n

The KMP algorithm searches for a length-m substring in a length-n string in worst-case O(n+m) time, compared to a worst-case of O(nm) for the naive algorithm, so using KMP may be reasonable if you care about worst-case time complexity.

\n

Here's a JavaScript implementation by Project Nayuki, taken from https://www.nayuki.io/res/knuth-morris-pratt-string-matching/kmp-string-matcher.js:

\n
// Searches for the given pattern string in the given text string using the Knuth-Morris-Pratt string matching algorithm.\n// If the pattern is found, this returns the index of the start of the earliest match in 'text'. Otherwise -1 is returned.\n
\n

\r\n
\r\n
function kmpSearch(pattern, text) {\n  if (pattern.length == 0)\n    return 0; // Immediate match\n\n  // Compute longest suffix-prefix table\n  var lsp = [0]; // Base case\n  for (var i = 1; i < pattern.length; i++) {\n    var j = lsp[i - 1]; // Start by assuming we're extending the previous LSP\n    while (j > 0 && pattern[i] !== pattern[j])\n      j = lsp[j - 1];\n    if (pattern[i] === pattern[j])\n      j++;\n    lsp.push(j);\n  }\n\n  // Walk through text string\n  var j = 0; // Number of chars matched in pattern\n  for (var i = 0; i < text.length; i++) {\n    while (j > 0 && text[i] != pattern[j])\n      j = lsp[j - 1]; // Fall back in the pattern\n    if (text[i]  == pattern[j]) {\n      j++; // Next char matched, increment position\n      if (j == pattern.length)\n        return i - (j - 1);\n    }\n  }\n  return -1; // Not found\n}\n\nconsole.log(kmpSearch('ays', 'haystack') != -1) // true\nconsole.log(kmpSearch('asdf', 'haystack') != -1) // false
\r\n
\r\n
\r\n

\n", + "upvotes": 187, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322a2082fcc3049e911c8", + "creator": "wz366", + "createdAt": 1626369604000, + "text": "KMP provides linear O(n) performance here.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f322a2082fcc3049e911ca", + "creator": "TheLebDev", + "createdAt": 1626595960000, + "text": "@wz366 KMP provides O(n), what about the rest? Any Idea?", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f322a2082fcc3049e911cc", + "creator": "dandavis", + "createdAt": 1629428067000, + "text": "If this is used for speed, it would likely run faster if you replaced .charAt(i) with [i] to avoid the extra function calls.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 7302877, + "uvac": 7302880 + } + }, + { + "_id": "62f321bb082fcc3049e8fefa", + "title": "Get all unique values in a JavaScript array (remove duplicates)", + "title-lowercase": "get all unique values in a javascript array (remove duplicates)", + "creator": "Mottie", + "createdAt": 1261715303000, + "status": "open", + "text": "

I have an array of numbers that I need to make sure are unique. I found the code snippet below on the internet and it works great until the array has a zero in it. I found this other script here on Stack Overflow that looks almost exactly like it, but it doesn't fail.

\n\n

So for the sake of helping me learn, can someone help me determine where the prototype script is going wrong?

\n\n
Array.prototype.getUnique = function() {\n var o = {}, a = [], i, e;\n for (i = 0; e = this[i]; i++) {o[e] = 1};\n for (e in o) {a.push (e)};\n return a;\n}\n
\n\n

More answers from duplicate question:

\n\n\n\n

Similar question:

\n\n\n", + "upvotes": 3325, + "upvoterUsernames": [], + "downvotes": 885, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 2628770, + "answers": 84, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90da8", + "creator": "Luca Matteis", + "createdAt": 1261715549000, + "text": "

That's because 0 is a falsy value in JavaScript.

\n\n

this[i] will be falsy if the value of the array is 0 or any other falsy value.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e9288a", + "creator": "Mottie", + "createdAt": 1261716406000, + "text": "Ahhhh, ok I see now... but would there be an easy fix to make it work?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dad", + "creator": "Mottie", + "createdAt": 1342107696000, + "text": "

I have since found a nice method that uses jQuery

\n\n
arr = $.grep(arr, function(v, k){\n    return $.inArray(v ,arr) === k;\n});\n
\n\n

Note: This code was pulled from Paul Irish's duck punching post - I forgot to give credit :P

\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e9288c", + "creator": "Mister Smith", + "createdAt": 1370441810000, + "text": "A concise solution, but calling inArray is way less efficient than calling hasOwnProperty.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f3288d082fcc3049e9288e", + "creator": "speedplane", + "createdAt": 1503550011000, + "text": "This is also O(N^2), right? Whereas the dictionary or hasOwnProperty approach would likely be O(N*logN).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dac", + "creator": "Nikola Petkanski", + "createdAt": 1342076182000, + "text": "

You can also use jQuery

\n\n
var a = [1,5,1,6,4,5,2,5,4,3,1,2,6,6,3,3,2,4];\n\n// note: jQuery's filter params are opposite of javascript's native implementation :(\nvar unique = $.makeArray($(a).filter(function(i,itm){ \n    // note: 'index', not 'indexOf'\n    return i == $(a).index(itm);\n}));\n\n// unique: [1, 5, 6, 4, 2, 3]\n
\n\n

Originally answered at: jQuery function to get all unique elements from an array?

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e92890", + "creator": "hippietrail", + "createdAt": 1347262219000, + "text": "This one seems only to work for arrays of integers. When I include some strings they all get stripped out of the result.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dab", + "creator": "kornfridge", + "createdAt": 1342023953000, + "text": "

You can also use underscore.js.

\n\n

\r\n
\r\n
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
\r\n
<script src=\"http://underscorejs.org/underscore-min.js\"></script>
\r\n
\r\n
\r\n

\n\n

which will return:

\n\n
[1, 2, 3, 4]\n
\n", + "upvotes": 218, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e92893", + "creator": "Jacob Dalton", + "createdAt": 1461701171000, + "text": "Please do this folks. Don't jack something onto to the Array prototype. Please.", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f3288d082fcc3049e92895", + "creator": "anshul", + "createdAt": 1627397314000, + "text": "@JacobDalton why not? Is there a downside to "jacking something" onto the array?", + "upvotes": 256, + "upvoterUsernames": [], + "downvotes": 256, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90daa", + "creator": "Decebal", + "createdAt": 1320153513000, + "text": "

If you're using Prototype framework there is no need to do 'for' loops, you can use http://prototypejs.org/doc/latest/language/Array/prototype/uniq/ like this:

\n
var a = Array.uniq();  \n
\n

Which will produce a duplicate array with no duplicates. I came across your question searching a method to count distinct array records so after uniq() I used size() and there was my simple result.\np.s. Sorry if i mistyped something

\n

edit: if you want to escape undefined records you may want to add compact() before, like this:

\n
var a = Array.compact().uniq();  \n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e92898", + "creator": "Decebal", + "createdAt": 1320160237000, + "text": "because i found a better answer, i think about topics are for all people not just for the one who asked", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90da9", + "creator": "ephemient", + "createdAt": 1261717892000, + "text": "
Array.prototype.getUnique = function() {\n    var o = {}, a = []\n    for (var i = 0; i < this.length; i++) o[this[i]] = 1\n    for (var e in o) a.push(e)\n    return a\n}\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e9289a", + "creator": "Camilo Martin", + "createdAt": 1369317776000, + "text": "I think this won't work if the array contains objects/arrays, and I'm not sure if it will preserve the type of scalars.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90db1", + "creator": "Dan Fox", + "createdAt": 1370575853000, + "text": "

I'm not sure why Gabriel Silveira wrote the function that way but a simpler form that works for me just as well and without the minification is:

\n\n
Array.prototype.unique = function() {\n  return this.filter(function(value, index, array) {\n    return array.indexOf(value, index + 1) < 0;\n  });\n};\n
\n\n

or in CoffeeScript:

\n\n
Array.prototype.unique = ->\n  this.filter( (value, index, array) ->\n    array.indexOf(value, index + 1) < 0\n  )\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db0", + "creator": "Torbjörn Nomell", + "createdAt": 1369316031000, + "text": "

If anyone using knockoutjs

\n\n
ko.utils.arrayGetDistinctValues()\n
\n\n

BTW have look at all ko.utils.array* utilities.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dae", + "creator": "Gabriel Silveira", + "createdAt": 1343408224000, + "text": "

This prototype getUnique is not totally correct, because if i have a Array like: [\"1\",1,2,3,4,1,\"foo\"] it will return [\"1\",\"2\",\"3\",\"4\"] and \"1\" is string and 1 is a integer; they are different.

\n\n

Here is a correct solution:

\n\n
Array.prototype.unique = function(a){\n    return function(){ return this.filter(a) }\n}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });\n
\n\n

using:

\n\n
var foo;\nfoo = [\"1\",1,2,3,4,1,\"foo\"];\nfoo.unique();\n
\n\n

The above will produce [\"1\",2,3,4,1,\"foo\"].

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90daf", + "creator": "Cœur", + "createdAt": 1366217292000, + "text": "

Without extending Array.prototype (it is said to be a bad practice) or using jquery/underscore, you can simply filter the array.

\n\n

By keeping last occurrence:

\n\n
    function arrayLastUnique(array) {\n        return array.filter(function (a, b, c) {\n            // keeps last occurrence\n            return c.indexOf(a, b + 1) < 0;\n        });\n    },\n
\n\n

or first occurrence:

\n\n
    function arrayFirstUnique(array) {\n        return array.filter(function (a, b, c) {\n            // keeps first occurrence\n            return c.indexOf(a) === b;\n        });\n    },\n
\n\n

Well, it's only javascript ECMAScript 5+, which means only IE9+, but it's nice for a development in native HTML/JS (Windows Store App, Firefox OS, Sencha, Phonegap, Titanium, ...).

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db3", + "creator": "Jason", + "createdAt": 1374112337000, + "text": "

It appears we have lost Rafael's answer, which stood as the accepted answer for a few years. This was (at least in 2017) the best-performing solution if you don't have a mixed-type array:

\n\n
Array.prototype.getUnique = function(){\n    var u = {}, a = [];\n    for (var i = 0, l = this.length; i < l; ++i) {\n        if (u.hasOwnProperty(this[i])) {\n            continue;\n        }\n        a.push(this[i]);\n        u[this[i]] = 1;\n    }\nreturn a;\n}\n
\n\n

If you do have a mixed-type array, you can serialize the hash key:

\n\n
Array.prototype.getUnique = function() {\n    var hash = {}, result = [], key; \n    for ( var i = 0, l = this.length; i < l; ++i ) {\n        key = JSON.stringify(this[i]);\n        if ( !hash.hasOwnProperty(key) ) {\n            hash[key] = true;\n            result.push(this[i]);\n        }\n    }\n    return result;\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db2", + "creator": "Kishore Relangi", + "createdAt": 1372826882000, + "text": "

If order is not important then we can make an hash and get the keys to make unique array.

\n\n
var ar = [1,3,4,5,5,6,5,6,2,1];\nvar uarEle = {};\nlinks.forEach(function(a){ uarEle[a] = 1; });\nvar uar = keys(uarEle)\n
\n\n

uar will be having the unique array elements.

\n", + "upvotes": 139, + "upvoterUsernames": [], + "downvotes": 139, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db4", + "creator": "kornfridge", + "createdAt": 1374866322000, + "text": "

You can also use sugar.js:

\n\n
[1,2,2,3,1].unique() // => [1,2,3]\n\n[{id:5, name:\"Jay\"}, {id:6, name:\"Jay\"}, {id: 5, name:\"Jay\"}].unique('id') \n  // => [{id:5, name:\"Jay\"}, {id:6, name:\"Jay\"}]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db5", + "creator": "Mrchief", + "createdAt": 1376602119000, + "text": "

Building on other answers, here's another variant that takes an optional flag to choose a strategy (keep first occurrence or keep last):

\n\n

Without extending Array.prototype

\n\n
function unique(arr, keepLast) {\n  return arr.filter(function (value, index, array) {\n    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;\n  });\n};\n\n// Usage\nunique(['a', 1, 2, '1', 1, 3, 2, 6]); // -> ['a', 1, 2, '1', 3, 6]\nunique(['a', 1, 2, '1', 1, 3, 2, 6], true); // -> ['a', '1', 1, 3, 2, 6]\n
\n\n

Extending Array.prototype

\n\n
Array.prototype.unique = function (keepLast) {\n  return this.filter(function (value, index, array) {\n    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;\n  });\n};\n\n// Usage\n['a', 1, 2, '1', 1, 3, 2, 6].unique(); // -> ['a', 1, 2, '1', 3, 6]\n['a', 1, 2, '1', 1, 3, 2, 6].unique(true); // -> ['a', '1', 1, 3, 2, 6]\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db7", + "creator": "Seth Holladay", + "createdAt": 1389768963000, + "text": "

Many of the answers here may not be useful to beginners. If de-duping an array is difficult, will they really know about the prototype chain, or even jQuery?

\n

In modern browsers, a clean and simple solution is to store data in a Set, which is designed to be a list of unique values.

\n

\r\n
\r\n
const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];\nconst uniqueCars = Array.from(new Set(cars));\nconsole.log(uniqueCars);
\r\n
\r\n
\r\n

\n

The Array.from is useful to convert the Set back to an Array so that you have easy access to all of the awesome methods (features) that arrays have. There are also other ways of doing the same thing. But you may not need Array.from at all, as Sets have plenty of useful features like forEach.

\n

If you need to support old Internet Explorer, and thus cannot use Set, then a simple technique is to copy items over to a new array while checking beforehand if they are already in the new array.

\n
// Create a list of cars, with duplicates.\nvar cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];\n// Create a list of unique cars, to put a car in if we haven't already.\nvar uniqueCars = [];\n\n// Go through each car, one at a time.\ncars.forEach(function (car) {\n    // The code within the following block runs only if the\n    // current car does NOT exist in the uniqueCars list\n    // - a.k.a. prevent duplicates\n    if (uniqueCars.indexOf(car) === -1) {\n        // Since we now know we haven't seen this car before,\n        // copy it to the end of the uniqueCars list.\n        uniqueCars.push(car);\n    }\n});\n
\n

To make this instantly reusable, let's put it in a function.

\n
function deduplicate(data) {\n    if (data.length > 0) {\n        var result = [];\n\n        data.forEach(function (elem) {\n            if (result.indexOf(elem) === -1) {\n                result.push(elem);\n            }\n        });\n\n        return result;\n    }\n}\n
\n

So to get rid of the duplicates, we would now do this.

\n
var uniqueCars = deduplicate(cars);\n
\n

The deduplicate(cars) part becomes the thing we named result when the function completes.

\n

Just pass it the name of any array you like.

\n", + "upvotes": 110, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db6", + "creator": "PaulL", + "createdAt": 1386403504000, + "text": "

Yet another answer, just because I wrote one for my specific use case. I happened to be sorting the array anyway, and given I'm sorting I can use that to deduplicate.

\n\n

Note that my sort deals with my specific data types, you might need a different sort depending on what sort of elements you have.

\n\n
var sortAndDedup = function(array) {\n  array.sort(function(a,b){\n    if(isNaN(a) && isNaN(b)) { return a > b ? 1 : (a < b ? -1 : 0); }\n    if(isNaN(a)) { return 1; }\n    if(isNaN(b)) { return -1; }\n    return a-b;\n  });\n\n  var newArray = [];\n  var len = array.length;\n  for(var i=0; i<len; i++){\n    if(i === 0 || array[i] != array[i-1]){\n      newArray.push(array[i]);\n    }\n  }\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db9", + "creator": "Roobie Nuby", + "createdAt": 1393154798000, + "text": "

I looked at Joeytje50's code on jsperf who has compared a number of alternatives. His code had many minor typos, which made a difference in the performance and the correctness.

\n\n

More importantly, he is testing on a very small array. I made an array with 1000 integers. Each integer was 100 times a random integer between 0 and 1000. This makes for about 1000/e = 368 duplicates on the average. The results are at jsperf.

\n\n

This is a much more realistic scenario of where efficiency might be needed. These changes make dramatic changes in the claims (specifically the code touted as fastest is nowhere near fast). The obvious winners are where hashing techniques are used, with the best one being

\n\n
Array.prototype.getUnique3 = function(){\n   var u = Object.create(null), a = [];\n   for(var i = 0, l = this.length; i < l; ++i){\n      if(this[i] in u) continue;\n      a.push(this[i]);\n      u[this[i]] = 1;\n   }\n   return a.length;\n}\n
\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dba", + "creator": "pix", + "createdAt": 1410823519000, + "text": "

Yet another solution for the pile.

\n\n

I recently needed to make a sorted list unique and I did it using filter that keeps track of the previous item in an object like this:

\n\n
uniqueArray = sortedArray.filter(function(e) { \n    if(e==this.last) \n      return false; \n    this.last=e; return true;  \n  },{last:null});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dbb", + "creator": "sergeyz", + "createdAt": 1411153747000, + "text": "
[\"Defects\", \"Total\", \"Days\", \"City\", \"Defects\"].reduce(function(prev, cur) {\n  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;\n }, []);\n\n[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {\n  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;\n }, []);\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90db8", + "creator": "Joeytje50", + "createdAt": 1391040613000, + "text": "

The simplest, and fastest (in Chrome) way of doing this:

\n
Array.prototype.unique = function() {\n    var a = [];\n    for (var i=0, l=this.length; i<l; i++)\n        if (a.indexOf(this[i]) === -1)\n            a.push(this[i]);\n    return a;\n}\n
\n

Simply goes through every item in the array, tests if that item is already in the list, and if it's not, pushes to the array that gets returned.

\n

According to JSBench, this function is the fastest of the ones I could find anywhere - feel free to add your own though.

\n

The non-prototype version:

\n
function uniques(arr) {\n    var a = [];\n    for (var i=0, l=arr.length; i<l; i++)\n        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')\n            a.push(arr[i]);\n    return a;\n}\n
\n

Sorting

\n

When also needing to sort the array, the following is the fastest:

\n
Array.prototype.sortUnique = function() {\n    this.sort();\n    var last_i;\n    for (var i=0;i<this.length;i++)\n        if ((last_i = this.lastIndexOf(this[i])) !== i)\n            this.splice(i+1, last_i-i);\n    return this;\n}\n
\n

or non-prototype:

\n
function sortUnique(arr) {\n    arr.sort();\n    var last_i;\n    for (var i=0;i<arr.length;i++)\n        if ((last_i = arr.lastIndexOf(arr[i])) !== i)\n            arr.splice(i+1, last_i-i);\n    return arr;\n}\n
\n

This is also faster than the above method in most non-Chrome browsers.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dbc", + "creator": "GibboK", + "createdAt": 1425976407000, + "text": "

This script modify the array, filtering out duplicated values. It works with numbers and strings.

\n\n

https://jsfiddle.net/qsdL6y5j/1/

\n\n
    Array.prototype.getUnique = function () {\n        var unique = this.filter(function (elem, pos) {\n            return this.indexOf(elem) == pos;\n        }.bind(this));\n        this.length = 0;\n        this.splice(0, 0, unique);\n    }\n\n    var duplicates = [0, 0, 1, 1, 2, 3, 1, 1, 0, 4, 4];\n    duplicates.getUnique();\n    alert(duplicates);\n
\n\n
\n\n

This version instead, allow you to return a new array with unique value keeping the original (just pass true).

\n\n

https://jsfiddle.net/dj7qxyL7/

\n\n
    Array.prototype.getUnique = function (createArray) {\n        createArray = createArray === true ? true : false;\n        var temp = JSON.stringify(this);\n        temp = JSON.parse(temp);\n        if (createArray) {\n            var unique = temp.filter(function (elem, pos) {\n                return temp.indexOf(elem) == pos;\n            }.bind(this));\n            return unique;\n        }\n        else {\n            var unique = this.filter(function (elem, pos) {\n                return this.indexOf(elem) == pos;\n            }.bind(this));\n            this.length = 0;\n            this.splice(0, 0, unique);\n        }\n    }\n\n    var duplicates = [0, 0, 1, 1, 2, 3, 1, 1, 0, 4, 4];\n    console.log('++++ ovveride')\n    duplicates.getUnique();\n    console.log(duplicates);\n    console.log('++++ new array')\n    var duplicates2 = [0, 0, 1, 1, 2, 3, 1, 1, 0, 4, 4];\n    var unique = duplicates2.getUnique(true);\n    console.log(unique);\n    console.log('++++ original')\n    console.log(duplicates2);\n
\n\n
\n\n
Browser support:\n\nFeature Chrome  Firefox (Gecko)     Internet Explorer   Opera   Safari\nBasic support   (Yes)   1.5 (1.8)   9                   (Yes)   (Yes)\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dbd", + "creator": "rab", + "createdAt": 1427617924000, + "text": "

Using object keys to make unique array, I have tried following

\n\n
function uniqueArray( ar ) {\n  var j = {};\n\n  ar.forEach( function(v) {\n    j[v+ '::' + typeof v] = v;\n  });\n\n\n  return Object.keys(j).map(function(v){\n    return j[v];\n  });\n}   \n\nuniqueArray([\"1\",1,2,3,4,1,\"foo\", false, false, null,1]);\n
\n\n

Which returns [\"1\", 1, 2, 3, 4, \"foo\", false, null]

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288f082fcc3049e928a8", + "creator": "Max Makhrov", + "createdAt": 1490617336000, + "text": "I think, your answer is the fastest solution because it uses hash.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dbe", + "creator": "ilgam", + "createdAt": 1429628291000, + "text": "

Look at this. Jquery provides uniq method:\nhttps://api.jquery.com/jQuery.unique/

\n\n
var ids_array = []\n\n$.each($(my_elements), function(index, el) {\n    var id = $(this).attr(\"id\")\n    ids_array.push(id)\n});\n\nvar clean_ids_array = jQuery.unique(ids_array)\n\n$.each(clean_ids_array, function(index, id) {\n   elment = $(\"#\" + id)   // my uniq element\n   // TODO WITH MY ELEMENT\n});\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dbf", + "creator": "Grozz", + "createdAt": 1442653305000, + "text": "

The version that accepts selector, should be pretty fast and concise:

\n\n
function unique(xs, f) {\n  var seen = {};\n  return xs.filter(function(x) {\n    var fx = (f && f(x)) || x;\n    return !seen[fx] && (seen[fx] = 1);\n  });\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dc1", + "creator": "webdeb", + "createdAt": 1446638075000, + "text": "

This one is not pure, it will modify the array, but this is the fastest one. If yours is faster, then please write in the comments ;)

\n\n

http://jsperf.com/unique-array-webdeb

\n\n
Array.prototype.uniq = function(){\n    for(var i = 0, l = this.length; i < l; ++i){\n        var item = this[i];\n        var duplicateIdx = this.indexOf(item, i + 1);\n        while(duplicateIdx != -1) {\n            this.splice(duplicateIdx, 1);\n            duplicateIdx = this.indexOf(item, duplicateIdx);\n            l--;\n        }\n    }\n\n    return this;\n}\n\n[\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\",\n \"\",2,4,\"A\",\"abc\"\n].uniq() //  [\"\",2,4,\"A\",\"abc\"]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288f082fcc3049e928ad", + "creator": "Michiel van der Blonk", + "createdAt": 1470502031000, + "text": "but you might want to name your variable 'duplicate' not 'dublicate', unless you live in Dublin perhaps.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3288f082fcc3049e928af", + "creator": "webdeb", + "createdAt": 1470571702000, + "text": "@MichielvanderBlonk sry for my b4d english / dublish :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dc0", + "creator": "ArunT", + "createdAt": 1444815754000, + "text": "

Updated answer for ES6/ES2015: Using the Set and the spread operator (thanks le-m), the single line solution is:

\n
let uniqueItems = [...new Set(items)]\n
\n

Which returns

\n
[4, 5, 6, 3, 2, 23, 1]\n
\n", + "upvotes": 3040, + "upvoterUsernames": [], + "downvotes": 1507, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288f082fcc3049e928b1", + "creator": "Alexander Goncharov", + "createdAt": 1477316999000, + "text": "Notice, that inner array wouldn't work Array.from(new Set([[1,2],[1,2],[1,2,3]]))", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3288f082fcc3049e928b3", + "creator": "Lee Goddard", + "createdAt": 1653297884000, + "text": "In Typescript, use Array.from( new Set( items ) )", + "upvotes": 5361, + "upvoterUsernames": [], + "downvotes": 5361, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dc2", + "creator": "rajesh", + "createdAt": 1447918036000, + "text": "
var a = [1,4,2,7,1,5,9,2,4,7,2]\nvar b = {}, c = {};\nvar len = a.length;\nfor(var i=0;i<len;i++){\n  a[i] in c ? delete b[a[i]] : b[a[i]] = true;\n  c[a[i]] = true;\n} \n\n// b contains all unique elements\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dc3", + "creator": "Saravanan Rajaraman", + "createdAt": 1452173626000, + "text": "

Finding unique Array values in simple method

\n\n
function arrUnique(a){\n  var t = [];\n  for(var x = 0; x < a.length; x++){\n    if(t.indexOf(a[x]) == -1)t.push(a[x]);\n  }\n  return t;\n}\narrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32890082fcc3049e928b7", + "creator": "Pooja Thapa", + "createdAt": 1604943883000, + "text": "How can this answer be correct? Expected result for unique array is [5,9] as per the input given [1,4,2,7,1,5,9,2,4,7,2]", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dc5", + "creator": "pgee70", + "createdAt": 1478815802000, + "text": "

I know this has been answered to death already... but...\nno one has mentioned the javascript implementation of linq.\nThen the .distinct() method can be used - and it makes the code super easy to read.

\n\n
var Linq = require('linq-es2015');\nvar distinctValues =  Linq.asEnumerable(testValues)\n            .Select(x)\n            .distinct()\n            .toArray();\n
\n\n

\r\n
\r\n
var testValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];\r\n\r\nvar distinctValues = Enumerable.asEnumerable(testValues)\r\n  .distinct()\r\n  .toArray();\r\n\r\nconsole.log(distinctValues);
\r\n
<script src=\"https://npmcdn.com/linq-es5/dist/linq.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dc4", + "creator": "Vamsi", + "createdAt": 1472736765000, + "text": "

One Liner, Pure JavaScript

\n

With ES6 syntax

\n

list = list.filter((x, i, a) => a.indexOf(x) == i)

\n
x --> item in array\ni --> index of item\na --> array reference, (in this case "list")\n
\n

\"enter

\n

With ES5 syntax

\n
list = list.filter(function (x, i, a) { \n    return a.indexOf(x) == i; \n});\n
\n

Browser Compatibility: IE9+

\n", + "upvotes": 206, + "upvoterUsernames": [], + "downvotes": 95, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dc6", + "creator": "Leonardo", + "createdAt": 1483655404000, + "text": "

For a array of strings:

\n\n
function removeDuplicatesFromArray(arr) {\n  const unique = {};\n  arr.forEach((word) => {\n    unique[word] = 1; // it doesn't really matter what goes here\n  });\n  return Object.keys(unique);\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32890082fcc3049e928bc", + "creator": "Sahith Vibudhi", + "createdAt": 1576064604000, + "text": "why is this answer down voted? It does work as expect!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dc7", + "creator": "bvmCoder", + "createdAt": 1483896799000, + "text": "

\r\n
\r\n
(function() {\r\n    \"use strict\";\r\n\r\n    Array.prototype.unique = function unique() {\r\n        var self = this;\r\n        return self.filter(function(a) {\r\n            var that = this;\r\n            // console.log(that);\r\n            return !that[a] ? that[a] = true : false;\r\n        }, {});\r\n    }\r\n\r\n    var sampleArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\r\n    var distinctArray = sampleArray.unique();\r\n    console.log(distinctArray);\r\n})();
\r\n
Here is the simple way to solve this problem...
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dc8", + "creator": "Yi Feng Xie", + "createdAt": 1484461151000, + "text": "

I used Array#reduce as way to create Array#unique

\n\n

\r\n
\r\n
Array.prototype.unique = function() {\r\n  var object = this.reduce(function(h, v) {\r\n    h[v] = true;\r\n    return h;\r\n  }, {});\r\n  return Object.keys(object);\r\n}\r\n\r\nconsole.log([\"a\", \"b\", \"c\", \"b\", \"c\", \"a\", \"b\"].unique()); // => [\"a\", \"b\", \"c\"]
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32890082fcc3049e928c0", + "creator": "limeandcoconut", + "createdAt": 1498004613000, + "text": "I think most would consider it poor form to modify Array.prototype.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32890082fcc3049e928c2", + "creator": "Yi Feng Xie", + "createdAt": 1498018093000, + "text": "if it's a good way, why not? and I didn't override any method.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dc9", + "creator": "Ben", + "createdAt": 1487888175000, + "text": "

If you have an array of objects, and you want a uniqueBy function, say, by an id field:

\n\n
function uniqueBy(field, arr) {\n   return arr.reduce((acc, curr) => {\n     const exists = acc.find(v => v[field] === curr[field]);\n     return exists ? acc : acc.concat(curr);\n   }, [])\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dca", + "creator": "Max Makhrov", + "createdAt": 1490617492000, + "text": "

I split all answers to 4 possible solutions:

\n\n
    \n
  1. Use object { } to prevent duplicates
  2. \n
  3. Use helper array [ ]
  4. \n
  5. Use filter + indexOf
  6. \n
  7. Bonus! ES6 Sets method.
  8. \n
\n\n

Here's sample codes found in answers:

\n\n

Use object { } to prevent duplicates

\n\n
function uniqueArray1( ar ) {\n  var j = {};\n\n  ar.forEach( function(v) {\n    j[v+ '::' + typeof v] = v;\n  });\n\n  return Object.keys(j).map(function(v){\n    return j[v];\n  });\n} \n
\n\n

Use helper array [ ]

\n\n
function uniqueArray2(arr) {\n    var a = [];\n    for (var i=0, l=arr.length; i<l; i++)\n        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')\n            a.push(arr[i]);\n    return a;\n}\n
\n\n

Use filter + indexOf

\n\n
function uniqueArray3(a) {\n  function onlyUnique(value, index, self) { \n      return self.indexOf(value) === index;\n  }\n\n  // usage\n  var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']\n\n  return unique;\n}\n
\n\n

Use ES6 [...new Set(a)]

\n\n
function uniqueArray4(a) {\n  return [...new Set(a)];\n}\n
\n\n

And I wondered which one is faster. I've made sample Google Sheet to test functions. Note: ECMA 6 is not avaliable in Google Sheets, so I can't test it.

\n\n

Here's the result of tests:\n\"enter

\n\n

I expected to see that code using object { } will win because it uses hash. So I'm glad that tests showed the best results for this algorithm in Chrome and IE. Thanks to @rab for the code.

\n\n

Update 2020

\n\n

Google Script enabled ES6 Engine. Now I tested the last code with Sets and it appeared faster than the object method.

\n", + "upvotes": 554, + "upvoterUsernames": [], + "downvotes": 262, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32890082fcc3049e928c4", + "creator": "Vass", + "createdAt": 1635533101000, + "text": "Makrov, so the uniqueItems = [...new Set(items)] appears to be the fastest and the most succinct of all the approaches?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32890082fcc3049e928c6", + "creator": "Tofandel", + "createdAt": 1651667419000, + "text": "Your solution only handles primitives, it won't work with objects, you'd need to JSON.stringify the v in the hash", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dcb", + "creator": "Ali", + "createdAt": 1504430334000, + "text": "

Making an array of unique arrays, using field[2] as an Id:

\n\n

\r\n
\r\n
const arr = [\r\n  ['497', 'Q0', 'WTX091-B06-138', '0', '1.000000000', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B09-92', '1', '0.866899288', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B09-92', '2', '0.846036819', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B09-57', '3', '0.835025326', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B43-79', '4', '0.765068215', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B43-56', '5', '0.764211464', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B44-448', '6', '0.761701704', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B44-12', '7', '0.761701704', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B49-128', '8', '0.747434800', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B18-17', '9', '0.746724770', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B19-374', '10', '0.733379549', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B19-344', '11', '0.731421782', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B09-92', '12', '0.726450470', 'GROUP001'],\r\n  ['497', 'Q0', 'WTX091-B19-174', '13', '0.712757036', 'GROUP001']\r\n];\r\n\r\n\r\narr.filter((val1, idx1, arr) => !!~val1.indexOf(val1[2]) &&\r\n  !(arr.filter((val2, idx2) => !!~val2.indexOf(val1[2]) &&\r\n    idx2 < idx1).length));\r\n\r\nconsole.log(arr);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dcc", + "creator": "Andrei", + "createdAt": 1509099030000, + "text": "

This is an ES6 function which removes duplicates from an array of objects, filtering by the specified object property

\n\n
function dedupe(arr = [], fnCheck = _ => _) {\n  const set = new Set();\n  let len = arr.length;\n\n  for (let i = 0; i < len; i++) {\n    const primitive = fnCheck(arr[i]);\n    if (set.has(primitive)) {\n      // duplicate, cut it\n      arr.splice(i, 1);\n      i--;\n      len--;\n    } else {\n      // new item, add it\n      set.add(primitive);\n    }\n  }\n\n  return arr;\n}\n\nconst test = [\n    {video:{slug: \"a\"}},\n    {video:{slug: \"a\"}},\n    {video:{slug: \"b\"}},\n    {video:{slug: \"c\"}},\n    {video:{slug: \"c\"}}\n]\nconsole.log(dedupe(test, x => x.video.slug));\n\n// [{video:{slug: \"a\"}}, {video:{slug: \"b\"}}, {video:{slug: \"c\"}}]\n
\n", + "upvotes": 1181, + "upvoterUsernames": [], + "downvotes": 1181, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dcd", + "creator": "daviestar", + "createdAt": 1517502991000, + "text": "

strange this hasn't been suggested before.. to remove duplicates by object key (id below) in an array you can do something like this:

\n\n
const uniqArray = array.filter((obj, idx, arr) => (\n  arr.findIndex((o) => o.id === obj.id) === idx\n)) \n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dcf", + "creator": "NikeshPathania", + "createdAt": 1528716879000, + "text": "

If you're okay with extra dependencies, or you already have one of the libraries in your codebase, you can remove duplicates from an array in place using LoDash (or Underscore).

\n\n

Usage

\n\n

If you don't have it in your codebase already, install it using npm:

\n\n
npm install lodash\n
\n\n

Then use it as follows:

\n\n
import _ from 'lodash';\nlet idArray = _.uniq ([\n    1,\n    2,\n    3,\n    3,\n    3\n]);\nconsole.dir(idArray);\n
\n\n

Out:

\n\n
[ 1, 2, 3 ]\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32891082fcc3049e928c8", + "creator": "Mike", + "createdAt": 1605929716000, + "text": "You can also use lodash to remove objects with duplicate properties from an array: _.uniqWith(objectArray, _.isEqual).", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dce", + "creator": "chinmayan", + "createdAt": 1523245755000, + "text": "

We can do this using ES6 sets:

\n

\r\n
\r\n
var duplicatesArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];\nvar uniqueArray = [...new Set(duplicatesArray)];\n\nconsole.log(uniqueArray); // [1,2,3,4,5]
\r\n
\r\n
\r\n

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd1", + "creator": "BazSTR", + "createdAt": 1531475022000, + "text": "

Do it with lodash and identity lambda function, just define it before use your object

\n\n
const _ = require('lodash');\n...    \n_.uniqBy([{a:1,b:2},{a:1,b:2},{a:1,b:3}], v=>v.a.toString()+v.b.toString())\n_.uniq([1,2,3,3,'a','a','x'])\n
\n\n

and will have:

\n\n
[{a:1,b:2},{a:1,b:3}]\n[1,2,3,'a','x']\n
\n\n

(this is the simplest way )

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd0", + "creator": "Morris S", + "createdAt": 1530038795000, + "text": "

You can use Ramda.js, a functional javascript library to do this:

\n\n

\r\n
\r\n
var unique = R.uniq([1, 2, 1, 3, 1, 4])\r\nconsole.log(unique)
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd2", + "creator": "tjacks3", + "createdAt": 1535035553000, + "text": "

I have a solution that uses es6 reduce and find array helper methods to remove duplicates.

\n\n

\r\n
\r\n
let numbers = [2, 2, 3, 3, 5, 6, 6];\r\n\r\nconst removeDups = array => {\r\n  return array.reduce((acc, inc) => {\r\n    if (!acc.find(i => i === inc)) {\r\n      acc.push(inc);\r\n    }\r\n    return acc;\r\n  }, []);\r\n}\r\n\r\nconsole.log(removeDups(numbers)); /// [2,3,5,6]
\r\n
\r\n
\r\n

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd3", + "creator": "shunryu111", + "createdAt": 1539264632000, + "text": "

I had a slightly different problem where I needed to remove objects with duplicate id properties from an array. this worked.

\n\n

\r\n
\r\n
let objArr = [{\r\n  id: '123'\r\n}, {\r\n  id: '123'\r\n}, {\r\n  id: '456'\r\n}];\r\n\r\nobjArr = objArr.reduce((acc, cur) => [\r\n  ...acc.filter((obj) => obj.id !== cur.id), cur\r\n], []);\r\n\r\nconsole.log(objArr);
\r\n
\r\n
\r\n

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd5", + "creator": "user3591464", + "createdAt": 1543503824000, + "text": "

The Object answer above does not seem to work for me in my use case with Objects.

\n\n

I have modified it as follows:

\n\n
var j = {};\n\nthis.forEach( function(v) {\n   var typ = typeof v;\n   var v = (typ === 'object') ? JSON.stringify(v) : v;\n\n   j[v + '::' + typ] = v;\n});\n\nreturn Object.keys(j).map(function(v){\n  if ( v.indexOf('::object') > -1 ) {\n    return JSON.parse(j[v]);\n  }\n\n  return j[v];\n});\n
\n\n

This seems to now work correctly for objects, arrays, arrays with mixed values, booleans, etc.

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd4", + "creator": "Kamil Kiełczewski", + "createdAt": 1543434333000, + "text": "

Magic

\n
a.filter(e=>!(t[e]=e in t)) \n
\n

O(n) performance (is faster than new Set); we assume your array is in a and t={}. Explanation here (+Jeppe impr.)

\n

\r\n
\r\n
let t, unique= a=> ( t={}, a.filter(e=>!(t[e]=e in t)) );\n\n// \"stand-alone\" version working with global t:\n// a1.filter((t={},e=>!(t[e]=e in t)));\n\n// Test data\nlet a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];\nlet a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];\nlet a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];\n\n// Results\nconsole.log(JSON.stringify( unique(a1) ))\nconsole.log(JSON.stringify( unique(a2) ))\nconsole.log(JSON.stringify( unique(a3) ))
\r\n
\r\n
\r\n

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32891082fcc3049e928ce", + "creator": "Ondřej Želazko", + "createdAt": 1546956970000, + "text": "this look so super cool, that without a solid explanation i fell you're gonna mine bitcoins when i run this", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dd6", + "creator": "Junaid Khan", + "createdAt": 1544616381000, + "text": "

\r\n
\r\n
var numbers = [1, 1, 2, 3, 4, 4];\r\n\r\nfunction unique(dupArray) {\r\n  return dupArray.reduce(function(previous, num) {\r\n\r\n    if (previous.find(function(item) {\r\n        return item == num;\r\n      })) {\r\n      return previous;\r\n    } else {\r\n      previous.push(num);\r\n      return previous;\r\n    }\r\n  }, [])\r\n}\r\n\r\nvar check = unique(numbers);\r\nconsole.log(check);
\r\n
\r\n
\r\n

\n", + "upvotes": 845, + "upvoterUsernames": [], + "downvotes": 845, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd7", + "creator": "LEMUEL ADANE", + "createdAt": 1550236878000, + "text": "

To filter-out undefined and null values because most of the time you do not need them.

\n\n
const uniques = myArray.filter(e => e).filter((e, i, a) => a.indexOf(e) === i);\n
\n\n

or

\n\n
const uniques = [...new Set(myArray.filter(e => e))];\n
\n", + "upvotes": 2415, + "upvoterUsernames": [], + "downvotes": 2415, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd8", + "creator": "Nidhal Ben Tahar", + "createdAt": 1550499603000, + "text": "

Sometimes I need to get unique occurrences from an array of objects. Lodash seems like a nice helper but I don't think filtering an array justifies adding a dependency to a project.

\n\n

Let's assume the comparison of two objects poses on comparing a property, an id for example.

\n\n

const a = [{id: 3}, {id: 4}, {id: 3}, {id: 5}, {id: 5}, {id: 5}];

\n\n

Since we all love one line snippets, here is how it can be done:

\n\n

a.reduce((acc, curr) => acc.find(e => e.id === curr.id) ? acc : [...acc, curr], [])

\n", + "upvotes": 109, + "upvoterUsernames": [], + "downvotes": 109, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dd9", + "creator": "Firas Abd Alrahman", + "createdAt": 1554758989000, + "text": "

This solution should be very fast, and will work in many cases.

\n\n
    \n
  1. Convert the indexed array items to object keys
  2. \n
  3. Use Object.keys function

    \n\n
    var indexArray = [\"hi\",\"welcome\",\"welcome\",1,-9];\nvar keyArray = {};\nindexArray.forEach(function(item){ keyArray[item]=null; });\nvar uniqueArray = Object.keys(keyArray);\n
  4. \n
\n", + "upvotes": 3699, + "upvoterUsernames": [], + "downvotes": 3699, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90ddb", + "creator": "Krishnadas PC", + "createdAt": 1558081138000, + "text": "

Now using sets you can remove duplicates and convert them back to the array.

\n\n

\r\n
\r\n
var names = [\"Mike\",\"Matt\",\"Nancy\", \"Matt\",\"Adam\",\"Jenny\",\"Nancy\",\"Carl\"];\r\n\r\nconsole.log([...new Set(names)])
\r\n
\r\n
\r\n

\n\n

Another solution is to use sort & filter

\n\n

\r\n
\r\n
var names = [\"Mike\",\"Matt\",\"Nancy\", \"Matt\",\"Adam\",\"Jenny\",\"Nancy\",\"Carl\"];\r\nvar namesSorted = names.sort();\r\nconst result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);\r\nconsole.log(result);
\r\n
\r\n
\r\n

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dda", + "creator": "Shridhar Sagari", + "createdAt": 1557482920000, + "text": "

I have a simple example where we can remove objects from array having repeated id in objects,

\n\n
  let data = new Array({id: 1},{id: 2},{id: 3},{id: 1},{id: 3});\n  let unique = [];\n  let tempArr = [];\n  console.log('before', data);\n  data.forEach((value, index) => {\n    if (unique.indexOf(value.id) === -1) {\n      unique.push(value.id);\n    } else {\n      tempArr.push(index);    \n    }\n  });\n  tempArr.reverse();\n  tempArr.forEach(ele => {\n    data.splice(ele, 1);\n  });\n  console.log(data);\n
\n", + "upvotes": 603, + "upvoterUsernames": [], + "downvotes": 603, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90ddc", + "creator": "Nizmox", + "createdAt": 1562135921000, + "text": "

A lot of people have already mentioned using...

\n\n
[...new Set(arr)];\n
\n\n

And this is a great solution, but my preference is a solution that works with .filter. In my opinion filter is a more natural way to get unique values. You're effectively removing duplicates, and removing elements from an array is exactly what filter is meant for. It also lets you chain off of .map, .reduce and other .filter calls. I devised this solution...

\n\n
const unique = () => {\n  let cache;  \n  return (elem, index, array) => {\n    if (!cache) cache = new Set(array);\n    return cache.delete(elem);\n  };\n};\n\nmyArray.filter(unique());\n
\n\n

The caveat is that you need a closure, but I think this is a worthy tradeoff. In terms of performance, it is more performant than the other solutions I have seen posted that use .filter, but worse performing than [...new Set(arr)].

\n\n

See also my github package youneek

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90ddd", + "creator": "Iven Marquardt", + "createdAt": 1563195845000, + "text": "

Deduplication usually requires an equality operator for the given type. However, using an eq function stops us from utilizing a Set to determine duplicates in an efficient manner, because Set falls back to ===. As you know for sure, === doesn't work for reference types. So we're kind if stuck, right?

\n\n

The way out is simply using a transformer function that allows us to transform a (reference) type into something we can actually lookup using a Set. We could use a hash function, for instance, or JSON.stringify the data structure, if it doesn't contain any functions.

\n\n

Often we only need to access a property, which we can then compare instead of the Object's reference.

\n\n

Here are two combinators that meet these requirements:

\n\n

\r\n
\r\n
const dedupeOn = k => xs => {\r\n  const s = new Set();\r\n\r\n  return xs.filter(o =>\r\n    s.has(o[k])\r\n      ? null\r\n      : (s.add(o[k]), o[k]));\r\n};\r\n\r\nconst dedupeBy = f => xs => {\r\n  const s = new Set();\r\n\r\n  return xs.filter(x => {\r\n    const r = f(x);\r\n    \r\n    return s.has(r)\r\n      ? null\r\n      : (s.add(r), x);\r\n  });\r\n};\r\n\r\nconst xs = [{foo: \"a\"}, {foo: \"b\"}, {foo: \"A\"}, {foo: \"b\"}, {foo: \"c\"}];\r\n\r\nconsole.log(\r\n  dedupeOn(\"foo\") (xs)); // [{foo: \"a\"}, {foo: \"b\"}, {foo: \"A\"}, {foo: \"c\"}]\r\n\r\nconsole.log(\r\n  dedupeBy(o => o.foo.toLowerCase()) (xs)); // [{foo: \"a\"}, {foo: \"b\"}, {foo: \"c\"}]
\r\n
\r\n
\r\n

\n\n

With these combinators we're extremely flexible in handling all kinds of deduplication issues. It's not the fastes approach, but the most expressive and most generic one.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90ddf", + "creator": "Dave", + "createdAt": 1567783435000, + "text": "

This has been answered a lot, but it didn't address my particular need.

\n\n

Many answers are like this:

\n\n
a.filter((item, pos, self) => self.indexOf(item) === pos);\n
\n\n

But this doesn't work for arrays of complex objects.

\n\n

Say we have an array like this:

\n\n
const a = [\n { age: 4, name: 'fluffy' },\n { age: 5, name: 'spot' },\n { age: 2, name: 'fluffy' },\n { age: 3, name: 'toby' },\n];\n
\n\n

If we want the objects with unique names, we should use array.prototype.findIndex instead of array.prototype.indexOf:

\n\n
a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32892082fcc3049e928d7", + "creator": "Thanwa Ch.", + "createdAt": 1585848807000, + "text": "Great solution, beware that a new array will return from a function. (it doesn't modify itself)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32892082fcc3049e928d9", + "creator": "Edgar Quintero", + "createdAt": 1608027041000, + "text": "Works will with a complex array of objets", + "upvotes": 148, + "upvoterUsernames": [], + "downvotes": 148, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dde", + "creator": "ifelse.codes", + "createdAt": 1563538701000, + "text": "
[...new Set(duplicates)]\n
\n\n
\n

This is the simplest one and referenced from MDN Web Docs.

\n
\n\n
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]\nconsole.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32892082fcc3049e928db", + "creator": "vsync", + "createdAt": 1655808584000, + "text": "Identical to previous answer which dates a year prior to this one", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90de0", + "creator": "Nikki Luzader", + "createdAt": 1570596340000, + "text": "

You can try this:

\n\n
function removeDuplicates(arr){\n  var temp = arr.sort();\n  for(i = 0; i < temp.length; i++){\n    if(temp[i] == temp[i + 1]){\n      temp.splice(i,1);\n      i--;\n    }\n  }\n  return temp;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de1", + "creator": "noor", + "createdAt": 1571426919000, + "text": "

If you want to only get the unique elements and remove the elements which repeats even once, you can do this:

\n

\r\n
\r\n
let array = [2, 3, 4, 1, 2, 8, 1, 1, 2, 9, 3, 5, 3, 4, 8, 4];\n\nfunction removeDuplicates(inputArray) {\n  let output = [];\n  let countObject = {};\n\n  for (value of array) {\n    countObject[value] = (countObject[value] || 0) + 1;\n  }\n\n  for (key in countObject) {\n    if (countObject[key] === 1) {\n      output.push(key);\n    }\n  }\n\n  return output;\n}\n\nconsole.log(removeDuplicates(array));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de2", + "creator": "WesleyAC", + "createdAt": 1573839926000, + "text": "

You don't need .indexOf() at all; you can do this O(n):

\n
function SelectDistinct(array) {\n    const seenIt = new Set();\n\n    return array.filter(function (val) {\n        if (seenIt.has(val)) { \n            return false;\n        }\n\n        seenIt.add(val);\n\n        return true;\n    });\n}\n\nvar hasDuplicates = [1,2,3,4,5,5,6,7,7];\nconsole.log(SelectDistinct(hasDuplicates)) //[1,2,3,4,5,6,7]\n
\n

If you don't want to use .filter():

\n
function SelectDistinct(array) {\n    const seenIt = new Set();\n    const distinct = [];\n\n    for (let i = 0; i < array.length; i++) {\n        const value = array[i];\n\n        if (!seenIt.has(value)) {\n            seenIt.add(value);\n            distinct.push(value);\n        }\n    }\n    \n    return distinct; \n    /* you could also drop the 'distinct' array and return 'Array.from(seenIt)', which converts the set object to an array */\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de3", + "creator": "Gherciu Gheorghe", + "createdAt": 1579859466000, + "text": "

The easiest way is to transform values into strings to filter also nested objects values.

\n
const uniq = (arg = []) => {\n  const stringifyedArg = arg.map(value => JSON.stringify(value))\n  return arg.filter((value, index, self) => {\n    if (typeof value === 'object')\n      return stringifyedArg.indexOf(JSON.stringify(value)) === index\n    return self.indexOf(value) === index\n  })\n}\n\n    console.log(uniq([21, 'twenty one', 21])) // [21, 'twenty one']\n    console.log(uniq([{ a: 21 }, { a: 'twenty one' }, { a: 21 }])) // [{a: 21}, {a: 'twenty one'}]\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de4", + "creator": "Shreyansh Sharma", + "createdAt": 1582990258000, + "text": "

For an object-based array with some unique id's, I have a simple solution through which you can sort in linear complexity

\n\n
function getUniqueArr(arr){\n    const mapObj = {};\n    arr.forEach(a => { \n       mapObj[a.id] = a\n    })\n    return Object.values(mapObj);\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de5", + "creator": "Roman", + "createdAt": 1593579285000, + "text": "

The task is to get a unique array from an array consisted of arbitrary types (primitive and non primitive).

\n

The approach based on using new Set(...) is not new. Here it is leveraged by JSON.stringify(...), JSON.parse(...) and [].map method. The advantages are universality (applicability for an array of any types), short ES6 notation and probably performance for this case:

\n

\r\n
\r\n
const dedupExample = [\n    { a: 1 },\n    { a: 1 },\n    [ 1, 2 ],\n    [ 1, 2 ],\n    1,\n    1,\n    '1',\n    '1'\n]\n\nconst getUniqArrDeep = arr => {\n    const arrStr = arr.map(item => JSON.stringify(item))\n    return [...new Set(arrStr)]\n        .map(item => JSON.parse(item))\n}\n\nconsole.info(getUniqArrDeep(dedupExample))\n   /* [ {a: 1}, [1, 2], 1, '1' ] */
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32893082fcc3049e928e1", + "creator": "airtonix", + "createdAt": 1652399929000, + "text": "since you're stringifying and then reparsing, positive performance is definitely not an attribute here.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f32893082fcc3049e928e3", + "creator": "Roman", + "createdAt": 1652413533000, + "text": "What do you mean "positive performance" and "attribute here"? Need some elaboration.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f32893082fcc3049e928e5", + "creator": "Roman", + "createdAt": 1652811713000, + "text": "@airtonix, It is true, we need to take performance into an account. Some cases are like "Tough times call for tough decisions" though :-)", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90de6", + "creator": "Didier68", + "createdAt": 1593680131000, + "text": "

in my solution, I sort data before filtering :

\n
const uniqSortedArray = dataArray.sort().filter((v, idx, t) => idx==0 || v != t[idx-1]); \n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de8", + "creator": "nkitku", + "createdAt": 1603394346000, + "text": "

Finding unique in Array of objects using One Liner

\n
const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));\n// f -> should must return string because it will be use as key\n\nconst data = [\n  { comment: "abc", forItem: 1, inModule: 1 },\n  { comment: "abc", forItem: 1, inModule: 1 },\n  { comment: "xyz", forItem: 1, inModule: 2 },\n  { comment: "xyz", forItem: 1, inModule: 2 },\n];\n\nuniqueBy(data, (x) => x.forItem +'-'+ x.inModule); // find unique by item with module\n// output\n// [\n//   { comment: "abc", forItem: 1, inModule: 1 },\n//   { comment: "xyz", forItem: 1, inModule: 2 },\n// ];\n\n// can also use for strings and number or other primitive values\n\nuniqueBy([1, 2, 2, 1], (v) => v); // [1, 2]\nuniqueBy(["a", "b", "a"], (v) => v); // ['a', 'b']\n\nuniqueBy(\n  [\n    { id: 1, name: "abc" },\n    { id: 2, name: "xyz" },\n    { id: 1, name: "abc" },\n  ],\n  (v) => v.id\n);\n// output\n// [\n//   { id: 1, name: "abc" },\n//   { id: 2, name: "xyz" },\n// ];\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32893082fcc3049e928e7", + "creator": "Danish", + "createdAt": 1622474415000, + "text": "use can use uniqBy as well instead of uniqueBy", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90de7", + "creator": "vsync", + "createdAt": 1601755061000, + "text": "

After looking into all the 90+ answers here, I saw there is room for one more:

\n

Array.includes has a very handy second-parameter: "fromIndex", so by using it, every iteration of the filter callback method will search the array, starting from [current index] + 1 which guarantees not to include currently filtered item in the lookup and also saves time.

\n
\n

Note - this solution does not retain the order, as it removed duplicated items from left to right, but it wins the Set trick if the Array is a collection of Objects.

\n
\n

\r\n
\r\n
//                🚩              🚩 🚩\nvar list = [0,1,2,2,3,'a','b',4,5,2,'a']\n\nconsole.log( \n  list.filter((v,i) => !list.includes(v,i+1))\n)\n\n// [0,1,3,\"b\",4,5,2,\"a\"]
\r\n
\r\n
\r\n

\n

Explanation:

\n

For example, lets assume the filter function is currently iterating at index 2) and the value at that index happens to be 2. The section of the array that is then scanned for duplicates (includes method) is everything after index 2 (i+1):

\n
           👇                    👇\n[0, 1, 2,   2 ,3 ,'a', 'b', 4, 5, 2, 'a']\n       👆   |---------------------------|\n
\n

And since the currently filtered item's value 2 is included in the rest of the array, it will be filtered out, because of the leading exclamation mark which negates the filter rule.

\n
\n

If order is important, use this method:

\n

\r\n
\r\n
//                🚩              🚩 🚩\nvar list = [0,1,2,2,3,'a','b',4,5,2,'a']\n\nconsole.log( \n  // Initialize with empty array and fill with non-duplicates\n  list.reduce((acc, v) => (!acc.includes(v) && acc.push(v), acc), [])\n)\n\n// [0,1,2,3,\"a\",\"b\",4,5]
\r\n
\r\n
\r\n

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90de9", + "creator": "user239558", + "createdAt": 1613858580000, + "text": "

Here is an almost one-liner that is O(n), keeps the first element, and where you can keep the field you are uniq'ing on separate.

\n

This is a pretty common technique in functional programming - you use reduce to build up an array that you return. Since we build the array like this, we guarantee that we get a stable ordering, unlike the [...new Set(array)] approach. We still use a Set to ensure we don't have duplicates, so our accumulator contains both a Set and the array we are building.

\n

\r\n
\r\n
const removeDuplicates = (arr) =>\n  arr.reduce(\n    ([set, acc], item) => set.has(item) ? [set, acc] : [set.add(item), (acc.push(item), acc)],\n    [new Set(), []]\n  )[1]
\r\n
\r\n
\r\n

\n

The above will work for simple values, but not for objects, similarly to how [...new Set(array)] breaks down. If the items are objects that contain an id property, you'd do:

\n

\r\n
\r\n
const removeDuplicates = (arr) =>\n  arr.reduce(\n    ([set, acc], item) => set.has(item.id) ? [set, acc] : [set.add(item.id), (acc.push(item), acc)],\n    [new Set(), []]\n  )[1]
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dea", + "creator": "moshfiqrony", + "createdAt": 1621936964000, + "text": "

For removing the duplicates there could be 2 situations.\nfirst, all the data are not objects, secondly all the data are objects.

\n

If all the data are any kind of primitive data type like int, float, string etc then you can follow this one

\n
const uniqueArray = [...new Set(oldArray)]\n
\n

But suppose your array consist JS objects like bellow

\n
{\n    id: 1,\n    name: 'rony',\n    email: 'rony@example.com'\n}\n
\n

then to get all the unique objects you can follow this

\n
let uniqueIds = [];\nconst uniqueUsers = oldArray.filter(item => {\n    if(uniqueIds.includes(item.id)){\n        return false;\n    }else{\n        uniqueIds.push(item.id);\n        return true;\n    }\n})\n
\n

You can also use this method to make any kind of array to make unique. Just keep the tracking key on the uniqueIds array.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dec", + "creator": "Ylama", + "createdAt": 1629202410000, + "text": "

Using mongoose I had an array of ObjectIds to work with.

\n

I had a array/list of Object Ids to work with which first needed to be set to an string and after the unique set, amended back to Object Ids.

\n

\r\n
\r\n
var mongoose = require('mongoose')\n\nvar ids = [ObjectId(\"1\"), ObjectId(\"2\"), ObjectId(\"3\")]\n\nvar toStringIds = ids.map(e => '' + e)\nlet uniqueIds = [...new Set(toStringIds)]\nuniqueIds = uniqueIds.map(b => mongoose.Types.ObjectId(b))\n\n\nconsole.log(\"uniqueIds :\", uniqueIds)
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90deb", + "creator": "Ballpin", + "createdAt": 1628616506000, + "text": "

For my part this was the easiest solution

\n

\r\n
\r\n
// A way to check if the arrays are equal\nconst a = ['A', 'B', 'C'].sort().toString()\nconst b = ['A', 'C', 'B'].sort().toString()\n\nconsole.log(a === b); // true\n\n\n// Test Case\nconst data = [\n  { group: 'A', name: 'SD' },\n  { group: 'B', name: 'FI' },\n  { group: 'A', name: 'SD' },\n  { group: 'B', name: 'CO' }\n];\n\n// Return a new Array without dublocates\nfunction unique(data) {\n  return data.reduce(function (accumulator, currentValue) {\n    // Convert to string in order to check if they are the same value.\n    const currentKeys = Object.keys(currentValue).sort().toString();\n    const currentValues = Object.values(currentValue).sort().toString();\n\n    let hasObject = false\n    \n    for (const obj of accumulator) {\n      // Convert keys and values into strings so we can\n      // see if they are equal with the currentValue\n      const keys = Object.keys(obj).sort().toString();\n      const values = Object.values(obj).sort().toString();\n      // Check if keys and values are equal\n      if (keys === currentKeys && values === currentValues) {\n        hasObject = true\n      }\n    }\n\n    // Push the object if it does not exist already.\n    if (!hasObject) {\n      accumulator.push(currentValue)\n    }\n\n    return accumulator\n  }, []);\n}\n\n// Run Test Case\nconsole.log(unique(data)); // [ { group: 'A', name: 'SD' }, { group: 'B', name: 'FI' }, { group: 'B', name: 'CO' } ]
\r\n
\r\n
\r\n

\n", + "upvotes": 1940, + "upvoterUsernames": [], + "downvotes": 1940, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dee", + "creator": "Giacomo Casadei", + "createdAt": 1632074081000, + "text": "

You can simlply use the built-in functions Array.prototype.filter() and Array.prototype.indexOf()

\n

array.filter((x, y) => array.indexOf(x) == y)

\n

\r\n
\r\n
var arr = [1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 8, 9, 6, 9];\n\nvar newarr = arr.filter((x, y) => arr.indexOf(x) == y);\n\nconsole.log(newarr);
\r\n
\r\n
\r\n

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90ded", + "creator": "Artisan72", + "createdAt": 1629286667000, + "text": "

I would sort the array, then all duplicates are neighbours.\nThen walk once through the array and eliminate all duplicates.

\n
function getUniques(array) {\n  var l = array.length\n  if(l > 1) {\n    // get a cloned copy and sort it\n    array = [...array].sort();\n    var i = 1, j = 0;\n    while(i < l) {\n      if(array[i] != array[j]) {\n        array[++j] = array[i];\n      }\n      i++;\n    }\n    array.length = j + 1;\n  }\n  return array;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90def", + "creator": "lonix", + "createdAt": 1633977427000, + "text": "

As explained already, [...new Set(values)] is the best option, if that's available to you.

\n

Otherwise, here's a one-liner that doesn't iterate the array for every index:

\n
values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);\n
\n

That simply compares each value to the one before it. The result will be sorted.

\n

Example:\n

\r\n
\r\n
let values = [ 1, 2, 3, 3, 4, 5, 5, 5, 4, 4, 4, 5, 1, 1, 1, 3, 3 ];\nlet unique = values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);\nconsole.log(unique);
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32894082fcc3049e928ee", + "creator": "Ponciusz", + "createdAt": 1636730035000, + "text": "Not working when multiple same values are in row", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32894082fcc3049e928f0", + "creator": "lonix", + "createdAt": 1636737205000, + "text": "Added code snippet, seems to work.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90df0", + "creator": "Muhammad Atif Akram", + "createdAt": 1635838577000, + "text": "

\r\n
\r\n
  var myArray = [\"a\",2, \"a\", 2, \"b\", \"1\"];\n  const uniques = [];\n  myArray.forEach((t) => !uniques.includes(t) && uniques.push(t));\n  console.log(uniques);
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df2", + "creator": "Mike Reiche", + "createdAt": 1636891549000, + "text": "

Here is another approach using comparators (I care more about clean code than performance):

\n
const list = [\n    {name: "Meier"},\n    {name: "Hans"},\n    {name: "Meier"},\n]\nconst compare = (a, b) => a.name.localeCompare(b.name);\nconst uniqueNames = list.makeUnique(compare);\nuniqueNames.pushIfAbsent({name: "Hans"}, compare);\n
\n

Prototype declaration:

\n
declare global {\n    interface Array<T>  {\n        pushIfAbsent(item: T, compare:(a:T, b:T)=>number): number;\n    }\n    interface Array<T>  {\n        makeUnique(compare:(a:T, b:T)=>number): Array<T>;\n    }\n}\nArray.prototype.pushIfAbsent = function <T>(this:T[], item:T, compare:(a:T, b:T)=>number) {\n    if (!this.find(existing => compare(existing, item)===0)) {\n        return this.push(item)\n    } else {\n        return this.length;\n    }\n}\nArray.prototype.makeUnique = function <T>(this:T[], compare:(a:T, b:T)=>number) {\n    return this.filter((existing, index, self) => self.findIndex(item => compare(existing, item) == 0) == index);\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df1", + "creator": "Re_p1ay", + "createdAt": 1636403961000, + "text": "

If you want to remove duplicates, return the whole objects and want to use ES6 Set and Map syntax, and also run only one loop, you can try this, to get unique ids:

\n

\r\n
\r\n
const collection = [{id:3, name: \"A\"}, {id:3, name: \"B\"}, {id:4, name: \"C\"}, {id:5, name: \"D\"}]\n\nfunction returnUnique(itemsCollection){\n  const itemsMap = new Map();\n\n  itemsCollection.forEach(item => {\n    if(itemsMap.size === 0){\n      itemsMap.set(item.id, item)       \n    }else if(!itemsMap.has(item.id)){\n      itemsMap.set(item.id, item)\n    }\n  });\n  \n    return [...new Set(itemsMap.values())];\n }\n\nconsole.log(returnUnique(collection));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df3", + "creator": "vitaly-t", + "createdAt": 1637108666000, + "text": "

A modern approach that's extensible, fast, efficient and easy to read, using iter-ops library:

\n
import {pipe, distinct} from 'iter-ops';\n\nconst input = [1, 1, 2, 2, 2, 3]; // our data\n\nconst i = pipe(input, distinct()); // distinct iterable\n\nconsole.log([...i]); //=> [1, 2, 3]\n
\n

And if your input is an array of objects, you will just provide a key selector for the distinct operator.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df4", + "creator": "rop", + "createdAt": 1638779573000, + "text": "

Not really a direct literal answer to the original question, because I preferred to have the duplicate values never in the array in the first place. So here's my UniqueArray:

\n
class UniqueArray extends Array {\n    constructor(...args) {\n        super(...new Set(args));\n    }\n    push(...args) {\n        for (const a of args) if (!this.includes(a)) super.push(a);\n        return this.length;\n    }\n    unshift(...args) {\n        for (const a of args.reverse()) if (!this.includes(a)) super.unshift(a);\n        return this.length;\n    }\n    concat(...args) {\n        var r = new UniqueArray(...this);\n        for (const a of args) r.push(...a);\n        return r;\n    }\n}\n
\n
> a = new UniqueArray(1,2,3,1,2,4,5,1)\nUniqueArray(5) [ 1, 2, 3, 4, 5 ]\n> a.push(1,4,6)\n6\n> a\nUniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]\n> a.unshift(1)\n6\n> a\nUniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]\n> a.unshift(0)\n7\n> a\nUniqueArray(7) [\n  0, 1, 2, 3,\n  4, 5, 6\n]\n> a.concat(2,3,7)\nUniqueArray(8) [\n  0, 1, 2, 3,\n  4, 5, 6, 7\n]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df5", + "creator": "Kalana Weerarathne", + "createdAt": 1639450045000, + "text": "

\r\n
\r\n
let ar = [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 2, 1];\nlet unique = ar.filter((value, index) => {\n        return ar.indexOf(value) == index;\n      });\nconsole.log(unique);
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df6", + "creator": "Surbhi Dighe", + "createdAt": 1640321888000, + "text": "

Using ES6 new Set

\n

\r\n
\r\n
var array = [3,7,5,3,2,5,2,7];\nvar unique_array = [...new Set(array)];\nconsole.log(unique_array);    // output = [3,7,5,2]
\r\n
\r\n
\r\n

\n

Using For Loop

\n

\r\n
\r\n
var array = [3,7,5,3,2,5,2,7];\n\nfor(var i=0;i<array.length;i++) {\n    for(var j=i+1;j<array.length;j++) {\n        if(array[i]===array[j]) {\n            array.splice(j,1);\n        }\n    }\n}\nconsole.log(array); // output = [3,7,5,2]
\r\n
\r\n
\r\n

\n", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df7", + "creator": "mdmundo", + "createdAt": 1643207995000, + "text": "

Remove duplicates using Set.

\n
// Array with duplicates⤵️\nconst withDuplicates = [2, 2, 5, 5, 1, 1, 2, 2, 3, 3];\n// Get new array without duplicates by using Set\n// [2, 5, 1, 3]\nconst withoutDuplicates = Array.from(new Set(arrayWithDuplicates));\n
\n

A shorter version, as follows:

\n
const withoutDuplicates = [...new Set(arrayWithDuplicates)];\n
\n", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 42, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32895082fcc3049e928f8", + "creator": "vsync", + "createdAt": 1655808658000, + "text": "Completely duplicated answer for ones other ones given years before this one.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90df8", + "creator": "Vivekraj K R", + "createdAt": 1645841071000, + "text": "

There's already bunch of great answers. Here's my approach.

\n
var removeDuplicates = function(nums) {\n    let filteredArr = [];\n    nums.forEach((item) => {\n        if(!filteredArr.includes(item)) {\n            filteredArr.push(item);\n        }\n    })\n\n  return filteredArr;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90df9", + "creator": "rotarydial", + "createdAt": 1646775463000, + "text": "

For an array of tuples, I'll throw things into a Map and let it do the work. With this approach, you have to be mindful about the key you want to be using:

\n
const arrayOfArraysWithDuplicates = [\n    [1, 'AB'],\n    [2, 'CD'],\n    [3, 'EF'],\n    [1, 'AB'],\n    [2, 'CD'],\n    [3, 'EF'],\n    [3, 'GH'],\n]\n\nconst uniqueByFirstValue = new Map();\nconst uniqueBySecondValue = new Map();\n\narrayOfArraysWithDuplicates.forEach((item) => {\n    uniqueByFirstValue.set(item[0], item[1]);\n    uniqueBySecondValue.set(item[1], item[0]);\n});\n\nlet uniqueList = Array.from( uniqueByFirstValue, ( [ value, name ] ) => ( [value, name] ) );\n\nconsole.log('Unique by first value:');\nconsole.log(uniqueList);\n\nuniqueList = Array.from( uniqueBySecondValue, ( [ value, name ] ) => ( [value, name] ) );\n\nconsole.log('Unique by second value:');\nconsole.log(uniqueList);\n
\n

Output:

\n
Unique by first value:\n[ [ 1, 'AB' ], [ 2, 'CD' ], [ 3, 'GH' ] ]\n\nUnique by second value:\n[ [ 'AB', 1 ], [ 'CD', 2 ], [ 'EF', 3 ], [ 'GH', 3 ] ]\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dfa", + "creator": "Yadab Sd", + "createdAt": 1652383297000, + "text": "

Always remember,\nThe build-in methods are easy to use. But keep in mind that they have a complexity.

\n

The basic logic is best. There is no hidden complexity.

\n
let list = [1, 1, 2, 100, 2] // your array\nlet check = {}\nlist = list.filter(item => {\n    if(!check[item]) {\n        check[item] = true\n        return true;\n    }\n})\n
\n
\n

or use, let check = [] if you need future traverse to checked items (waste of memory though)

\n
\n", + "upvotes": 439, + "upvoterUsernames": [], + "downvotes": 439, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32895082fcc3049e928fb", + "creator": "Aakash", + "createdAt": 1652519863000, + "text": "Maybe filter() was not released at time when this question was asked.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32895082fcc3049e928fd", + "creator": "coder9833idls", + "createdAt": 1653725666000, + "text": "Does this return the new array without duplicates?", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [] + }, + { + "_id": "62f32895082fcc3049e928ff", + "creator": "Yadab Sd", + "createdAt": 1653803121000, + "text": "Yes @coder9833idls", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dfb", + "creator": "coder9833idls", + "createdAt": 1653725573000, + "text": "

ES2016 .includes() One Method Simple Answer:

\n
var arr = [1,5,2,4,1,6]\nfunction getOrigs(arr) {\n  let unique = []\n  arr && arr.forEach(number => {\n    !unique.includes(number) && unique.push(number)\n    if (number === arr[arr.length - 1]) {\n      console.log('unique: ', unique)\n    }\n  })\n}\ngetOrigs(arr)\n
\n

Use this instead:

\n\n
var arr = [1,5,2,4,1,6];\nfunction getOrigs(arr) {let unique = []; \n  arr && arr.forEach(number => !unique.includes(number) && unique.push(number) && ((number === arr[arr.length - 1]) && console.log('unique: ', unique)))};\ngetOrigs(arr);\n
\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90d73", + "creator": "varad_s", + "createdAt": 1614003000000, + "text": "using the ramda R.uniq(list) solves this. ramdajs.com/docs/#uniq", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d74", + "creator": "Lukas Liesis", + "createdAt": 1640263155000, + "text": "my favorite way is let uniqueArray = [...new Set(originalArray)]; 🚀", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d75", + "creator": "Lukas Liesis", + "createdAt": 1642430660000, + "text": "@user6316468 Please note the highlight of the code part. Rocket emoji has nothing to do with sample code. Sorry to confuse though.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2632098, + "uvac": 2632182 + } + }, + { + "_id": "62f321bb082fcc3049e8fed0", + "title": "What is the difference between call and apply?", + "title-lowercase": "what is the difference between call and apply?", + "creator": "John Duff", + "createdAt": 1262289385000, + "status": "open", + "text": "

What is the difference between using Function.prototype.apply() and Function.prototype.call() to invoke a function?

\n
var func = function() {\n  alert('hello!');\n};\n
\n

func.apply(); vs func.call();

\n

Are there performance differences between the two aforementioned methods? When is it best to use call over apply and vice versa?

\n", + "upvotes": 5423, + "upvoterUsernames": [], + "downvotes": 2064, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 781370, + "answers": 23, + "answerItems": [ + { + "_id": "62f321c2082fcc3049e90688", + "creator": "notnoop", + "createdAt": 1262289576000, + "text": "

K. Scott Allen has a nice writeup on the matter.

\n\n

Basically, they differ on how they handle function arguments.

\n\n
\n

The apply() method is identical to call(), except apply() requires an array as the second parameter. The array represents the arguments for the target method.\"

\n
\n\n

So:

\n\n
// assuming you have f\nfunction f(message) { ... }\nf.call(receiver, \"test\");\nf.apply(receiver, [\"test\"]);\n
\n", + "upvotes": 411, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254e082fcc3049e91c3c", + "creator": "angry kiwi", + "createdAt": 1311912071000, + "text": "the second parameter of apply() and call() is optional, not required.", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c3e", + "creator": "Ikrom", + "createdAt": 1370410439000, + "text": "First parameter is not required too.", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c3f", + "creator": "iamcastelli", + "createdAt": 1580194685000, + "text": "@Ikrom, the first parameter is not required for call but a requirement for apply", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90689", + "creator": "flatline", + "createdAt": 1262289642000, + "text": "

The difference is that apply lets you invoke the function with arguments as an array; call requires the parameters be listed explicitly. A useful mnemonic is "A for array and C for comma."

\n

See MDN's documentation on apply and call.

\n

Pseudo syntax:

\n

theFunction.apply(valueForThis, arrayOfArgs)

\n

theFunction.call(valueForThis, arg1, arg2, ...)

\n

There is also, as of ES6, the possibility to spread the array for use with the call function, you can see the compatibilities here.

\n

Sample code:

\n

\r\n
\r\n
function theFunction(name, profession) {\n    console.log(\"My name is \" + name + \" and I am a \" + profession +\".\");\n}\ntheFunction(\"John\", \"fireman\");\ntheFunction.apply(undefined, [\"Susan\", \"school teacher\"]);\ntheFunction.call(undefined, \"Claude\", \"mathematician\");\ntheFunction.call(undefined, ...[\"Matthew\", \"physicist\"]); // used with the spread operator
\r\n
\r\n
\r\n

\n", + "upvotes": 6052, + "upvoterUsernames": [], + "downvotes": 2147, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3254e082fcc3049e91c43", + "creator": "devios1", + "createdAt": 1308792671000, + "text": "Are they both supported by the majority of browsers? I seem to remember reading that call() was more of an IE thing.", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 84, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c45", + "creator": "Kevin Schroeder", + "createdAt": 1343492318000, + "text": "One thing to add is that the args must be a numerical array ([]). Associative arrays ({}) will not work.", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c47", + "creator": "Martijn", + "createdAt": 1357831199000, + "text": "@KevinSchroeder: In javascript parlance, [] is called an array, {} is called an object.", + "upvotes": 521, + "upvoterUsernames": [], + "downvotes": 151, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c48", + "creator": "Álvaro González", + "createdAt": 1495616739000, + "text": "The mnemonic alone is worth the answer. I don't think I'll need to look it up again!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9068a", + "creator": "Matthew Crumley", + "createdAt": 1262296240000, + "text": "

To answer the part about when to use each function, use apply if you don't know the number of arguments you will be passing, or if they are already in an array or array-like object (like the arguments object to forward your own arguments. Use call otherwise, since there's no need to wrap the arguments in an array.

\n\n
f.call(thisObject, a, b, c); // Fixed number of arguments\n\nf.apply(thisObject, arguments); // Forward this function's arguments\n\nvar args = [];\nwhile (...) {\n    args.push(some_value());\n}\nf.apply(thisObject, args); // Unknown number of arguments\n
\n\n

When I'm not passing any arguments (like your example), I prefer call since I'm calling the function. apply would imply you are applying the function to the (non-existent) arguments.

\n\n

There shouldn't be any performance differences, except maybe if you use apply and wrap the arguments in an array (e.g. f.apply(thisObject, [a, b, c]) instead of f.call(thisObject, a, b, c)). I haven't tested it, so there could be differences, but it would be very browser specific. It's likely that call is faster if you don't already have the arguments in an array and apply is faster if you do.

\n", + "upvotes": 194, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9068c", + "creator": "user669677", + "createdAt": 1373021764000, + "text": "

I'd like to show an example, where the 'valueForThis' argument is used:

\n\n
Array.prototype.push = function(element) {\n   /*\n   Native code*, that uses 'this'       \n   this.put(element);\n   */\n}\nvar array = [];\narray.push(1);\narray.push.apply(array,[2,3]);\nArray.prototype.push.apply(array,[4,5]);\narray.push.call(array,6,7);\nArray.prototype.push.call(array,8,9);\n//[1, 2, 3, 4, 5, 6, 7, 8, 9] \n
\n\n

**details: http://es5.github.io/#x15.4.4.7*

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9068b", + "creator": "kmatheny", + "createdAt": 1320687405000, + "text": "

While this is an old topic, I just wanted to point out that .call is slightly faster than .apply. I can't tell you exactly why.

\n\n

See jsPerf, http://jsperf.com/test-call-vs-apply/3

\n\n
\n\n

[UPDATE!]

\n\n

Douglas Crockford mentions briefly the difference between the two, which may help explain the performance difference... http://youtu.be/ya4UHuXNygM?t=15m52s

\n\n

Apply takes an array of arguments, while Call takes zero or more individual parameters! Ah hah!

\n\n

.apply(this, [...])

\n\n

.call(this, param1, param2, param3, param4...)

\n", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254e082fcc3049e91c4c", + "creator": "Eric Hodonsky", + "createdAt": 1330633804000, + "text": "This depends on what the function does with the parameters/array, if it doesn't need to process the array, does it take less time?", + "upvotes": 375, + "upvoterUsernames": [], + "downvotes": 375, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c4e", + "creator": "Vincent McNabb", + "createdAt": 1381194655000, + "text": "@JoshMc That would be very browser specific. In IE 11, I'm getting apply going twice as fast as call.", + "upvotes": 7304, + "upvoterUsernames": [], + "downvotes": 7304, + "downvoterUsernames": [] + }, + { + "_id": "62f3254e082fcc3049e91c50", + "creator": "Gary", + "createdAt": 1500808943000, + "text": "Thank you for sharing the test and video", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e9068e", + "creator": "Praveen D", + "createdAt": 1383738646000, + "text": "

We can differentiate call and apply methods as below

\n\n

CALL : A function with argument provide individually.\nIf you know the arguments to be passed or there are no argument to pass you can use call.

\n\n

APPLY : Call a function with argument provided as an array. You can use apply if you don't know how many argument are going to pass to the function.

\n\n

There is a advantage of using apply over call, we don't need to change the number of argument only we can change a array that is passed.

\n\n

There is not big difference in performance. But we can say call is bit faster as compare to apply because an array need to evaluate in apply method.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90690", + "creator": "Mark Karwowski", + "createdAt": 1390327882000, + "text": "

Call() takes comma-separated arguments, ex:

\n\n

.call(scope, arg1, arg2, arg3)

\n\n

and apply() takes an array of arguments, ex:

\n\n

.apply(scope, [arg1, arg2, arg3])

\n\n

here are few more usage examples: \nhttp://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9068d", + "creator": "Joe", + "createdAt": 1378301819000, + "text": "

Here's a good mnemonic. Apply uses Arrays and Always takes one or two Arguments. When you use Call you have to Count the number of arguments.

\n", + "upvotes": 230, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9068f", + "creator": "Dan", + "createdAt": 1386165509000, + "text": "

Here's a small-ish post, I wrote on this:

\n\n

http://sizeableidea.com/call-versus-apply-javascript/

\n\n
var obj1 = { which : \"obj1\" },\nobj2 = { which : \"obj2\" };\n\nfunction execute(arg1, arg2){\n    console.log(this.which, arg1, arg2);\n}\n\n//using call\nexecute.call(obj1, \"dan\", \"stanhope\");\n//output: obj1 dan stanhope\n\n//using apply\nexecute.apply(obj2, [\"dan\", \"stanhope\"]);\n//output: obj2 dan stanhope\n\n//using old school\nexecute(\"dan\", \"stanhope\");\n//output: undefined \"dan\" \"stanhope\"\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90693", + "creator": "Dhana Krishnasamy", + "createdAt": 1423764605000, + "text": "

Even though call and apply achive the same thing, I think there is atleast one place where you cannot use call but can only use apply. That is when you want to support inheritance and want to call the constructor.

\n\n

Here is a function allows you to create classes which also supports creating classes by extending other classes.

\n\n
function makeClass( properties ) {\n    var ctor = properties['constructor'] || function(){}\n    var Super = properties['extends'];\n    var Class = function () {\n                 // Here 'call' cannot work, only 'apply' can!!!\n                 if(Super)\n                    Super.apply(this,arguments);  \n                 ctor.apply(this,arguments);\n                }\n     if(Super){\n        Class.prototype = Object.create( Super.prototype );\n        Class.prototype.constructor = Class;\n     }\n     Object.keys(properties).forEach( function(prop) {\n           if(prop!=='constructor' && prop!=='extends')\n            Class.prototype[prop] = properties[prop];\n     });\n   return Class; \n}\n\n//Usage\nvar Car = makeClass({\n             constructor: function(name){\n                         this.name=name;\n                        },\n             yourName: function() {\n                     return this.name;\n                   }\n          });\n//We have a Car class now\n var carInstance=new Car('Fiat');\ncarInstance.youName();// ReturnsFiat\n\nvar SuperCar = makeClass({\n               constructor: function(ignore,power){\n                     this.power=power;\n                  },\n               extends:Car,\n               yourPower: function() {\n                    return this.power;\n                  }\n              });\n//We have a SuperCar class now, which is subclass of Car\nvar superCar=new SuperCar('BMW xy',2.6);\nsuperCar.yourName();//Returns BMW xy\nsuperCar.yourPower();// Returns 2.6\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254f082fcc3049e91c55", + "creator": "jhliberty", + "createdAt": 1508958600000, + "text": "I believe call would work there with the spread operator as described in the selected answer. Unless I'm missing something.", + "upvotes": 157, + "upvoterUsernames": [], + "downvotes": 157, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90691", + "creator": "tjacks3", + "createdAt": 1393356711000, + "text": "

It is useful at times for one object to borrow the function of another object, meaning that the borrowing object simply executes the lent function as if it were its own.

\n\n

A small code example:

\n\n
var friend = {\n    car: false,\n    lendCar: function ( canLend ){\n      this.car = canLend;\n }\n\n}; \n\nvar me = {\n    car: false,\n    gotCar: function(){\n      return this.car === true;\n  }\n};\n\nconsole.log(me.gotCar()); // false\n\nfriend.lendCar.call(me, true); \n\nconsole.log(me.gotCar()); // true\n\nfriend.lendCar.apply(me, [false]);\n\nconsole.log(me.gotCar()); // false\n
\n\n

These methods are very useful for giving objects temporary functionality.

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90692", + "creator": "Rakesh Kumar", + "createdAt": 1393563468000, + "text": "

Fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments.

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90694", + "creator": "Mahesh", + "createdAt": 1427787125000, + "text": "

Another example with Call, Apply and Bind.\nThe difference between Call and Apply is evident, but Bind works like this:

\n\n
    \n
  1. Bind returns an instance of a function that can be executed
  2. \n
  3. First Parameter is 'this'
  4. \n
  5. Second parameter is a Comma separated list of arguments (like Call)
  6. \n
\n\n

}

\n\n
function Person(name) {\n    this.name = name; \n}\nPerson.prototype.getName = function(a,b) { \n     return this.name + \" \" + a + \" \" + b; \n}\n\nvar reader = new Person('John Smith');\n\nreader.getName = function() {\n   // Apply and Call executes the function and returns value\n\n   // Also notice the different ways of extracting 'getName' prototype\n   var baseName = Object.getPrototypeOf(this).getName.apply(this,[\"is a\", \"boy\"]);\n   console.log(\"Apply: \" + baseName);\n\n   var baseName = Object.getPrototypeOf(reader).getName.call(this, \"is a\", \"boy\"); \n   console.log(\"Call: \" + baseName);\n\n   // Bind returns function which can be invoked\n   var baseName = Person.prototype.getName.bind(this, \"is a\", \"boy\"); \n   console.log(\"Bind: \" + baseName());\n}\n\nreader.getName();\n/* Output\nApply: John Smith is a boy\nCall: John Smith is a boy\nBind: John Smith is a boy\n*/\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90696", + "creator": "Raghavendra", + "createdAt": 1448354162000, + "text": "

Call and apply both are used to force the this value when a function is executed. The only difference is that call takes n+1 arguments where 1 is this and 'n' arguments. apply takes only two arguments, one is this the other is argument array.

\n\n

The advantage I see in apply over call is that we can easily delegate a function call to other function without much effort;

\n\n
function sayHello() {\n  console.log(this, arguments);\n}\n\nfunction hello() {\n  sayHello.apply(this, arguments);\n}\n\nvar obj = {name: 'my name'}\nhello.call(obj, 'some', 'arguments');\n
\n\n

Observe how easily we delegated hello to sayHello using apply, but with call this is very difficult to achieve.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90695", + "creator": "venkat7668", + "createdAt": 1438582516000, + "text": "

Difference between these to methods are, how you want to pass the parameters.

\n\n

“A for array and C for comma” is a handy mnemonic.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3254f082fcc3049e91c5c", + "creator": "Kyll", + "createdAt": 1441622307000, + "text": "What does this answer provide that is not already well-provided in other answers?", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c2082fcc3049e90697", + "creator": "John Slegers", + "createdAt": 1453084020000, + "text": "

From the MDN docs on Function.prototype.apply() :

\n\n
\n

The apply() method calls a function with a given this value and\n arguments provided as an array (or an array-like object).

\n \n

Syntax

\n\n
fun.apply(thisArg, [argsArray])\n
\n
\n\n

From the MDN docs on Function.prototype.call() :

\n\n
\n

The call() method calls a function with a given this value and arguments provided individually.

\n \n

Syntax

\n\n
fun.call(thisArg[, arg1[, arg2[, ...]]])\n
\n
\n\n

From Function.apply and Function.call in JavaScript :

\n\n
\n

The apply() method is identical to call(), except apply() requires an\n array as the second parameter. The array represents the arguments for\n the target method.

\n
\n\n
\n\n

Code example :

\n\n

\r\n
\r\n
var doSomething = function() {\r\n    var arr = [];\r\n    for(i in arguments) {\r\n        if(typeof this[arguments[i]] !== 'undefined') {\r\n            arr.push(this[arguments[i]]);\r\n        }\r\n    }\r\n    return arr;\r\n}\r\n\r\nvar output = function(position, obj) {\r\n    document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\\n<br>\\n<br><hr>';\r\n}\r\n\r\noutput(1, doSomething(\r\n    'one',\r\n    'two',\r\n    'two',\r\n    'one'\r\n));\r\n\r\noutput(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [\r\n    'one',\r\n    'two',\r\n    'two',\r\n    'one'\r\n]));\r\n\r\noutput(3, doSomething.call({one : 'Steven', two : 'Jane'},\r\n    'one',\r\n    'two',\r\n    'two',\r\n    'one'\r\n));
\r\n
\r\n
\r\n

\n\n

See also this Fiddle.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9069a", + "creator": "Willem van der Veen", + "createdAt": 1535556582000, + "text": "

Summary:

\n\n

Both call() and apply() are methods which are located on Function.prototype. Therefore they are available on every function object via the prototype chain. Both call() and apply() can execute a function with a specified value of the this.

\n\n

The main difference between call() and apply() is the way you have to pass in arguments into it. In both call() and apply() you pass as a first argument the object you want to be the value as this. The other arguments differ in the following way:

\n\n\n\n

Example:

\n\n

\r\n
\r\n
let obj = {\r\n  val1: 5,\r\n  val2: 10\r\n}\r\n\r\nconst summation = function (val3, val4) {\r\n  return  this.val1 + this.val2 + val3 + val4;\r\n}\r\n\r\nconsole.log(summation.apply(obj, [2 ,3]));\r\n// first we assign we value of this in the first arg\r\n// with apply we have to pass in an array\r\n\r\n\r\nconsole.log(summation.call(obj, 2, 3));\r\n// with call we can pass in each arg individually
\r\n
\r\n
\r\n

\n\n

Why would I need to use these functions?

\n\n

The this value can be tricky sometimes in javascript. The value of this determined when a function is executed not when a function is defined. If our function is dependend on a right this binding we can use call() and apply() to enforce this behaviour. For example:

\n\n

\r\n
\r\n
var name = 'unwantedGlobalName';\r\n\r\nconst obj =  {\r\n  name: 'Willem',\r\n  sayName () { console.log(this.name);}\r\n}\r\n\r\n\r\nlet copiedMethod = obj.sayName;\r\n// we store the function in the copiedmethod variable\r\n\r\n\r\n\r\ncopiedMethod();\r\n// this is now window, unwantedGlobalName gets logged\r\n\r\ncopiedMethod.call(obj);\r\n// we enforce this to be obj, Willem gets logged
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90698", + "creator": "Sanjib Debnath", + "createdAt": 1470907523000, + "text": "

The difference is that call() takes the function arguments separately, and apply() takes the function arguments in an array.

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e90699", + "creator": "Alireza", + "createdAt": 1494513358000, + "text": "

The main difference is, using call, we can change the scope and pass arguments as normal, but apply lets you call it using arguments as an Array (pass them as an array). But in terms of what they to do in your code, they are pretty similar.

\n\n
\n

While the syntax of this function is almost identical to that of\n apply(), the fundamental difference is that call() accepts an argument\n list, while apply() accepts a single array of arguments.

\n
\n\n

So as you see, there is not a big difference, but still, there are cases we prefer using call() or apply(). For example, look at the code below, which finding the smallest and largest number in an array from MDN, using the apply method:

\n\n
// min/max number in an array\nvar numbers = [5, 6, 2, 3, 7];\n\n// using Math.min/Math.max apply\nvar max = Math.max.apply(null, numbers); \n// This about equal to Math.max(numbers[0], ...)\n// or Math.max(5, 6, ...)\n\nvar min = Math.min.apply(null, numbers)\n
\n\n

So the main difference is just the way we passing the arguments:

\nCall:

\n\n
function.call(thisArg, arg1, arg2, ...);\n
\n\n

Apply:

\n\n
function.apply(thisArg, [argsArray]);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9069b", + "creator": "Pravin Divraniya", + "createdAt": 1568273918000, + "text": "

Let me add a little detail to this.

\n\n

these two calls are almost equivalent:

\n\n
func.call(context, ...args); // pass an array as list with spread operator\n\nfunc.apply(context, args);   // is same as using apply\n
\n\n

There’s only a minor difference:

\n\n
\n \n
\n\n

So, these calls complement each other. Where we expect an iterable, call works, where we expect an array-like, apply works.

\n\n

And for objects that are both iterable and array-like, like a real array, we technically could use any of them, but apply will probably be faster because most JavaScript engines internally optimize it better.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9069c", + "creator": "Abdul Rehman Kaim Khani", + "createdAt": 1591864974000, + "text": "

I just want to add a simple example to a well explained post by flatline, which makes it easy to understand for beginners.

\n
func.call(context, args1, args2 );   // pass arguments as "," separated value\n\nfunc.apply(context, [args1, args2]); // pass arguments as "Array"\n
\n

we also use "Call" and "Apply" method for changing reference as defined in code below

\n

\r\n
\r\n
let Emp1 = {\n  name: 'X',\n  getEmpDetail: function(age, department) {\n    console.log(`Name: ${this.name}    Age: ${age}    Department: ${department}`)\n  }\n}\n\nEmp1.getEmpDetail(23, 'Delivery')\n\n// 1st approach of changing \"this\"\nlet Emp2 = {\n  name: 'Y',\n  getEmpDetail: Emp1.getEmpDetail\n}\n\nEmp2.getEmpDetail(55, 'Finance')\n\n// 2nd approach of changing \"this\" using \"Call\" and \"Apply\"\nlet Emp3 = {\n  name: 'Emp3_Object',\n}\n\nEmp1.getEmpDetail.call(Emp3, 30, 'Admin')\n\n// here we have change the ref from **Emp1 to Emp3**  object\n// now this will print \"Name =  Emp3_Object\" because it is pointing to Emp3 object\nEmp1.getEmpDetail.apply(Emp3, [30, 'Admin'])
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9069e", + "creator": "Ran Turner", + "createdAt": 1640370361000, + "text": "

The call() method calls a function with a given this value and a second parameter which are arguments separated by comma.

\n
object.someMethod.call( someObject, arguments )\n
\n

The apply() method is the same as call except the fact that the second argument it takes is an array of arguments .

\n
object.someMethod.apply( someObject, arrayOfarguments )\n
\n

\r\n
\r\n
var car  = {  \n  name: \"Reno\",\n  country: \"France\",\n  showBuyer: function(firstName, lastName) {\n    console.log(`${firstName} ${lastName} just bought a ${this.name} from ${this.country}`);\n  }\n}\n\nconst firstName = \"Bryan\";\nconst lastName = \"Smith\";\n\ncar.showBuyer(firstName, lastName);  // Bryan just bought a Reno from France\n\nconst obj = { name: \"Maserati\", country: \"Italy\" };\n\ncar.showBuyer.call(obj, firstName, lastName); // Bryan Smith just bought a Maserati from Italy\n\ncar.showBuyer.apply(obj, [firstName, lastName]); // Bryan Smith just bought a Maserati from Italy
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c2082fcc3049e9069d", + "creator": "unsuredev", + "createdAt": 1593105112000, + "text": "

The call() method calls a function with a given this value and arguments provided individually.\"enter

\n

apply() -\nSimilar to the call() method, the first parameter in the apply() method sets the this value which is the object upon which the function is invoked. In this case, it's the obj object above. The only difference between the apply() and call() method is that the second parameter of the apply() method accepts the arguments to the actual function as an array.\n\"enter

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321c2082fcc3049e90686", + "creator": "Larry Battle", + "createdAt": 1339481866000, + "text": "Think of a in apply for array of args and c in call for columns of args.", + "upvotes": 1071, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e90687", + "creator": "Samih", + "createdAt": 1386346803000, + "text": "@LarryBattle I do almost the same, but I think a in apply for array and c in call for comma (i.e comma separated arguments).", + "upvotes": 278, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 786795, + "uvac": 786818 + } + }, + { + "_id": "62f321bb082fcc3049e8fee0", + "title": "How to store objects in HTML5 localStorage", + "title-lowercase": "how to store objects in html5 localstorage", + "creator": "Kristopher Johnson", + "createdAt": 1262750751000, + "status": "open", + "text": "

I'd like to store a JavaScript object in HTML5 localStorage, but my object is apparently being converted to a string.

\n

I can store and retrieve primitive JavaScript types and arrays using localStorage, but objects don't seem to work. Should they?

\n

Here's my code:

\n
var testObject = { 'one': 1, 'two': 2, 'three': 3 };\nconsole.log('typeof testObject: ' + typeof testObject);\nconsole.log('testObject properties:');\nfor (var prop in testObject) {\n    console.log('  ' + prop + ': ' + testObject[prop]);\n}\n\n// Put the object into storage\nlocalStorage.setItem('testObject', testObject);\n\n// Retrieve the object from storage\nvar retrievedObject = localStorage.getItem('testObject');\n\nconsole.log('typeof retrievedObject: ' + typeof retrievedObject);\nconsole.log('Value of retrievedObject: ' + retrievedObject);\n
\n

The console output is

\n
typeof testObject: object\ntestObject properties:\n  one: 1\n  two: 2\n  three: 3\ntypeof retrievedObject: string\nValue of retrievedObject: [object Object]\n
\n

It looks to me like the setItem method is converting the input to a string before storing it.

\n

I see this behavior in Safari, Chrome, and Firefox, so I assume it's my misunderstanding of the HTML5 Web Storage specification, not a browser-specific bug or limitation.

\n

I've tried to make sense of the structured clone algorithm described in 2 Common infrastructure. I don't fully understand what it's saying, but maybe my problem has to do with my object's properties not being enumerable (???).

\n

Is there an easy workaround?

\n
\n

Update: The W3C eventually changed their minds about the structured-clone specification, and decided to change the spec to match the implementations. See 12111 – spec for Storage object getItem(key) method does not match implementation behavior. So this question is no longer 100% valid, but the answers still may be of interest.

\n", + "upvotes": 5107, + "upvoterUsernames": [], + "downvotes": 2169, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1353245, + "answers": 21, + "answerItems": [ + { + "_id": "62f321c6082fcc3049e909c9", + "creator": "Christian C. Salvadó", + "createdAt": 1262751958000, + "text": "

Looking at the Apple, Mozilla and Mozilla again documentation, the functionality seems to be limited to handle only string key/value pairs.

\n\n

A workaround can be to stringify your object before storing it, and later parse it when you retrieve it:

\n\n
var testObject = { 'one': 1, 'two': 2, 'three': 3 };\n\n// Put the object into storage\nlocalStorage.setItem('testObject', JSON.stringify(testObject));\n\n// Retrieve the object from storage\nvar retrievedObject = localStorage.getItem('testObject');\n\nconsole.log('retrievedObject: ', JSON.parse(retrievedObject));\n
\n", + "upvotes": 5696, + "upvoterUsernames": [], + "downvotes": 2015, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921dd", + "creator": "oligofren", + "createdAt": 1381171722000, + "text": "do observe that any metadata will be removed. you just get an object with the key-value pairs, so any object with behaviour need to be rebuilt.", + "upvotes": 392, + "upvoterUsernames": [], + "downvotes": 193, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921df", + "creator": "Ashish Negi", + "createdAt": 1395826056000, + "text": "@CMS can setItem throw some exception if the data is over the capacity ?", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921e1", + "creator": "Mark", + "createdAt": 1414575328000, + "text": "The problem with this approach are performance issues, if you have to handle large arrays or objects.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921e2", + "creator": "Fredrick", + "createdAt": 1610134922000, + "text": "I have just used it for the first time. So, should we need to remove item just after retrieving it from local storage?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909ca", + "creator": "Justin Voskuhl", + "createdAt": 1262752967000, + "text": "

You might find it useful to extend the Storage object with these handy methods:

\n\n
Storage.prototype.setObject = function(key, value) {\n    this.setItem(key, JSON.stringify(value));\n}\n\nStorage.prototype.getObject = function(key) {\n    return JSON.parse(this.getItem(key));\n}\n
\n\n

This way you get the functionality that you really wanted even though underneath the API only supports strings.

\n", + "upvotes": 325, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921e4", + "creator": "Sethen", + "createdAt": 1404672880000, + "text": "Just my two cents, but I'm pretty sure it's not a good idea to extend objects provided by the vendor like this.", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909cd", + "creator": "aster_x", + "createdAt": 1302038404000, + "text": "

In theory, it is possible to store objects with functions:

\n
function store (a)\n{\n  var c = {f: {}, d: {}};\n  for (var k in a)\n  {\n    if (a.hasOwnProperty(k) && typeof a[k] === 'function')\n    {\n      c.f[k] = encodeURIComponent(a[k]);\n    }\n  }\n\n  c.d = a;\n  var data = JSON.stringify(c);\n  window.localStorage.setItem('CODE', data);\n}\n\nfunction restore ()\n{\n  var data = window.localStorage.getItem('CODE');\n  data = JSON.parse(data);\n  var b = data.d;\n\n  for (var k in data.f)\n  {\n    if (data.f.hasOwnProperty(k))\n    {\n      b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");\n    }\n  }\n\n  return b;\n}\n
\n

However, function serialization/deserialization is unreliable because it is implementation-dependent.

\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909cc", + "creator": "Alex Grande", + "createdAt": 1295634556000, + "text": "

Creating a facade for the Storage object is an awesome solution. That way, you can implement your own get and set methods. For my API, I have created a facade for localStorage and then check if it is an object or not while setting and getting.

\n
var data = {\n  set: function(key, value) {\n    if (!key || !value) {return;}\n\n    if (typeof value === "object") {\n      value = JSON.stringify(value);\n    }\n    localStorage.setItem(key, value);\n  },\n  get: function(key) {\n    var value = localStorage.getItem(key);\n\n    if (!value) {return;}\n\n    // assume it is an object that has been stringified\n    if (value[0] === "{") {\n      value = JSON.parse(value);\n    }\n\n    return value;\n  }\n}\n
\n", + "upvotes": 91, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921e8", + "creator": "Jimmy T.", + "createdAt": 1518858462000, + "text": "And then someone tries to store a string starting with {", + "upvotes": 1213, + "upvoterUsernames": [], + "downvotes": 1213, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921ea", + "creator": "Jimmy T.", + "createdAt": 1519515584000, + "text": "You could just stringify everything", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909cb", + "creator": "Guria", + "createdAt": 1277880308000, + "text": "

A minor improvement on a variant:

\n\n
Storage.prototype.setObject = function(key, value) {\n    this.setItem(key, JSON.stringify(value));\n}\n\nStorage.prototype.getObject = function(key) {\n    var value = this.getItem(key);\n    return value && JSON.parse(value);\n}\n
\n\n

Because of short-circuit evaluation, getObject() will immediately return null if key is not in Storage. It also will not throw a SyntaxError exception if value is \"\" (the empty string; JSON.parse() cannot handle that).

\n", + "upvotes": 804, + "upvoterUsernames": [], + "downvotes": 140, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921ec", + "creator": "Ezeke", + "createdAt": 1358338523000, + "text": "This wont't work in IE8, so you're better of using the functions in the confirmed answer if you need to support it.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921ee", + "creator": "Jordan Arseno", + "createdAt": 1372719246000, + "text": "FYI: The getObject() function barfs if the value undefined finds it's way into localStorage.", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 75, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909cf", + "creator": "Andy Lorenz", + "createdAt": 1399462540000, + "text": "

I arrived at this post after hitting on another post that has been closed as a duplicate of this - titled 'how to store an array in localstorage?'. Which is fine except neither thread actually provides a full answer as to how you can maintain an array in localStorage - however I have managed to craft a solution based on information contained in both threads.

\n\n

So if anyone else is wanting to be able to push/pop/shift items within an array, and they want that array stored in localStorage or indeed sessionStorage, here you go:

\n\n
Storage.prototype.getArray = function(arrayName) {\n  var thisArray = [];\n  var fetchArrayObject = this.getItem(arrayName);\n  if (typeof fetchArrayObject !== 'undefined') {\n    if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }\n  }\n  return thisArray;\n}\n\nStorage.prototype.pushArrayItem = function(arrayName,arrayItem) {\n  var existingArray = this.getArray(arrayName);\n  existingArray.push(arrayItem);\n  this.setItem(arrayName,JSON.stringify(existingArray));\n}\n\nStorage.prototype.popArrayItem = function(arrayName) {\n  var arrayItem = {};\n  var existingArray = this.getArray(arrayName);\n  if (existingArray.length > 0) {\n    arrayItem = existingArray.pop();\n    this.setItem(arrayName,JSON.stringify(existingArray));\n  }\n  return arrayItem;\n}\n\nStorage.prototype.shiftArrayItem = function(arrayName) {\n  var arrayItem = {};\n  var existingArray = this.getArray(arrayName);\n  if (existingArray.length > 0) {\n    arrayItem = existingArray.shift();\n    this.setItem(arrayName,JSON.stringify(existingArray));\n  }\n  return arrayItem;\n}\n\nStorage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {\n  var existingArray = this.getArray(arrayName);\n  existingArray.unshift(arrayItem);\n  this.setItem(arrayName,JSON.stringify(existingArray));\n}\n\nStorage.prototype.deleteArray = function(arrayName) {\n  this.removeItem(arrayName);\n}\n
\n\n

example usage - storing simple strings in localStorage array:

\n\n
localStorage.pushArrayItem('myArray','item one');\nlocalStorage.pushArrayItem('myArray','item two');\n
\n\n

example usage - storing objects in sessionStorage array:

\n\n
var item1 = {}; item1.name = 'fred'; item1.age = 48;\nsessionStorage.pushArrayItem('myArray',item1);\n\nvar item2 = {}; item2.name = 'dave'; item2.age = 22;\nsessionStorage.pushArrayItem('myArray',item2);\n
\n\n

common methods to manipulate arrays:

\n\n
.pushArrayItem(arrayName,arrayItem); -> adds an element onto end of named array\n.unshiftArrayItem(arrayName,arrayItem); -> adds an element onto front of named array\n.popArrayItem(arrayName); -> removes & returns last array element\n.shiftArrayItem(arrayName); -> removes & returns first array element\n.getArray(arrayName); -> returns entire array\n.deleteArray(arrayName); -> removes entire array from storage\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909ce", + "creator": "JProgrammer", + "createdAt": 1314071522000, + "text": "

There is a great library that wraps many solutions so it even supports older browsers called jStorage

\n\n

You can set an object

\n\n
$.jStorage.set(key, value)\n
\n\n

And retrieve it easily

\n\n
value = $.jStorage.get(key)\nvalue = $.jStorage.get(key, \"default value\")\n
\n", + "upvotes": 69, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921f2", + "creator": "JProgrammer", + "createdAt": 1472598152000, + "text": "@SuperUberDuper\tjStorage requires Prototype, MooTools or jQuery", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909d0", + "creator": "Adrian May", + "createdAt": 1412162051000, + "text": "

https://github.com/adrianmay/rhaboo is a localStorage sugar layer that lets you write things like this:

\n\n
var store = Rhaboo.persistent('Some name');\nstore.write('count', store.count ? store.count+1 : 1);\nstore.write('somethingfancy', {\n  one: ['man', 'went'],\n  2: 'mow',\n  went: [  2, { mow: ['a', 'meadow' ] }, {}  ]\n});\nstore.somethingfancy.went[1].mow.write(1, 'lawn');\n
\n\n

It doesn't use JSON.stringify/parse because that would be inaccurate and slow on big objects. Instead, each terminal value has its own localStorage entry.

\n\n

You can probably guess that I might have something to do with rhaboo.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909d1", + "creator": "Nadu", + "createdAt": 1423599937000, + "text": "

Here is some extended version of the code posted by danott:

\n

It'll also implement a delete value from localstorage and shows how to adds a Getter and Setter layer so instead of,

\n

localstorage.setItem(preview, true)

\n

you can write

\n

config.preview = true

\n

Okay, here were go:

\n
var PT=Storage.prototype\n\nif (typeof PT._setItem >='u')\n  PT._setItem = PT.setItem;\nPT.setItem = function(key, value)\n{\n  if (typeof value >='u') //..undefined\n    this.removeItem(key)\n  else\n    this._setItem(key, JSON.stringify(value));\n}\n\nif (typeof PT._getItem >='u')\n  PT._getItem = PT.getItem;\nPT.getItem = function(key)\n{\n  var ItemData = this._getItem(key)\n  try\n  {\n    return JSON.parse(ItemData);\n  }\n  catch(e)\n  {\n    return ItemData;\n  }\n}\n\n// Aliases for localStorage.set/getItem\nget = localStorage.getItem.bind(localStorage)\nset = localStorage.setItem.bind(localStorage)\n\n// Create ConfigWrapperObject\nvar config = {}\n\n// Helper to create getter & setter\nfunction configCreate(PropToAdd){\n    Object.defineProperty( config, PropToAdd, {\n      get: function ()    { return (get(PropToAdd)    )},\n      set: function (val) {         set(PropToAdd, val)}\n    })\n}\n//------------------------------\n\n// Usage Part\n// Create properties\nconfigCreate('preview')\nconfigCreate('notification')\n//...\n\n// Configuration Data transfer\n// Set\nconfig.preview = true\n\n// Get\nconfig.preview\n\n// Delete\nconfig.preview = undefined\n
\n

Well, you may strip the aliases part with .bind(...). However, I just put it in since it's really good to know about this. I took me hours to find out why a simple get = localStorage.getItem; don't work.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ca082fcc3049e921f5", + "creator": "Flimm", + "createdAt": 1639680642000, + "text": "It's generally not a good idea to monkey patch globals like this. It can break code and it's not future compatible.", + "upvotes": 247, + "upvoterUsernames": [], + "downvotes": 247, + "downvoterUsernames": [] + }, + { + "_id": "62f326ca082fcc3049e921f6", + "creator": "Peter Mortensen", + "createdAt": 1650065825000, + "text": "danott's answer is now deleted. It was deleted without any explanation near the end of 2019 in a mass deletion of answers by a moderator.", + "upvotes": 993, + "upvoterUsernames": [], + "downvotes": 993, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909d2", + "creator": "doublejosh", + "createdAt": 1426147179000, + "text": "

It is recommended using an abstraction library for many of the features discussed here, as well as better compatibility. There are lots of options:

\n\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909d3", + "creator": "Rudie", + "createdAt": 1448743181000, + "text": "

I made a thing that doesn't break the existing Storage objects, but creates a wrapper so you can do what you want. The result is a normal object, no methods, with access like any object.

\n\n

The thing I made.

\n\n

If you want 1 localStorage property to be magic:

\n\n
var prop = ObjectStorage(localStorage, 'prop');\n
\n\n

If you need several:

\n\n
var storage = ObjectStorage(localStorage, ['prop', 'more', 'props']);\n
\n\n

Everything you do to prop, or the objects inside storage will be automatically saved into localStorage. You're always playing with a real object, so you can do stuff like this:

\n\n
storage.data.list.push('more data');\nstorage.another.list.splice(1, 2, {another: 'object'});\n
\n\n

And every new object inside a tracked object will be automatically tracked.

\n\n

The very big downside: it depends on Object.observe() so it has very limited browser support. And it doesn't look like it'll be coming for Firefox or Edge anytime soon.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cb082fcc3049e921f9", + "creator": "Flimm", + "createdAt": 1639680738000, + "text": "Object.observe is deprecated in all major browsers, now.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909d4", + "creator": "mar10", + "createdAt": 1466097051000, + "text": "

Another option would be to use an existing plugin.

\n\n

For example persisto is an open source project that provides an easy interface to localStorage/sessionStorage and automates persistence for form fields (input, radio buttons, and checkboxes).

\n\n

\"persisto

\n\n

(Disclaimer: I am the author.)

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909d7", + "creator": "Mac", + "createdAt": 1494982729000, + "text": "

You can use localDataStorage to transparently store JavaScript data types (Array, Boolean, Date, Float, Integer, String and Object). It also provides lightweight data obfuscation, automatically compresses strings, facilitates query by key (name) as well as query by (key) value, and helps to enforce segmented shared storage within the same domain by prefixing keys.

\n

[DISCLAIMER] I am the author of the utility [/DISCLAIMER]

\n

Examples:

\n
localDataStorage.set( 'key1', 'Belgian' )\nlocalDataStorage.set( 'key2', 1200.0047 )\nlocalDataStorage.set( 'key3', true )\nlocalDataStorage.set( 'key4', { 'RSK' : [1,'3',5,'7',9] } )\nlocalDataStorage.set( 'key5', null )\n\nlocalDataStorage.get( 'key1' )  // -->   'Belgian'\nlocalDataStorage.get( 'key2' )  // -->   1200.0047\nlocalDataStorage.get( 'key3' )  // -->   true\nlocalDataStorage.get( 'key4' )  // -->   Object {RSK: Array(5)}\nlocalDataStorage.get( 'key5' )  // -->   null\n
\n

As you can see, the primitive values are respected.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909d9", + "creator": "Moshiur Rahman", + "createdAt": 1579587103000, + "text": "

You cannot store a key value without a string format.

\n

LocalStorage only supports string formats for keys/values.

\n

That is why you should convert your data to string whatever it is an array or object.

\n

To store data in localStorage, first of all stringify it using the JSON.stringify() method.

\n
var myObj = [{name:"test", time:"Date 2017-02-03T08:38:04.449Z"}];\nlocalStorage.setItem('item', JSON.stringify(myObj));\n
\n

Then when you want to retrieve data, you need to parse the string to object again.

\n
var getObj = JSON.parse(localStorage.getItem('item'));\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cb082fcc3049e921fc", + "creator": "Ashish Kamble", + "createdAt": 1631708286000, + "text": "Thanks,i got concept of localstorage cleared", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909d8", + "creator": "Flavien Volken", + "createdAt": 1527665666000, + "text": "

For TypeScript users willing to set and get typed properties:

\n
/**\n * Silly wrapper to be able to type the storage keys\n */\nexport class TypedStorage<T> {\n\n    public removeItem(key: keyof T): void {\n        localStorage.removeItem(key);\n    }\n\n    public getItem<K extends keyof T>(key: K): T[K] | null {\n        const data: string | null =  localStorage.getItem(key);\n        return JSON.parse(data);\n    }\n\n    public setItem<K extends keyof T>(key: K, value: T[K]): void {\n        const data: string = JSON.stringify(value);\n        localStorage.setItem(key, data);\n    }\n}\n
\n

Example usage:

\n
// write an interface for the storage\ninterface MyStore {\n   age: number,\n   name: string,\n   address: {city:string}\n}\n\nconst storage: TypedStorage<MyStore> = new TypedStorage<MyStore>();\n\nstorage.setItem("wrong key", ""); // error unknown key\nstorage.setItem("age", "hello"); // error, age should be number\nstorage.setItem("address", {city:"Here"}); // ok\n\nconst address: {city:string} = storage.getItem("address");\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909db", + "creator": "Gabriel H", + "createdAt": 1616150608000, + "text": "

I suggest using Jackson-js. It is a library that handles serializing and deserializing of Objects while retaining their structure, based on decorators.

\n

The library handles all the pitfalls such as cyclic reference, attributes aliasing, etc.

\n

Simply describe your class using the @JsonProperty() and @JsonClassType() decorators.

\n

Serialize your object using:

\n
const objectMapper = new ObjectMapper();\nlocalstore.setItem(key, objectMapper.stringify<yourObjectType>(yourObject));\n
\n

For slightly more detailed explanation, check my answer here:

\n

Typescript objects serialization?

\n

And the Jackson-js tutorial here:

\n

Jackson-js: Powerful JavaScript decorators to serialize/deserialize objects into JSON and vice versa (Part 1)

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909da", + "creator": "manasa woddeyar manu", + "createdAt": 1580965343000, + "text": "
localStorage.setItem('user', JSON.stringify(user));\n
\n

Then to retrieve it from the store and convert to an object again:

\n
var user = JSON.parse(localStorage.getItem('user'));\n\nIf we need to delete all entries of the store we can simply do:\n\nlocalStorage.clear();\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cb082fcc3049e92200", + "creator": "Kristopher Johnson", + "createdAt": 1581014964000, + "text": "This is a 10-year-old question. Do you think your answer adds anything not already covered by the other answers?", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909d6", + "creator": "zevero", + "createdAt": 1475058611000, + "text": "

I made another minimalistic wrapper with only 20 lines of code to allow using it like it should:

\n\n
localStorage.set('myKey',{a:[1,2,5], b: 'ok'});\nlocalStorage.has('myKey');   // --> true\nlocalStorage.get('myKey');   // --> {a:[1,2,5], b: 'ok'}\nlocalStorage.keys();         // --> ['myKey']\nlocalStorage.remove('myKey');\n
\n\n

https://github.com/zevero/simpleWebstorage

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909d5", + "creator": "Tony Brix", + "createdAt": 1469720491000, + "text": "

You can use ejson to store the objects as strings.

\n\n
\n

EJSON is an extension of JSON to support more types. It supports all JSON-safe types, as well as:

\n \n \n \n

All EJSON serializations are also valid JSON. For example an object with a date and a binary buffer would be serialized in EJSON as:

\n\n
{\n  \"d\": {\"$date\": 1358205756553},\n  \"b\": {\"$binary\": \"c3VyZS4=\"}\n}\n
\n
\n\n

Here is my localStorage wrapper using ejson

\n\n

https://github.com/UziTech/storage.js

\n\n

I added some types to my wrapper including regular expressions and functions

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c6082fcc3049e909dc", + "creator": "dwlz", + "createdAt": 1617566009000, + "text": "

This question has been answered sufficiently from the JavaScript-only perspective, and others have already noted that both localStorage.getItem and localStorage.setItem have no concept of objects—they handle strings and strings only. This answer provides a TypeScript-friendly solution that incorporates what others have suggested in JavaScript-only solutions.

\n

TypeScript 4.2.3

\n
Storage.prototype.setObject = function (key: string, value: unknown) {\n  this.setItem(key, JSON.stringify(value));\n};\n\nStorage.prototype.getObject = function (key: string) {\n  const value = this.getItem(key);\n  if (!value) {\n    return null;\n  }\n\n  return JSON.parse(value);\n};\n\ndeclare global {\n  interface Storage {\n    setObject: (key: string, value: unknown) => void;\n    getObject: (key: string) => unknown;\n  }\n}\n
\n

Usage

\n
localStorage.setObject('ages', [23, 18, 33, 22, 58]);\nlocalStorage.getObject('ages');\n
\n

Explanation

\n

We declare both setObject and getObject functions on the Storage prototype—localStorage is an instance of this type. There's nothing special we really need to note besides the null handling in getObject. Since getItem can return null, we must exit early since calling JSON.parse on a null value will throw a runtime exception.

\n

After declaring the functions on the Storage prototype, we include their type definitions on the Storage type in the global namespace.

\n

Note: If we defined these functions with arrow functions, we'd need to assume that the storage object we're calling is always localStorage, which might not be true. For instance, the above code will add setObject and getObject support to sessionStorage as well.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326cc082fcc3049e92204", + "creator": "Flimm", + "createdAt": 1639680811000, + "text": "It's generally not a good idea to monkey-patch a global shipped by the browser. It can break other code, and it's not future-compatible.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c6082fcc3049e909dd", + "creator": "Akash Aher", + "createdAt": 1639679621000, + "text": "
localStorage.setItem('obj',JSON.stringify({name:'Akash'})); // Set Object in localStorage\nlocalStorage.getItem('obj'); // Get Object from localStorage\n\nsessionStorage.setItem('obj',JSON.stringify({name:'Akash'})); // Set Object in sessionStorage\nsessionStorage.getItem('obj'); // Get Object from sessionStorage\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c5082fcc3049e90969", + "creator": "markasoftware", + "createdAt": 1375409169000, + "text": "This seems like a job for indexedDB...", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e9096a", + "creator": "Jayant Pareek", + "createdAt": 1497035306000, + "text": "How about storing an array of Objects in localStorage? I am facing same problem that it is getting converted to string.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e9096b", + "creator": "brandito", + "createdAt": 1520836524000, + "text": "could you instead just serialize the array? like store with JSON stringify then parse again upon loading?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1358355, + "uvac": 1358376 + } + }, + { + "_id": "62f321bb082fcc3049e8fefe", + "title": "What is JSONP, and why was it created?", + "title-lowercase": "what is jsonp, and why was it created?", + "creator": "Cheeso", + "createdAt": 1263502428000, + "status": "open", + "text": "

I understand JSON, but not JSONP. Wikipedia's document on JSON is (was) the top search result for JSONP. It says this:

\n\n
\n

JSONP or \"JSON with padding\" is a JSON extension wherein a prefix is specified as an input argument of the call itself.

\n
\n\n

Huh? What call? That doesn't make any sense to me. JSON is a data format. There's no call.

\n\n

The 2nd search result is from some guy named Remy, who writes this about JSONP:

\n\n
\n

JSONP is script tag injection, passing the response from the server in to a user specified function.

\n
\n\n

I can sort of understand that, but it's still not making any sense.

\n\n
\n\n

So what is JSONP? Why was it created (what problem does it solve)? And why would I use it?

\n\n
\n\n

Addendum: I've just created a new page for JSONP on Wikipedia; it now has a clear and thorough description of JSONP, based on jvenema's answer.

\n", + "upvotes": 3813, + "upvoterUsernames": [], + "downvotes": 1447, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 568811, + "answers": 8, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90e4b", + "creator": "jldupont", + "createdAt": 1263502705000, + "text": "

Because you can ask the server to prepend a prefix to the returned JSON object. E.g

\n\n

function_prefix(json_object);

\n\n

in order for the browser to eval \"inline\" the JSON string as an expression. This trick makes it possible for the server to \"inject\" javascript code directly in the Client browser and this with bypassing the \"same origin\" restrictions.

\n\n

In other words, you can achieve cross-domain data exchange.

\n\n
\n\n

Normally, XMLHttpRequest doesn't permit cross-domain data-exchange directly (one needs to go through a server in the same domain) whereas:

\n\n

<script src=\"some_other_domain/some_data.js&prefix=function_prefix>` one can access data from a domain different than from the origin.

\n\n
\n\n

Also worth noting: even though the server should be considered as \"trusted\" before attempting that sort of \"trick\", the side-effects of possible change in object format etc. can be contained. If a function_prefix (i.e. a proper js function) is used to receive the JSON object, the said function can perform checks before accepting/further processing the returned data.

\n", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d9082fcc3049e929a4", + "creator": "jub0bs", + "createdAt": 1579680360000, + "text": ""append a prefix" is confusing :)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e4d", + "creator": "Ajain Vivek", + "createdAt": 1363527165000, + "text": "

JSONP works by constructing a “script” element (either in HTML markup or inserted into the DOM via JavaScript), which requests to a remote data service location. The response is a javascript loaded on to your browser with name of the pre-defined function along with parameter being passed that is tht JSON data being requested. When the script executes, the function is called along with JSON data, allowing the requesting page to receive and process the data.

\n\n

For Further Reading Visit: https://blogs.sap.com/2013/07/15/secret-behind-jsonp/

\n\n

client side snippet of code

\n\n
    <!DOCTYPE html>\n    <html lang=\"en\">\n    <head>\n     <title>AvLabz - CORS : The Secrets Behind JSONP </title>\n     <meta charset=\"UTF-8\" />\n    </head>\n    <body>\n      <input type=\"text\" id=\"username\" placeholder=\"Enter Your Name\"/>\n      <button type=\"submit\" onclick=\"sendRequest()\"> Send Request to Server </button>\n    <script>\n    \"use strict\";\n    //Construct the script tag at Runtime\n    function requestServerCall(url) {\n      var head = document.head;\n      var script = document.createElement(\"script\");\n\n      script.setAttribute(\"src\", url);\n      head.appendChild(script);\n      head.removeChild(script);\n    }\n\n    //Predefined callback function    \n    function jsonpCallback(data) {\n      alert(data.message); // Response data from the server\n    }\n\n    //Reference to the input field\n    var username = document.getElementById(\"username\");\n\n    //Send Request to Server\n    function sendRequest() {\n      // Edit with your Web Service URL\n      requestServerCall(\"http://localhost/PHP_Series/CORS/myService.php?callback=jsonpCallback&message=\"+username.value+\"\");\n    }    \n\n  </script>\n   </body>\n   </html>\n
\n\n

Server side piece of PHP code

\n\n
<?php\n    header(\"Content-Type: application/javascript\");\n    $callback = $_GET[\"callback\"];\n    $message = $_GET[\"message\"].\" you got a response from server yipeee!!!\";\n    $jsonResponse = \"{\\\"message\\\":\\\"\" . $message . \"\\\"}\";\n    echo $callback . \"(\" . $jsonResponse . \")\";\n?>\n
\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d9082fcc3049e929a7", + "creator": "Kevin Beal", + "createdAt": 1393804350000, + "text": "the link at the top just 404s now", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e4c", + "creator": "Jerod Venema", + "createdAt": 1263503288000, + "text": "

It's actually not too complicated...

\n\n

Say you're on domain example.com, and you want to make a request to domain example.net. To do so, you need to cross domain boundaries, a no-no in most of browserland.

\n\n

The one item that bypasses this limitation is <script> tags. When you use a script tag, the domain limitation is ignored, but under normal circumstances, you can't really do anything with the results, the script just gets evaluated.

\n\n

Enter JSONP. When you make your request to a server that is JSONP enabled, you pass a special parameter that tells the server a little bit about your page. That way, the server is able to nicely wrap up its response in a way that your page can handle.

\n\n

For example, say the server expects a parameter called callback to enable its JSONP capabilities. Then your request would look like:

\n\n
http://www.example.net/sample.aspx?callback=mycallback\n
\n\n

Without JSONP, this might return some basic JavaScript object, like so:

\n\n
{ foo: 'bar' }\n
\n\n

However, with JSONP, when the server receives the \"callback\" parameter, it wraps up the result a little differently, returning something like this:

\n\n
mycallback({ foo: 'bar' });\n
\n\n

As you can see, it will now invoke the method you specified. So, in your page, you define the callback function:

\n\n
mycallback = function(data){\n  alert(data.foo);\n};\n
\n\n

And now, when the script is loaded, it'll be evaluated, and your function will be executed. Voila, cross-domain requests!

\n\n

It's also worth noting the one major issue with JSONP: you lose a lot of control of the request. For example, there is no \"nice\" way to get proper failure codes back. As a result, you end up using timers to monitor the request, etc, which is always a bit suspect. The proposition for JSONRequest is a great solution to allowing cross domain scripting, maintaining security, and allowing proper control of the request.

\n\n

These days (2015), CORS is the recommended approach vs. JSONRequest. JSONP is still useful for older browser support, but given the security implications, unless you have no choice CORS is the better choice.

\n", + "upvotes": 3880, + "upvoterUsernames": [], + "downvotes": 1647, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f328d9082fcc3049e929aa", + "creator": "Jerod Venema", + "createdAt": 1263505964000, + "text": "Nope, it doesn't. It you trust it to deliver the javascript, same thing applies for JSONP.", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f328d9082fcc3049e929ac", + "creator": "JustLearn", + "createdAt": 1283919554000, + "text": "@jvenema CURL is also developed for cross domain data retrivation ( AFAIK) then what is the need of JSONP?", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [] + }, + { + "_id": "62f328d9082fcc3049e929ae", + "creator": "Jerod Venema", + "createdAt": 1283951409000, + "text": "CURL is a server-side solution, not client-side. They serve two different purposes.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f328d9082fcc3049e929af", + "creator": "Joe Coder", + "createdAt": 1363135282000, + "text": "@Stephen { 'foo' : 'bar' } is still not valid JSON. Double-quotes are required.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f328d9082fcc3049e929b1", + "creator": "Quentin", + "createdAt": 1425369061000, + "text": "I think JSONRequest is a red herring, as far as I know there aren't any browsers that support it.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f328d9082fcc3049e929b2", + "creator": "Cholthi Paul Ttiopic", + "createdAt": 1484817691000, + "text": "I see the conventions of JSONP are designed around convenience rather than security", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e4f", + "creator": "sarath joseph", + "createdAt": 1402037137000, + "text": "

A simple example for the usage of JSONP.

\n\n

client.html

\n\n
    <html>\n    <head>\n   </head>\n     body>\n\n\n    <input type=\"button\" id=\"001\" onclick=gO(\"getCompany\") value=\"Company\"  />\n    <input type=\"button\" id=\"002\" onclick=gO(\"getPosition\") value=\"Position\"/>\n    <h3>\n    <div id=\"101\">\n\n    </div>\n    </h3>\n\n    <script type=\"text/javascript\">\n\n    var elem=document.getElementById(\"101\");\n\n    function gO(callback){\n\n    script = document.createElement('script');\n    script.type = 'text/javascript';\n    script.src = 'http://localhost/test/server.php?callback='+callback;\n    elem.appendChild(script);\n    elem.removeChild(script);\n\n\n    }\n\n    function getCompany(data){\n\n    var message=\"The company you work for is \"+data.company +\"<img src='\"+data.image+\"'/   >\";\n    elem.innerHTML=message;\n}\n\n    function getPosition(data){\n    var message=\"The position you are offered is \"+data.position;\n    elem.innerHTML=message;\n    }\n    </script>\n    </body>\n    </html>\n
\n\n

server.php

\n\n
  <?php\n\n    $callback=$_GET[\"callback\"];\n    echo $callback;\n\n    if($callback=='getCompany')\n    $response=\"({\\\"company\\\":\\\"Google\\\",\\\"image\\\":\\\"xyz.jpg\\\"})\";\n\n    else\n    $response=\"({\\\"position\\\":\\\"Development Intern\\\"})\";\n    echo $response;\n\n    ?>    \n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e4e", + "creator": "dardawk", + "createdAt": 1364486344000, + "text": "

JSONP is a great away to get around cross-domain scripting errors. You can consume a JSONP service purely with JS without having to implement a AJAX proxy on the server side.

\n\n

You can use the b1t.co service to see how it works. This is a free JSONP service that alllows you to minify your URLs. Here is the url to use for the service:

\n\n

http://b1t.co/Site/api/External/MakeUrlWithGet?callback=[resultsCallBack]&url=[escapedUrlToMinify]

\n\n

For example the call, http://b1t.co/Site/api/External/MakeUrlWithGet?callback=whateverJavascriptName&url=google.com

\n\n

would return

\n\n
whateverJavascriptName({\"success\":true,\"url\":\"http://google.com\",\"shortUrl\":\"http://b1t.co/54\"});\n
\n\n

And thus when that get's loaded in your js as a src, it will automatically run whateverJavascriptName which you should implement as your callback function:

\n\n
function minifyResultsCallBack(data)\n{\n    document.getElementById(\"results\").innerHTML = JSON.stringify(data);\n}\n
\n\n

To actually make the JSONP call, you can do it about several ways (including using jQuery) but here is a pure JS example:

\n\n
function minify(urlToMinify)\n{\n   url = escape(urlToMinify);\n   var s = document.createElement('script');\n   s.id = 'dynScript';\n   s.type='text/javascript';\n   s.src = \"http://b1t.co/Site/api/External/MakeUrlWithGet?callback=resultsCallBack&url=\" + url;\n   document.getElementsByTagName('head')[0].appendChild(s);\n}\n
\n\n

A step by step example and a jsonp web service to practice on is available at: this post

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e51", + "creator": "simhumileco", + "createdAt": 1563569043000, + "text": "
\n

JSONP stands for JSON with Padding.

\n
\n\n

Here is the site, with great examples, with the explanation from the simplest use of this technique to the most advanced in plane JavaScript:

\n\n

w3schools.com / JSONP

\n\n

One of my more favorite techniques described above is Dynamic JSON Result, which allow to send JSON to the PHP file in URL parameter, and let the PHP file also return a JSON object based on the information it gets.

\n\n

Tools like jQuery also have facilities to use JSONP:

\n\n
jQuery.ajax({\n  url: \"https://data.acgov.org/resource/k9se-aps6.json?city=Berkeley\",\n  jsonp: \"callbackName\",\n  dataType: \"jsonp\"\n}).done(\n  response => console.log(response)\n);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e50", + "creator": "Marcus Thornton", + "createdAt": 1449547350000, + "text": "

Before understanding JSONP, you need to know JSON format and XML. Currently the most frequently used data format on the web is XML, but XML is very complicated. It makes users inconvenient to process embedded in Web pages.

\n\n

To make JavaScript can easily exchange data, even as the data processing program, we use the wording according to JavaScript objects and developed a simple data exchange format, which is JSON. JSON can be used as data, or as a JavaScript program.

\n\n

JSON can be directly embedded in JavaScript, using them you can directly execute certain JSON program, but due to security constraints, the browser Sandbox mechanism disables cross-domain JSON code execution.

\n\n

To make JSON can be passed after the execution, we developed a JSONP. JSONP bypass the security limits of the browser with JavaScript Callback functionality and the < script > tag.

\n\n

So in short it explains what JSONP is, what problem it solves (when to use it).

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f328d9082fcc3049e929b7", + "creator": "RobbyD", + "createdAt": 1484582113000, + "text": "I downvoted this because I don't believe the statement that XML was the most used dat format on the web in Dec '15.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e52", + "creator": "Muhammad Ali", + "createdAt": 1620245951000, + "text": "

I try to explain in easy way:

\n\n

Why Used:

\n

Requesting a file from another domain can cause problems, due to cross-domain policy.

\n

Requesting an external script from another domain does not have this problem.

\n

JSONP uses this advantage, and request files using the script tag instead of the XMLHttpRequest object.

\n

Code For Server File:

\n

\r\n
\r\n
<?php\n$myJSON = '{ \"name\":\"John\", \"age\":30, \"city\":\"New York\" }';\n\necho \"myFunc(\".$myJSON.\");\";\n?>
\r\n
\r\n
\r\n

\n

Function that will handle data:

\n

\r\n
\r\n
function myFunc(myObj) {\n  document.getElementById(\"demo\").innerHTML = myObj.name;\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321cb082fcc3049e90e4a", + "creator": "Pacerier", + "createdAt": 1427613626000, + "text": "Also note that JSONP can be hijacked if not implemented correctly.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 572625, + "uvac": 572633 + } + }, + { + "_id": "62f321bb082fcc3049e8fef5", + "title": "How can I determine if a variable is 'undefined' or 'null'?", + "title-lowercase": "how can i determine if a variable is 'undefined' or 'null'?", + "creator": "sadmicrowave", + "createdAt": 1271355136000, + "status": "open", + "text": "

How do I determine if variable is undefined or null?

\n\n

My code is as follows:

\n\n
var EmpName = $(\"div#esd-names div#name\").attr('class');\nif(EmpName == 'undefined'){\n  // DO SOMETHING\n};\n
\n\n
<div id=\"esd-names\">\n  <div id=\"name\"></div>\n</div>\n
\n\n\n\n

But if I do this, the JavaScript interpreter halts execution.

\n", + "upvotes": 4600, + "upvoterUsernames": [], + "downvotes": 2092, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3124031, + "answers": 35, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90c68", + "creator": "Sarfraz", + "createdAt": 1271355275000, + "text": "

You can use the qualities of the abstract equality operator to do this:

\n
if (variable == null){\n    // your code here.\n}\n
\n

Because null == undefined is true, the above code will catch both null and undefined.

\n", + "upvotes": 4474, + "upvoterUsernames": [], + "downvotes": 1118, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327f7082fcc3049e9264d", + "creator": "Entretoize", + "createdAt": 1536229933000, + "text": "I'm trying to test if event is null in firefox and an error blocks the execution: "event is undefined"", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e9264f", + "creator": "temporary_user_name", + "createdAt": 1542554011000, + "text": "Can you share the section of code that's erroring? Use a pastebin link.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92650", + "creator": "Seraf", + "createdAt": 1552871990000, + "text": "@MichaelFever How does that not work? Copy paste this in your console: const y = undefined; y == null; It should return true", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92652", + "creator": "Chris Stryczynski", + "createdAt": 1554457477000, + "text": "const y = undefined; abc == null; Will return an error... It seems undefined is defined as something...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92654", + "creator": "Mike", + "createdAt": 1556179546000, + "text": "I never suggest to use that kind of comparsion even if it covers two values. Keep using (var === null || var === undefined) instead.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92655", + "creator": "d.popov", + "createdAt": 1557825185000, + "text": "could be tricky: undefined !== null --> true undefined == null --> true", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92657", + "creator": "Ryan Haining", + "createdAt": 1565284627000, + "text": "live example of relevant comparisons. lgtm.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327f7082fcc3049e92659", + "creator": "shamaseen", + "createdAt": 1641495062000, + "text": "that doesn't work, let obj = {}; obj.ha === null \\\\ return false", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c69", + "creator": "Chetan S", + "createdAt": 1271355606000, + "text": "

jQuery attr() function returns either a blank string or the actual value (and never null or undefined). The only time it returns undefined is when your selector didn't return any element.

\n\n

So you may want to test against a blank string. Alternatively, since blank strings, null and undefined are false-y, you can just do this:

\n\n
if (!EmpName) { //do something }\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e9265c", + "creator": "Eran Medan", + "createdAt": 1331686064000, + "text": "Chrome 17.0.963.78 m gives this error: ReferenceError: EmpName is not defined", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c6a", + "creator": "user216441", + "createdAt": 1271355933000, + "text": "
if (variable == null) {\n    // Do stuff, will only match null or undefined, this won't match false\n}\n
\n", + "upvotes": 357, + "upvoterUsernames": [], + "downvotes": 164, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e9265f", + "creator": "Chuck", + "createdAt": 1271356669000, + "text": "Just in case anybody thinks this is another half-answer, this actually does work. undefined evaluates equal to null.", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92661", + "creator": "Eran Medan", + "createdAt": 1331685989000, + "text": "Failed to me in chrome console... ReferenceError: variable is not defined, so it might work, but not for me...", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92662", + "creator": "Web_Designer", + "createdAt": 1371588857000, + "text": "For some reason JSHint doesn't like this. :(", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92664", + "creator": "temporary_user_name", + "createdAt": 1393020247000, + "text": "The fact that code makes sense to you doesn't make it sensible code.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92665", + "creator": "user664833", + "createdAt": 1409875166000, + "text": "This answer is WRONG. An undefined variable produces ReferenceError.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c6c", + "creator": "Welshboy", + "createdAt": 1381855472000, + "text": "

I've just had this problem i.e. checking if an object is null.
\nI simply use this:

\n\n
if (object) {\n    // Your code\n}\n
\n\n

For example:

\n\n
if (document.getElementById(\"enterJob\")) {\n    document.getElementById(\"enterJob\").className += ' current';\n}\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c6b", + "creator": "jkindwall", + "createdAt": 1381510937000, + "text": "

Combining the above answers, it seems the most complete answer would be:

\n\n
if( typeof variable === 'undefined' || variable === null ){\n    // Do stuff\n}\n
\n\n

This should work for any variable that is either undeclared or declared and explicitly set to null or undefined. The boolean expression should evaluate to false for any declared variable that has an actual non-null value.

\n", + "upvotes": 333, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e92669", + "creator": "Frozen Crayon", + "createdAt": 1425563120000, + "text": "what about directly checking if(variable===undefined) instead of using typeof?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e9266b", + "creator": "Abdul Sadik Yalcin", + "createdAt": 1463137994000, + "text": "This is a better solution because as @Rogue pointed out, the variable might not be declared.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e9266d", + "creator": "March Ho", + "createdAt": 1464663792000, + "text": "Correct me if I am wrong, but isn't the first conditional a superset of the second one, and therefore the second conditional is superfluous?", + "upvotes": 1230, + "upvoterUsernames": [], + "downvotes": 1230, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e9266f", + "creator": "Uriahs Victor", + "createdAt": 1624429178000, + "text": "I used just the first part if( typeof variable === 'undefined' ){} since adding the null part would throw a ReferenceError", + "upvotes": 281, + "upvoterUsernames": [], + "downvotes": 281, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c6e", + "creator": "DenisS", + "createdAt": 1392049811000, + "text": "

If the variable you want to check is a global, do

\n\n
if (window.yourVarName) {\n    // Your code here\n}\n
\n\n

This way to check will not throw an error even if the yourVarName variable doesn't exist.

\n\n

Example: I want to know if my browser supports History API

\n\n
if (window.history) {\n    history.back();\n}\n
\n\n

How this works:

\n\n

window is an object which holds all global variables as its properties, and in JavaScript it is legal to try to access a non-existing object property. If history doesn't exist then window.history returns undefined. undefined is falsey, so code in an if(undefined){} block won't run.

\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e92672", + "creator": "GreenAsJade", + "createdAt": 1476184205000, + "text": "This assumes that the script is running in a browser. That's not a given.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c6d", + "creator": "temporary_user_name", + "createdAt": 1390359691000, + "text": "

The standard way to catch null and undefined simultaneously is this:

\n
if (variable == null) {\n     // do something \n}\n
\n

--which is 100% equivalent to the more explicit but less concise:

\n
if (variable === undefined || variable === null) {\n     // do something \n}\n
\n

When writing professional JS, it's taken for granted that type equality and the behavior of == vs === is understood. Therefore we use == and only compare to null.

\n
\n

Edit again

\n

The comments suggesting the use of typeof are simply wrong. Yes, my solution above will cause a ReferenceError if the variable doesn't exist. This is a good thing. This ReferenceError is desirable: it will help you find your mistakes and fix them before you ship your code, just like compiler errors would in other languages. Use try/catch if you are working with input you don't have control over.

\n

You should not have any references to undeclared variables in your code.

\n", + "upvotes": 1826, + "upvoterUsernames": [], + "downvotes": 529, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e92673", + "creator": "Mani Gandham", + "createdAt": 1402825795000, + "text": "This will cause a ReferenceError and break execution if variable is not defined or referred to at all in the code, using typeof is safer.", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92675", + "creator": "temporary_user_name", + "createdAt": 1476286763000, + "text": "Yes because you wrote !== instead of !=.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92676", + "creator": "Pankaj Singh", + "createdAt": 1499337804000, + "text": "@Aerovistae can I use it as - if (variable != null) {} ? will it give the desired result ?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92678", + "creator": "Omer", + "createdAt": 1526299490000, + "text": "its better to put undefine in single qoutes, 'undefined' because it will make it more understandable..", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e9267a", + "creator": "temporary_user_name", + "createdAt": 1552429496000, + "text": "That's explained in the comments above.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e9267b", + "creator": "David J", + "createdAt": 1562512177000, + "text": "Unless I missed something obvious, having tried this, exactly as written I received "variable is not defined".and the code halted.", + "upvotes": 245, + "upvoterUsernames": [], + "downvotes": 245, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c6f", + "creator": "Jones Agyemang", + "createdAt": 1394105068000, + "text": "

Calling typeof null returns a value of “object”, as the special value null is considered to be an empty object reference. Safari through version 5 and Chrome through version 7 have a quirk where calling typeof on a regular expression returns “function” while all other browsers return “object”.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c70", + "creator": "Angelin Nadar", + "createdAt": 1404806458000, + "text": "

Since you are using jQuery, you can determine whether a variable is undefined or its value is null by using a single function.

\n\n
var s; // undefined\njQuery.isEmptyObject(s); // will return true;\n\ns = null; // defined as null\njQuery.isEmptyObject(s); // will return true;\n\n// usage\nif(jQuery.isEmptyObject(s)){\n    alert('Either variable: s is undefined or its value is null');\n}else{\n     alert('variable: s has value ' + s);\n}\n\ns = 'something'; // defined with some value\njQuery.isEmptyObject(s); // will return false;\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e9267e", + "creator": "Mark", + "createdAt": 1463696092000, + "text": "This did not work for me. I still got the error: ReferenceError: s is not defined for the first example.", + "upvotes": 1545, + "upvoterUsernames": [], + "downvotes": 1545, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c71", + "creator": "Kapil", + "createdAt": 1425489020000, + "text": "

jQuery check element not null:

\n\n
var dvElement = $('#dvElement');\n\nif (dvElement.length  > 0) {\n    // Do something\n}\nelse{\n    // Else do something else\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c72", + "creator": "Thamaraiselvam", + "createdAt": 1430983441000, + "text": "
if (typeof EmpName != 'undefined' && EmpName) {\n
\n\n

will evaluate to true if value is not:

\n\n\n", + "upvotes": 108, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f8082fcc3049e92680", + "creator": "villamejia", + "createdAt": 1463761819000, + "text": "Please provide a reference of this javascript especification", + "upvotes": 771, + "upvoterUsernames": [], + "downvotes": 771, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92682", + "creator": "Rudy", + "createdAt": 1478878769000, + "text": "This is the same as if (EmpName). If it's undefined will be falsy already.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92684", + "creator": "Thamaraiselvam", + "createdAt": 1478922246000, + "text": "If variable is not defined. then if(EmpName) will throw error", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f8082fcc3049e92685", + "creator": "hungerstar", + "createdAt": 1479310567000, + "text": "@Thamaraiselvam I think Rudy might have meant this var EmpName; if (EmpName). Where the variable is defined but not assigned a value.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c74", + "creator": "phil294", + "createdAt": 1445699661000, + "text": "

Edited answer: In my opinion, you shouldn't use the function from my below old answer. Instead, you should probably know the type of your variable and use the according to check directly (for example, wondering if an array is empty? just do if(arr.length===0){} etc.). This answer doesn't even answer OP's question.

\n
\n

I've come to write my own function for this. JavaScript is weird.

\n

It is usable on literally anything. (Note that this also checks if the variable contains any usable values. But since this information is usually also needed, I think it's worth posting). Please consider leaving a note.

\n
function empty(v) {\n    let type = typeof v;\n    if (type === 'undefined') {\n        return true;\n    }\n    if (type === 'boolean') {\n        return !v;\n    }\n    if (v === null) {\n        return true;\n    }\n    if (v === undefined) {\n        return true;\n    }\n    if (v instanceof Array) {\n        if (v.length < 1) {\n            return true;\n        }\n    } else if (type === 'string') {\n        if (v.length < 1) {\n            return true;\n        }\n        if (v === '0') {\n            return true;\n        }\n    } else if (type === 'object') {\n        if (Object.keys(v).length < 1) {\n            return true;\n        }\n    } else if (type === 'number') {\n        if (v === 0) {\n            return true;\n        }\n    }\n    return false;\n}\n
\n

TypeScript-compatible.

\n
\n

This function should do exactly the same thing like PHP's empty() function (see RETURN VALUES)

\n

Considers undefined, null, false, 0, 0.0, "0" {}, [] as empty.

\n

"0.0", NaN, " ", true are considered non-empty.

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e92687", + "creator": "Andy", + "createdAt": 1496597100000, + "text": "Thanks, I've been able to drop this function in and clean up a lot of code. Why this isn't a standard JS function is beyond me.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f9082fcc3049e92688", + "creator": "Ben McIntyre", + "createdAt": 1496792925000, + "text": "You should change all of your == to === here, then this would be a reasonable function.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c73", + "creator": "keshav", + "createdAt": 1439623111000, + "text": "
var x;\nif (x === undefined) {\n    alert (\"only declared, but not defined.\")\n};\nif (typeof y === \"undefined\") {\n    alert (\"not even declared.\")\n};\n
\n\n

You can only use second one: as it will check for both definition and declaration

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c75", + "creator": "DanKodi", + "createdAt": 1456981043000, + "text": "

To test if a variable is null or undefined I use the below code.

\n\n
    if(typeof sVal === 'undefined' || sVal === null || sVal === ''){\n      console.log('variable is undefined or null');\n    }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e9268c", + "creator": "Anthony Rutledge", + "createdAt": 1463844740000, + "text": "Your or statement is backwards. Checking that something is undefined would be the first step, not the second.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c76", + "creator": "Suhail", + "createdAt": 1463602258000, + "text": "

I run this test in the Chrome console. Using (void 0) you can check undefined:

\n\n
var c;\nundefined\nif (c === void 0) alert();\n// output =  undefined\nvar c = 1;\n// output =  undefined\nif (c === void 0) alert();\n// output =   undefined\n// check c value  c\n// output =  1\nif (c === void 0) alert();\n// output =  undefined\nc = undefined;\n// output =  undefined\nif (c === void 0) alert();\n// output =   undefined\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c77", + "creator": "Nishanth Matha", + "createdAt": 1464177310000, + "text": "

Best way:

\n\n
if(typeof variable==='undefined' || variable===null) {\n\n/* do your stuff */\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c79", + "creator": "n1kkou", + "createdAt": 1496216849000, + "text": "

I still think the best/safe way to test these two conditions is to cast the value to a string:

\n\n
var EmpName = $(\"div#esd-names div#name\").attr('class');\n\n// Undefined check\nif (Object.prototype.toString.call(EmpName) === '[object Undefined]'){\n    // Do something with your code\n}\n\n// Nullcheck\nif (Object.prototype.toString.call(EmpName) === '[object Null]'){\n    // Do something with your code\n}\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e92690", + "creator": "sadmicrowave", + "createdAt": 1496235018000, + "text": "can you explain why you believe this is the "best/safe way" to perform the tests?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327f9082fcc3049e92692", + "creator": "n1kkou", + "createdAt": 1496236251000, + "text": "No worries! I had a lot of issues with this type of comparisons, and until now, I find it as the most useful approach for this matter.", + "upvotes": 259, + "upvoterUsernames": [], + "downvotes": 259, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c78", + "creator": "KARTHIKEYAN.A", + "createdAt": 1480810219000, + "text": "
var i;\n\nif (i === null || typeof i === 'undefined') {\n    console.log(i, 'i is undefined or null')\n}\nelse {\n    console.log(i, 'i has some value')\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e92694", + "creator": "Chuck", + "createdAt": 1481115832000, + "text": "What happens if the user enters the word 'undefined' ?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327f9082fcc3049e92695", + "creator": "KARTHIKEYAN.A", + "createdAt": 1481119137000, + "text": "Your question is good, it show condition is true so that we need to change the option normal undefined into typeof condition. @Chuck", + "upvotes": 258, + "upvoterUsernames": [], + "downvotes": 258, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c7a", + "creator": "gideon", + "createdAt": 1501035407000, + "text": "

You can check if the value is undefined or null by simply using typeof:

\n\n
if(typeof value == 'undefined'){\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c7b", + "creator": "Albert.Qing", + "createdAt": 1501909576000, + "text": "

if(x==null) is a bad idea in JavaScript. Judge with \"==\" - it may cause an unexpected type coercion, and it can't be read by CoffeeScript,\n never use \"==\" or \"!=\" in condition judgment!

\n\n

if(x) will be better, but be careful with 0 and \"\". It will be treated as false, not the equal method with \"!= null\" is true.

\n\n

\"Enter

\n\n

See JavaScript Best Practices.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e92697", + "creator": "Albert.Qing", + "createdAt": 1504352352000, + "text": "avoid "==" . Everything is always keep changing , I don't aggree with you @Aerovistae", + "upvotes": 227, + "upvoterUsernames": [], + "downvotes": 227, + "downvoterUsernames": [] + }, + { + "_id": "62f327f9082fcc3049e92699", + "creator": "temporary_user_name", + "createdAt": 1504624348000, + "text": "You don't disagree with me -- you disagree with the entire JavaScript establishment. Let's be clear.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c7c", + "creator": "Tony Tai Nguyen", + "createdAt": 1513074594000, + "text": "

With the solution below:

\n\n
const getType = (val) => typeof val === 'undefined' || !val ? null : typeof val;\nconst isDeepEqual = (a, b) => getType(a) === getType(b);\n\nconsole.log(isDeepEqual(1, 1)); // true\nconsole.log(isDeepEqual(null, null)); // true\nconsole.log(isDeepEqual([], [])); // true\nconsole.log(isDeepEqual(1, \"1\")); // false\netc...\n
\n\n

I'm able to check for the following:

\n\n\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327f9082fcc3049e9269a", + "creator": "Tony Tai Nguyen", + "createdAt": 1521736115000, + "text": "@Aerovistae, I thought I did with console.log(isDeepEqual(null, null)); and console.log(isDeepEqual(undefined, undefined)); ?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c7d", + "creator": "Franklin Pious", + "createdAt": 1527419958000, + "text": "
(null == undefined)  // true\n\n(null === undefined) // false\n
\n\n

Because === checks for both the type and value. Type of both are different but value is the same.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c7e", + "creator": "M. Arnold", + "createdAt": 1559025019000, + "text": "

The easiest way to check is:

\n\n
if(!variable) {\n  // If the variable is null or undefined then execution of code will enter here.\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fa082fcc3049e9269d", + "creator": "byxor", + "createdAt": 1561825414000, + "text": "This will execute the code if the variable has a value of false, which is potentially undesirable.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fa082fcc3049e9269f", + "creator": "byxor", + "createdAt": 1561986584000, + "text": "Sorry, but that's incorrect. Here is a JSfiddle to prove it.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c80", + "creator": "userPlus", + "createdAt": 1565864150000, + "text": "

The shortest and easiest:

\n\n
if(!EmpName ){\n // DO SOMETHING\n}\n
\n\n

this will evaluate true if EmpName is:

\n\n\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fa082fcc3049e926a1", + "creator": "gast128", + "createdAt": 1568644751000, + "text": "Use case here is that I want to know the difference between undefined and false. I will use the check on null then.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c7f", + "creator": "agc", + "createdAt": 1564410356000, + "text": "

You can simply use the following (I know there are shorter ways to do this, but this may make it easier to visually observe, at least for others looking at the code).

\n
if (x === null || x === undefined) {\n // Add your response code here, etc.\n}\n
\n

source: https://www.growthsnippets.com/how-can-i-determine-if-a-variable-is-undefined-or-null/

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c81", + "creator": "Hardik Desai", + "createdAt": 1573101590000, + "text": "

In JavaScript, as per my knowledge, we can check an undefined, null or empty variable like below.

\n
if (variable === undefined){\n}\n\nif (variable === null){\n}\n\nif (variable === ''){\n}\n
\n

Check all conditions:

\n
if(variable === undefined || variable === null || variable === ''){\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fa082fcc3049e926a5", + "creator": "dhilt", + "createdAt": 1584401441000, + "text": "var is a reserved word, this will throw SyntaxError", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fa082fcc3049e926a7", + "creator": "Hardik Desai", + "createdAt": 1593589153000, + "text": "@dhilt implemented from var to variable", + "upvotes": 1705, + "upvoterUsernames": [], + "downvotes": 1705, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c82", + "creator": "Kamil Kiełczewski", + "createdAt": 1578412042000, + "text": "

Probably the shortest way to do this is:

\n\n
if(EmpName == null) { /* DO SOMETHING */ };\n
\n\n

Here is proof:

\n\n

\r\n
\r\n
function check(EmpName) {\r\n  if(EmpName == null) { return true; };\r\n  return false;\r\n}\r\n\r\nvar log = (t,a) => console.log(`${t} -> ${check(a)}`);\r\n\r\nlog('null', null);\r\nlog('undefined', undefined);\r\nlog('NaN', NaN);\r\nlog('\"\"', \"\");\r\nlog('{}', {});\r\nlog('[]', []);\r\nlog('[1]', [1]);\r\nlog('[0]', [0]);\r\nlog('[[]]', [[]]);\r\nlog('true', true);\r\nlog('false', false);\r\nlog('\"true\"', \"true\");\r\nlog('\"false\"', \"false\");\r\nlog('Infinity', Infinity);\r\nlog('-Infinity', -Infinity);\r\nlog('1', 1);\r\nlog('0', 0);\r\nlog('-1', -1);\r\nlog('\"1\"', \"1\");\r\nlog('\"0\"', \"0\");\r\nlog('\"-1\"', \"-1\");\r\n\r\n// \"void 0\" case\r\nconsole.log('---\\n\"true\" is:', true);\r\nconsole.log('\"void 0\" is:', void 0);\r\nlog(void 0,void 0); // \"void 0\" is \"undefined\" 
\r\n
\r\n
\r\n

\n\n

And here are more details about == (source here)

\n\n

\"Enter

\n\n

BONUS: reason why === is more clear than == (look on agc answer)

\n\n

\"Enter

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fa082fcc3049e926a9", + "creator": "IceFire", + "createdAt": 1619864765000, + "text": "Thank you, great answer! What about expressions like if(var) or if(!var) ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327fa082fcc3049e926ab", + "creator": "IceFire", + "createdAt": 1619870360000, + "text": "Well, this is trivial, but can one of the matrices above also be used to check what the if expressions yield for different values?", + "upvotes": 218, + "upvoterUsernames": [], + "downvotes": 218, + "downvoterUsernames": [] + }, + { + "_id": "62f327fa082fcc3049e926ac", + "creator": "Kamil Kiełczewski", + "createdAt": 1619896004000, + "text": "@IceFire in my answer there is link to source of this picutres - there is also if statement "matrix" there", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c83", + "creator": "Anjana Kumari", + "createdAt": 1581400044000, + "text": "

Let's look at this,

\n\n
    \n
  1.  

    \n\n
    let apple; // Only declare the variable as apple\nalert(apple); // undefined\n
    \n\n

    In the above, the variable is only declared as apple. In this case, if we call method alert it will display undefined.

  2. \n
  3.  

    \n\n
       let apple = null; /* Declare the variable as apple and initialized but the value is null */\n   alert(apple); // null\n
  4. \n
\n\n

In the second one it displays null, because variable of apple value is null.

\n\n

So you can check whether a value is undefined or null.

\n\n
if(apple !== undefined || apple !== null) {\n    // Can use variable without any error\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c85", + "creator": "Ernesto", + "createdAt": 1589177352000, + "text": "

if you create a function to check it:

\n\n
export function isEmpty (v) {\n if (typeof v === \"undefined\") {\n   return true;\n }\n if (v === null) {\n   return true;\n }\n if (typeof v === \"object\" && Object.keys(v).length === 0) {\n   return true;\n }\n\n if (Array.isArray(v) && v.length === 0) {\n   return true;\n }\n\n if (typeof v === \"string\" && v.trim().length === 0) {\n   return true;\n }\n\nreturn false;\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c84", + "creator": "dhilt", + "createdAt": 1584401270000, + "text": "

The foo == null check should do the trick and resolve the \"undefined OR null\" case in the shortest manner. (Not considering \"foo is not declared\" case.) But people who are used to have 3 equals (as the best practice) might not accept it. Just look at eqeqeq or triple-equals rules in eslint and tslint...

\n\n

The explicit approach, when we are checking if a variable is undefined or null separately, should be applied in this case, and my contribution to the topic (27 non-negative answers for now!) is to use void 0 as both short and safe way to perform check for undefined.

\n\n

Using foo === undefined is not safe because undefined is not a reserved word and can be shadowed (MDN). Using typeof === 'undefined' check is safe, but if we are not going to care about foo-is-undeclared case the following approach can be used:

\n\n
if (foo === void 0 || foo === null) { ... }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c86", + "creator": "Dante_97", + "createdAt": 1605717306000, + "text": "

I know I'm 10 years late. But I'll leave my answer here just in case anybody needs a short method.

\n

Installing Lodash in your project could be useful because of the helper functions that could come in handy in these situations.

\n

Using ES6 modules, import would look like this:

\n
import isNull from 'lodash/isNull';\n\nimport isUndefined from 'lodash/isUndefined';\n\nimport isNil from 'lodash/isNil';\n
\n

Would be better if only the used functions are imported.

\n

Lodash's isNull checks if value is null.

\n
const value = null;\n \n if(isNull(value)) {\n    // do something if null\n }\n
\n

lodash's isUndefined checks if value is undefined.

\n
const value = undefined;\n\nif(isUndefined(value)) {\n   // do something if undefined.\n}\n
\n

isNil Checks if value is null OR undefined. I prefer this method over the other two because it checks both undefined and null.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327fa082fcc3049e926af", + "creator": "Joao Oliveira Rocha", + "createdAt": 1628689276000, + "text": "Why use lodash when === or == do not depend on anything and work just as well?", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90c87", + "creator": "dkburd", + "createdAt": 1617140934000, + "text": "
Simplest answer: \n
\n

if(!EmpName){\nDO SOMETHING\n};

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c88", + "creator": "Ashan Priyadarshana", + "createdAt": 1621903693000, + "text": "

With the newest javascript changes, you can use the new logical operator ??= to check if the left operand is null or undefined and if so assign the value of right operand.

\n

SO,

\n
if(EmpName == null){  // if Variable EmpName null or undefined\n  EmpName = 'some value';\n};\n
\n

Is equivalent to:

\n
EmpName ??= 'some value';\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c8a", + "creator": "noseratio", + "createdAt": 1657758153000, + "text": "

No one seems to have to posted this yet, so here we go:

\n

a?.valueOf() === undefined works reliably for either null or undefined.

\n

The following works pretty much like a == null or a == undefined, but it could be more attractive for purists who don't like == 😎

\n

\r\n
\r\n
function check(a) {\n  const value = a?.valueOf(); \n  if (value === undefined) {\n    console.log(\"a is null or undefined\");\n  }\n  else {\n    console.log(value);\n  }\n}\n\ncheck(null);\ncheck(undefined);\ncheck(0);\ncheck(\"\");\ncheck({});\ncheck([]);
\r\n
\r\n
\r\n

\n

On a side note, a?.constructor works too:

\n

\r\n
\r\n
function check(a) {\n  if (a?.constructor === undefined) {\n    console.log(\"a is null or undefined\");\n  }\n}\n\ncheck(null);\ncheck(undefined);\ncheck(0);\ncheck(\"\");\ncheck({});\ncheck([]);
\r\n
\r\n
\r\n

\n", + "upvotes": 2271, + "upvoterUsernames": [], + "downvotes": 2271, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90c89", + "creator": "Bhavya Koshiya", + "createdAt": 1653448749000, + "text": "

You can do something like this, I think its more efficient for multiple value check on the same variable in one condition

\n
const x = undefined;\nconst y = null;\nconst z = 'test';\n\nif ([undefined, null].includes(x)) {\n  // Will return true\n}\n\nif ([undefined, null].includes(y)) {\n  // Will return true\n}\n\nif ([undefined, null].includes(z)) {\n  // Will return false\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90c67", + "creator": "not my real name", + "createdAt": 1604288819000, + "text": "@DennisNolan This question was asked in 2010", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3128632, + "uvac": 3128667 + } + }, + { + "_id": "62f32a73082fcc3049e93137", + "title": "How can I determine if a variable is 'undefined' or 'null'?", + "title-lowercase": "how can i determine if a variable is 'undefined' or 'null'?", + "creator": "sadmicrowave", + "createdAt": 1271355136000, + "status": "open", + "text": "

How do I determine if variable is undefined or null?

\n\n

My code is as follows:

\n\n
var EmpName = $(\"div#esd-names div#name\").attr('class');\nif(EmpName == 'undefined'){\n  // DO SOMETHING\n};\n
\n\n
<div id=\"esd-names\">\n  <div id=\"name\"></div>\n</div>\n
\n\n\n\n

But if I do this, the JavaScript interpreter halts execution.

\n", + "upvotes": 3008, + "upvoterUsernames": [], + "downvotes": 500, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3124050, + "answers": 35, + "answerItems": [ + { + "_id": "62f32a74082fcc3049e9313a", + "creator": "Chetan S", + "createdAt": 1271355606000, + "text": "

jQuery attr() function returns either a blank string or the actual value (and never null or undefined). The only time it returns undefined is when your selector didn't return any element.

\n\n

So you may want to test against a blank string. Alternatively, since blank strings, null and undefined are false-y, you can just do this:

\n\n
if (!EmpName) { //do something }\n
\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e9315c", + "creator": "Eran Medan", + "createdAt": 1331686064000, + "text": "Chrome 17.0.963.78 m gives this error: ReferenceError: EmpName is not defined", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9313b", + "creator": "user216441", + "createdAt": 1271355933000, + "text": "
if (variable == null) {\n    // Do stuff, will only match null or undefined, this won't match false\n}\n
\n", + "upvotes": 352, + "upvoterUsernames": [], + "downvotes": 159, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e9315d", + "creator": "Chuck", + "createdAt": 1271356669000, + "text": "Just in case anybody thinks this is another half-answer, this actually does work. undefined evaluates equal to null.", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9315e", + "creator": "Eran Medan", + "createdAt": 1331685989000, + "text": "Failed to me in chrome console... ReferenceError: variable is not defined, so it might work, but not for me...", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9315f", + "creator": "Web_Designer", + "createdAt": 1371588857000, + "text": "For some reason JSHint doesn't like this. :(", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93160", + "creator": "temporary_user_name", + "createdAt": 1393020247000, + "text": "The fact that code makes sense to you doesn't make it sensible code.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93161", + "creator": "user664833", + "createdAt": 1409875166000, + "text": "This answer is WRONG. An undefined variable produces ReferenceError.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9313d", + "creator": "Welshboy", + "createdAt": 1381855472000, + "text": "

I've just had this problem i.e. checking if an object is null.
\nI simply use this:

\n\n
if (object) {\n    // Your code\n}\n
\n\n

For example:

\n\n
if (document.getElementById(\"enterJob\")) {\n    document.getElementById(\"enterJob\").className += ' current';\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e9313c", + "creator": "jkindwall", + "createdAt": 1381510937000, + "text": "

Combining the above answers, it seems the most complete answer would be:

\n\n
if( typeof variable === 'undefined' || variable === null ){\n    // Do stuff\n}\n
\n\n

This should work for any variable that is either undeclared or declared and explicitly set to null or undefined. The boolean expression should evaluate to false for any declared variable that has an actual non-null value.

\n", + "upvotes": 310, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e93162", + "creator": "Frozen Crayon", + "createdAt": 1425563120000, + "text": "what about directly checking if(variable===undefined) instead of using typeof?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93163", + "creator": "Abdul Sadik Yalcin", + "createdAt": 1463137994000, + "text": "This is a better solution because as @Rogue pointed out, the variable might not be declared.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93164", + "creator": "March Ho", + "createdAt": 1464663792000, + "text": "Correct me if I am wrong, but isn't the first conditional a superset of the second one, and therefore the second conditional is superfluous?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93165", + "creator": "Uriahs Victor", + "createdAt": 1624429178000, + "text": "I used just the first part if( typeof variable === 'undefined' ){} since adding the null part would throw a ReferenceError", + "upvotes": 1290, + "upvoterUsernames": [], + "downvotes": 1290, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93139", + "creator": "Sarfraz", + "createdAt": 1271355275000, + "text": "

You can use the qualities of the abstract equality operator to do this:

\n
if (variable == null){\n    // your code here.\n}\n
\n

Because null == undefined is true, the above code will catch both null and undefined.

\n", + "upvotes": 4601, + "upvoterUsernames": [], + "downvotes": 1245, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e93166", + "creator": "Entretoize", + "createdAt": 1536229933000, + "text": "I'm trying to test if event is null in firefox and an error blocks the execution: "event is undefined"", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93167", + "creator": "temporary_user_name", + "createdAt": 1542554011000, + "text": "Can you share the section of code that's erroring? Use a pastebin link.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93168", + "creator": "Seraf", + "createdAt": 1552871990000, + "text": "@MichaelFever How does that not work? Copy paste this in your console: const y = undefined; y == null; It should return true", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93169", + "creator": "Chris Stryczynski", + "createdAt": 1554457477000, + "text": "const y = undefined; abc == null; Will return an error... It seems undefined is defined as something...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9316a", + "creator": "Mike", + "createdAt": 1556179546000, + "text": "I never suggest to use that kind of comparsion even if it covers two values. Keep using (var === null || var === undefined) instead.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9316b", + "creator": "d.popov", + "createdAt": 1557825185000, + "text": "could be tricky: undefined !== null --> true undefined == null --> true", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9316c", + "creator": "Ryan Haining", + "createdAt": 1565284627000, + "text": "live example of relevant comparisons. lgtm.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9316d", + "creator": "shamaseen", + "createdAt": 1641495062000, + "text": "that doesn't work, let obj = {}; obj.ha === null \\\\ return false", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9313e", + "creator": "temporary_user_name", + "createdAt": 1390359691000, + "text": "

The standard way to catch null and undefined simultaneously is this:

\n
if (variable == null) {\n     // do something \n}\n
\n

--which is 100% equivalent to the more explicit but less concise:

\n
if (variable === undefined || variable === null) {\n     // do something \n}\n
\n

When writing professional JS, it's taken for granted that type equality and the behavior of == vs === is understood. Therefore we use == and only compare to null.

\n
\n

Edit again

\n

The comments suggesting the use of typeof are simply wrong. Yes, my solution above will cause a ReferenceError if the variable doesn't exist. This is a good thing. This ReferenceError is desirable: it will help you find your mistakes and fix them before you ship your code, just like compiler errors would in other languages. Use try/catch if you are working with input you don't have control over.

\n

You should not have any references to undeclared variables in your code.

\n", + "upvotes": 1803, + "upvoterUsernames": [], + "downvotes": 506, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e9316e", + "creator": "Mani Gandham", + "createdAt": 1402825795000, + "text": "This will cause a ReferenceError and break execution if variable is not defined or referred to at all in the code, using typeof is safer.", + "upvotes": 84, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e9316f", + "creator": "temporary_user_name", + "createdAt": 1476286763000, + "text": "Yes because you wrote !== instead of !=.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93170", + "creator": "Pankaj Singh", + "createdAt": 1499337804000, + "text": "@Aerovistae can I use it as - if (variable != null) {} ? will it give the desired result ?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93171", + "creator": "Omer", + "createdAt": 1526299490000, + "text": "its better to put undefine in single qoutes, 'undefined' because it will make it more understandable..", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93172", + "creator": "temporary_user_name", + "createdAt": 1552429496000, + "text": "That's explained in the comments above.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f32a75082fcc3049e93173", + "creator": "David J", + "createdAt": 1562512177000, + "text": "Unless I missed something obvious, having tried this, exactly as written I received "variable is not defined".and the code halted.", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9313f", + "creator": "DenisS", + "createdAt": 1392049811000, + "text": "

If the variable you want to check is a global, do

\n\n
if (window.yourVarName) {\n    // Your code here\n}\n
\n\n

This way to check will not throw an error even if the yourVarName variable doesn't exist.

\n\n

Example: I want to know if my browser supports History API

\n\n
if (window.history) {\n    history.back();\n}\n
\n\n

How this works:

\n\n

window is an object which holds all global variables as its properties, and in JavaScript it is legal to try to access a non-existing object property. If history doesn't exist then window.history returns undefined. undefined is falsey, so code in an if(undefined){} block won't run.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e93174", + "creator": "GreenAsJade", + "createdAt": 1476184205000, + "text": "This assumes that the script is running in a browser. That's not a given.", + "upvotes": 476, + "upvoterUsernames": [], + "downvotes": 476, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93140", + "creator": "Jones Agyemang", + "createdAt": 1394105068000, + "text": "

Calling typeof null returns a value of “object”, as the special value null is considered to be an empty object reference. Safari through version 5 and Chrome through version 7 have a quirk where calling typeof on a regular expression returns “function” while all other browsers return “object”.

\n", + "upvotes": 1436, + "upvoterUsernames": [], + "downvotes": 1436, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93141", + "creator": "Angelin Nadar", + "createdAt": 1404806458000, + "text": "

Since you are using jQuery, you can determine whether a variable is undefined or its value is null by using a single function.

\n\n
var s; // undefined\njQuery.isEmptyObject(s); // will return true;\n\ns = null; // defined as null\njQuery.isEmptyObject(s); // will return true;\n\n// usage\nif(jQuery.isEmptyObject(s)){\n    alert('Either variable: s is undefined or its value is null');\n}else{\n     alert('variable: s has value ' + s);\n}\n\ns = 'something'; // defined with some value\njQuery.isEmptyObject(s); // will return false;\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a75082fcc3049e93175", + "creator": "Mark", + "createdAt": 1463696092000, + "text": "This did not work for me. I still got the error: ReferenceError: s is not defined for the first example.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93142", + "creator": "Kapil", + "createdAt": 1425489020000, + "text": "

jQuery check element not null:

\n\n
var dvElement = $('#dvElement');\n\nif (dvElement.length  > 0) {\n    // Do something\n}\nelse{\n    // Else do something else\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93143", + "creator": "Thamaraiselvam", + "createdAt": 1430983441000, + "text": "
if (typeof EmpName != 'undefined' && EmpName) {\n
\n\n

will evaluate to true if value is not:

\n\n\n", + "upvotes": 140, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e93176", + "creator": "villamejia", + "createdAt": 1463761819000, + "text": "Please provide a reference of this javascript especification", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e93177", + "creator": "Rudy", + "createdAt": 1478878769000, + "text": "This is the same as if (EmpName). If it's undefined will be falsy already.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e93178", + "creator": "Thamaraiselvam", + "createdAt": 1478922246000, + "text": "If variable is not defined. then if(EmpName) will throw error", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e93179", + "creator": "hungerstar", + "createdAt": 1479310567000, + "text": "@Thamaraiselvam I think Rudy might have meant this var EmpName; if (EmpName). Where the variable is defined but not assigned a value.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93144", + "creator": "keshav", + "createdAt": 1439623111000, + "text": "
var x;\nif (x === undefined) {\n    alert (\"only declared, but not defined.\")\n};\nif (typeof y === \"undefined\") {\n    alert (\"not even declared.\")\n};\n
\n\n

You can only use second one: as it will check for both definition and declaration

\n", + "upvotes": 870, + "upvoterUsernames": [], + "downvotes": 870, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93145", + "creator": "phil294", + "createdAt": 1445699661000, + "text": "

Edited answer: In my opinion, you shouldn't use the function from my below old answer. Instead, you should probably know the type of your variable and use the according to check directly (for example, wondering if an array is empty? just do if(arr.length===0){} etc.). This answer doesn't even answer OP's question.

\n
\n

I've come to write my own function for this. JavaScript is weird.

\n

It is usable on literally anything. (Note that this also checks if the variable contains any usable values. But since this information is usually also needed, I think it's worth posting). Please consider leaving a note.

\n
function empty(v) {\n    let type = typeof v;\n    if (type === 'undefined') {\n        return true;\n    }\n    if (type === 'boolean') {\n        return !v;\n    }\n    if (v === null) {\n        return true;\n    }\n    if (v === undefined) {\n        return true;\n    }\n    if (v instanceof Array) {\n        if (v.length < 1) {\n            return true;\n        }\n    } else if (type === 'string') {\n        if (v.length < 1) {\n            return true;\n        }\n        if (v === '0') {\n            return true;\n        }\n    } else if (type === 'object') {\n        if (Object.keys(v).length < 1) {\n            return true;\n        }\n    } else if (type === 'number') {\n        if (v === 0) {\n            return true;\n        }\n    }\n    return false;\n}\n
\n

TypeScript-compatible.

\n
\n

This function should do exactly the same thing like PHP's empty() function (see RETURN VALUES)

\n

Considers undefined, null, false, 0, 0.0, "0" {}, [] as empty.

\n

"0.0", NaN, " ", true are considered non-empty.

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e9317a", + "creator": "Andy", + "createdAt": 1496597100000, + "text": "Thanks, I've been able to drop this function in and clean up a lot of code. Why this isn't a standard JS function is beyond me.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e9317b", + "creator": "Ben McIntyre", + "createdAt": 1496792925000, + "text": "You should change all of your == to === here, then this would be a reasonable function.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93147", + "creator": "Suhail", + "createdAt": 1463602258000, + "text": "

I run this test in the Chrome console. Using (void 0) you can check undefined:

\n\n
var c;\nundefined\nif (c === void 0) alert();\n// output =  undefined\nvar c = 1;\n// output =  undefined\nif (c === void 0) alert();\n// output =   undefined\n// check c value  c\n// output =  1\nif (c === void 0) alert();\n// output =  undefined\nc = undefined;\n// output =  undefined\nif (c === void 0) alert();\n// output =   undefined\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93146", + "creator": "DanKodi", + "createdAt": 1456981043000, + "text": "

To test if a variable is null or undefined I use the below code.

\n\n
    if(typeof sVal === 'undefined' || sVal === null || sVal === ''){\n      console.log('variable is undefined or null');\n    }\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e9317c", + "creator": "Anthony Rutledge", + "createdAt": 1463844740000, + "text": "Your or statement is backwards. Checking that something is undefined would be the first step, not the second.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93149", + "creator": "KARTHIKEYAN.A", + "createdAt": 1480810219000, + "text": "
var i;\n\nif (i === null || typeof i === 'undefined') {\n    console.log(i, 'i is undefined or null')\n}\nelse {\n    console.log(i, 'i has some value')\n}\n
\n", + "upvotes": 498, + "upvoterUsernames": [], + "downvotes": 498, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e9317d", + "creator": "Chuck", + "createdAt": 1481115832000, + "text": "What happens if the user enters the word 'undefined' ?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e9317e", + "creator": "KARTHIKEYAN.A", + "createdAt": 1481119137000, + "text": "Your question is good, it show condition is true so that we need to change the option normal undefined into typeof condition. @Chuck", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93148", + "creator": "Nishanth Matha", + "createdAt": 1464177310000, + "text": "

Best way:

\n\n
if(typeof variable==='undefined' || variable===null) {\n\n/* do your stuff */\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e9314b", + "creator": "gideon", + "createdAt": 1501035407000, + "text": "

You can check if the value is undefined or null by simply using typeof:

\n\n
if(typeof value == 'undefined'){\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e9314a", + "creator": "n1kkou", + "createdAt": 1496216849000, + "text": "

I still think the best/safe way to test these two conditions is to cast the value to a string:

\n\n
var EmpName = $(\"div#esd-names div#name\").attr('class');\n\n// Undefined check\nif (Object.prototype.toString.call(EmpName) === '[object Undefined]'){\n    // Do something with your code\n}\n\n// Nullcheck\nif (Object.prototype.toString.call(EmpName) === '[object Null]'){\n    // Do something with your code\n}\n
\n", + "upvotes": 2643, + "upvoterUsernames": [], + "downvotes": 2643, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e9317f", + "creator": "sadmicrowave", + "createdAt": 1496235018000, + "text": "can you explain why you believe this is the "best/safe way" to perform the tests?", + "upvotes": 1311, + "upvoterUsernames": [], + "downvotes": 1311, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e93180", + "creator": "n1kkou", + "createdAt": 1496236251000, + "text": "No worries! I had a lot of issues with this type of comparisons, and until now, I find it as the most useful approach for this matter.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9314c", + "creator": "Albert.Qing", + "createdAt": 1501909576000, + "text": "

if(x==null) is a bad idea in JavaScript. Judge with \"==\" - it may cause an unexpected type coercion, and it can't be read by CoffeeScript,\n never use \"==\" or \"!=\" in condition judgment!

\n\n

if(x) will be better, but be careful with 0 and \"\". It will be treated as false, not the equal method with \"!= null\" is true.

\n\n

\"Enter

\n\n

See JavaScript Best Practices.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a76082fcc3049e93181", + "creator": "Albert.Qing", + "createdAt": 1504352352000, + "text": "avoid "==" . Everything is always keep changing , I don't aggree with you @Aerovistae", + "upvotes": 2540, + "upvoterUsernames": [], + "downvotes": 2540, + "downvoterUsernames": [] + }, + { + "_id": "62f32a76082fcc3049e93182", + "creator": "temporary_user_name", + "createdAt": 1504624348000, + "text": "You don't disagree with me -- you disagree with the entire JavaScript establishment. Let's be clear.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9314d", + "creator": "Tony Tai Nguyen", + "createdAt": 1513074594000, + "text": "

With the solution below:

\n\n
const getType = (val) => typeof val === 'undefined' || !val ? null : typeof val;\nconst isDeepEqual = (a, b) => getType(a) === getType(b);\n\nconsole.log(isDeepEqual(1, 1)); // true\nconsole.log(isDeepEqual(null, null)); // true\nconsole.log(isDeepEqual([], [])); // true\nconsole.log(isDeepEqual(1, \"1\")); // false\netc...\n
\n\n

I'm able to check for the following:

\n\n\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a77082fcc3049e93183", + "creator": "Tony Tai Nguyen", + "createdAt": 1521736115000, + "text": "@Aerovistae, I thought I did with console.log(isDeepEqual(null, null)); and console.log(isDeepEqual(undefined, undefined)); ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9314e", + "creator": "Franklin Pious", + "createdAt": 1527419958000, + "text": "
(null == undefined)  // true\n\n(null === undefined) // false\n
\n\n

Because === checks for both the type and value. Type of both are different but value is the same.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93150", + "creator": "agc", + "createdAt": 1564410356000, + "text": "

You can simply use the following (I know there are shorter ways to do this, but this may make it easier to visually observe, at least for others looking at the code).

\n
if (x === null || x === undefined) {\n // Add your response code here, etc.\n}\n
\n

source: https://www.growthsnippets.com/how-can-i-determine-if-a-variable-is-undefined-or-null/

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93151", + "creator": "userPlus", + "createdAt": 1565864150000, + "text": "

The shortest and easiest:

\n\n
if(!EmpName ){\n // DO SOMETHING\n}\n
\n\n

this will evaluate true if EmpName is:

\n\n\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a77082fcc3049e93184", + "creator": "gast128", + "createdAt": 1568644751000, + "text": "Use case here is that I want to know the difference between undefined and false. I will use the check on null then.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e9314f", + "creator": "M. Arnold", + "createdAt": 1559025019000, + "text": "

The easiest way to check is:

\n\n
if(!variable) {\n  // If the variable is null or undefined then execution of code will enter here.\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a77082fcc3049e93185", + "creator": "byxor", + "createdAt": 1561825414000, + "text": "This will execute the code if the variable has a value of false, which is potentially undesirable.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a77082fcc3049e93186", + "creator": "byxor", + "createdAt": 1561986584000, + "text": "Sorry, but that's incorrect. Here is a JSfiddle to prove it.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93152", + "creator": "Hardik Desai", + "createdAt": 1573101590000, + "text": "

In JavaScript, as per my knowledge, we can check an undefined, null or empty variable like below.

\n
if (variable === undefined){\n}\n\nif (variable === null){\n}\n\nif (variable === ''){\n}\n
\n

Check all conditions:

\n
if(variable === undefined || variable === null || variable === ''){\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a77082fcc3049e93187", + "creator": "dhilt", + "createdAt": 1584401441000, + "text": "var is a reserved word, this will throw SyntaxError", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32a77082fcc3049e93188", + "creator": "Hardik Desai", + "createdAt": 1593589153000, + "text": "@dhilt implemented from var to variable", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93153", + "creator": "Kamil Kiełczewski", + "createdAt": 1578412042000, + "text": "

Probably the shortest way to do this is:

\n\n
if(EmpName == null) { /* DO SOMETHING */ };\n
\n\n

Here is proof:

\n\n

\r\n
\r\n
function check(EmpName) {\r\n  if(EmpName == null) { return true; };\r\n  return false;\r\n}\r\n\r\nvar log = (t,a) => console.log(`${t} -> ${check(a)}`);\r\n\r\nlog('null', null);\r\nlog('undefined', undefined);\r\nlog('NaN', NaN);\r\nlog('\"\"', \"\");\r\nlog('{}', {});\r\nlog('[]', []);\r\nlog('[1]', [1]);\r\nlog('[0]', [0]);\r\nlog('[[]]', [[]]);\r\nlog('true', true);\r\nlog('false', false);\r\nlog('\"true\"', \"true\");\r\nlog('\"false\"', \"false\");\r\nlog('Infinity', Infinity);\r\nlog('-Infinity', -Infinity);\r\nlog('1', 1);\r\nlog('0', 0);\r\nlog('-1', -1);\r\nlog('\"1\"', \"1\");\r\nlog('\"0\"', \"0\");\r\nlog('\"-1\"', \"-1\");\r\n\r\n// \"void 0\" case\r\nconsole.log('---\\n\"true\" is:', true);\r\nconsole.log('\"void 0\" is:', void 0);\r\nlog(void 0,void 0); // \"void 0\" is \"undefined\" 
\r\n
\r\n
\r\n

\n\n

And here are more details about == (source here)

\n\n

\"Enter

\n\n

BONUS: reason why === is more clear than == (look on agc answer)

\n\n

\"Enter

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a77082fcc3049e93189", + "creator": "IceFire", + "createdAt": 1619864765000, + "text": "Thank you, great answer! What about expressions like if(var) or if(!var) ?", + "upvotes": 4128, + "upvoterUsernames": [], + "downvotes": 4128, + "downvoterUsernames": [] + }, + { + "_id": "62f32a77082fcc3049e9318a", + "creator": "IceFire", + "createdAt": 1619870360000, + "text": "Well, this is trivial, but can one of the matrices above also be used to check what the if expressions yield for different values?", + "upvotes": 2774, + "upvoterUsernames": [], + "downvotes": 2774, + "downvoterUsernames": [] + }, + { + "_id": "62f32a77082fcc3049e9318b", + "creator": "Kamil Kiełczewski", + "createdAt": 1619896004000, + "text": "@IceFire in my answer there is link to source of this picutres - there is also if statement "matrix" there", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93154", + "creator": "Anjana Kumari", + "createdAt": 1581400044000, + "text": "

Let's look at this,

\n\n
    \n
  1.  

    \n\n
    let apple; // Only declare the variable as apple\nalert(apple); // undefined\n
    \n\n

    In the above, the variable is only declared as apple. In this case, if we call method alert it will display undefined.

  2. \n
  3.  

    \n\n
       let apple = null; /* Declare the variable as apple and initialized but the value is null */\n   alert(apple); // null\n
  4. \n
\n\n

In the second one it displays null, because variable of apple value is null.

\n\n

So you can check whether a value is undefined or null.

\n\n
if(apple !== undefined || apple !== null) {\n    // Can use variable without any error\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93155", + "creator": "dhilt", + "createdAt": 1584401270000, + "text": "

The foo == null check should do the trick and resolve the \"undefined OR null\" case in the shortest manner. (Not considering \"foo is not declared\" case.) But people who are used to have 3 equals (as the best practice) might not accept it. Just look at eqeqeq or triple-equals rules in eslint and tslint...

\n\n

The explicit approach, when we are checking if a variable is undefined or null separately, should be applied in this case, and my contribution to the topic (27 non-negative answers for now!) is to use void 0 as both short and safe way to perform check for undefined.

\n\n

Using foo === undefined is not safe because undefined is not a reserved word and can be shadowed (MDN). Using typeof === 'undefined' check is safe, but if we are not going to care about foo-is-undeclared case the following approach can be used:

\n\n
if (foo === void 0 || foo === null) { ... }\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93156", + "creator": "Ernesto", + "createdAt": 1589177352000, + "text": "

if you create a function to check it:

\n\n
export function isEmpty (v) {\n if (typeof v === \"undefined\") {\n   return true;\n }\n if (v === null) {\n   return true;\n }\n if (typeof v === \"object\" && Object.keys(v).length === 0) {\n   return true;\n }\n\n if (Array.isArray(v) && v.length === 0) {\n   return true;\n }\n\n if (typeof v === \"string\" && v.trim().length === 0) {\n   return true;\n }\n\nreturn false;\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93158", + "creator": "dkburd", + "createdAt": 1617140934000, + "text": "
Simplest answer: \n
\n

if(!EmpName){\nDO SOMETHING\n};

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e9315a", + "creator": "Bhavya Koshiya", + "createdAt": 1653448749000, + "text": "

You can do something like this, I think its more efficient for multiple value check on the same variable in one condition

\n
const x = undefined;\nconst y = null;\nconst z = 'test';\n\nif ([undefined, null].includes(x)) {\n  // Will return true\n}\n\nif ([undefined, null].includes(y)) {\n  // Will return true\n}\n\nif ([undefined, null].includes(z)) {\n  // Will return false\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e93157", + "creator": "Dante_97", + "createdAt": 1605717306000, + "text": "

I know I'm 10 years late. But I'll leave my answer here just in case anybody needs a short method.

\n

Installing Lodash in your project could be useful because of the helper functions that could come in handy in these situations.

\n

Using ES6 modules, import would look like this:

\n
import isNull from 'lodash/isNull';\n\nimport isUndefined from 'lodash/isUndefined';\n\nimport isNil from 'lodash/isNil';\n
\n

Would be better if only the used functions are imported.

\n

Lodash's isNull checks if value is null.

\n
const value = null;\n \n if(isNull(value)) {\n    // do something if null\n }\n
\n

lodash's isUndefined checks if value is undefined.

\n
const value = undefined;\n\nif(isUndefined(value)) {\n   // do something if undefined.\n}\n
\n

isNil Checks if value is null OR undefined. I prefer this method over the other two because it checks both undefined and null.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a78082fcc3049e9318c", + "creator": "Joao Oliveira Rocha", + "createdAt": 1628689276000, + "text": "Why use lodash when === or == do not depend on anything and work just as well?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a74082fcc3049e93159", + "creator": "Ashan Priyadarshana", + "createdAt": 1621903693000, + "text": "

With the newest javascript changes, you can use the new logical operator ??= to check if the left operand is null or undefined and if so assign the value of right operand.

\n

SO,

\n
if(EmpName == null){  // if Variable EmpName null or undefined\n  EmpName = 'some value';\n};\n
\n

Is equivalent to:

\n
EmpName ??= 'some value';\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a74082fcc3049e9315b", + "creator": "noseratio", + "createdAt": 1657758153000, + "text": "

No one seems to have to posted this yet, so here we go:

\n

a?.valueOf() === undefined works reliably for either null or undefined.

\n

The following works pretty much like a == null or a == undefined, but it could be more attractive for purists who don't like == 😎

\n

\r\n
\r\n
function check(a) {\n  const value = a?.valueOf(); \n  if (value === undefined) {\n    console.log(\"a is null or undefined\");\n  }\n  else {\n    console.log(value);\n  }\n}\n\ncheck(null);\ncheck(undefined);\ncheck(0);\ncheck(\"\");\ncheck({});\ncheck([]);
\r\n
\r\n
\r\n

\n

On a side note, a?.constructor works too:

\n

\r\n
\r\n
function check(a) {\n  if (a?.constructor === undefined) {\n    console.log(\"a is null or undefined\");\n  }\n}\n\ncheck(null);\ncheck(undefined);\ncheck(0);\ncheck(\"\");\ncheck({});\ncheck([]);
\r\n
\r\n
\r\n

\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 106, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f32a74082fcc3049e93138", + "creator": "not my real name", + "createdAt": 1604288819000, + "text": "@DennisNolan This question was asked in 2010", + "upvotes": 3198, + "upvoterUsernames": [], + "downvotes": 3198, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3127059, + "uvac": 3127094 + } + }, + { + "_id": "62f321bb082fcc3049e8fec3", + "title": "Why does Google prepend while(1); to their JSON responses?", + "title-lowercase": "why does google prepend while(1); to their json responses?", + "creator": "Jess", + "createdAt": 1271700009000, + "status": "open", + "text": "

Why does Google prepend while(1); to their (private) JSON responses?

\n\n

For example, here's a response while turning a calendar on and off in Google Calendar:

\n\n
while (1);\n[\n  ['u', [\n    ['smsSentFlag', 'false'],\n    ['hideInvitations', 'false'],\n    ['remindOnRespondedEventsOnly', 'true'],\n    ['hideInvitations_remindOnRespondedEventsOnly', 'false_true'],\n    ['Calendar ID stripped for privacy', 'false'],\n    ['smsVerifiedFlag', 'true']\n  ]]\n]\n
\n\n

I would assume this is to prevent people from doing an eval() on it, but all you'd really have to do is replace the while and then you'd be set. I would assume the eval prevention is to make sure people write safe JSON parsing code.

\n\n

I've seen this used in a couple of other places, too, but a lot more so with Google (Mail, Calendar, Contacts, etc.) Strangely enough, Google Docs starts with &&&START&&& instead, and Google Contacts seems to start with while(1); &&&START&&&.

\n\n

What's going on here?

\n", + "upvotes": 8635, + "upvoterUsernames": [], + "downvotes": 4274, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 574664, + "answers": 6, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e903fc", + "creator": "bdonlan", + "createdAt": 1242439729000, + "text": "

This is to ensure some other site can't do nasty tricks to try to steal your data. For example, by replacing the array constructor, then including this JSON URL via a <script> tag, a malicious third-party site could steal the data from the JSON response. By putting a while(1); at the start, the script will hang instead.

\n\n

A same-site request using XHR and a separate JSON parser, on the other hand, can easily ignore the while(1); prefix.

\n", + "upvotes": 615, + "upvoterUsernames": [], + "downvotes": 228, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903fd", + "creator": "Pointy", + "createdAt": 1271700168000, + "text": "

Note: as of 2019, many of the old vulnerabilities that lead to the preventative measures discussed in this question are no longer an issue in modern browsers. I'll leave the answer below as a historical curiosity, but really the whole topic has changed radically since 2010 (!!) when this was asked.

\n\n
\n\n

It prevents it from being used as the target of a simple <script> tag. (Well, it doesn't prevent it, but it makes it unpleasant.) That way bad guys can't just put that script tag in their own site and rely on an active session to make it possible to fetch your content.

\n\n

edit — note the comment (and other answers). The issue has to do with subverted built-in facilities, specifically the Object and Array constructors. Those can be altered such that otherwise innocuous JSON, when parsed, could trigger attacker code.

\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903fe", + "creator": "Daniel Vassallo", + "createdAt": 1271700276000, + "text": "

That would be to make it difficult for a third-party to insert the JSON response into an HTML document with the <script> tag. Remember that the <script> tag is exempt from the Same Origin Policy.

\n", + "upvotes": 138, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90400", + "creator": "Krishna Ganeriwal", + "createdAt": 1503029684000, + "text": "

Since the <script> tag is exempted from the Same Origin Policy which is a security necessity in the web world, while(1) when added to the JSON response prevents misuse of it in the <script> tag.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e903ff", + "creator": "rjh", + "createdAt": 1271700701000, + "text": "

It prevents JSON hijacking, a major JSON security issue that is formally fixed in all major browsers since 2011 with ECMAScript 5.

\n\n

Contrived example: say Google has a URL like mail.google.com/json?action=inbox which returns the first 50 messages of your inbox in JSON format. Evil websites on other domains can't make AJAX requests to get this data due to the same-origin policy, but they can include the URL via a <script> tag. The URL is visited with your cookies, and by overriding the global array constructor or accessor methods they can have a method called whenever an object (array or hash) attribute is set, allowing them to read the JSON content.

\n\n

The while(1); or &&&BLAH&&& prevents this: an AJAX request at mail.google.com will have full access to the text content, and can strip it away. But a <script> tag insertion blindly executes the JavaScript without any processing, resulting in either an infinite loop or a syntax error.

\n\n

This does not address the issue of cross-site request forgery.

\n", + "upvotes": 6169, + "upvoterUsernames": [], + "downvotes": 1667, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32424082fcc3049e91751", + "creator": "Jakub P.", + "createdAt": 1359855827000, + "text": "Why doesn't the request to obtain this data require a CSRF-token instead?", + "upvotes": 388, + "upvoterUsernames": [], + "downvotes": 137, + "downvoterUsernames": [] + }, + { + "_id": "62f32424082fcc3049e91753", + "creator": "Pedro Felix", + "createdAt": 1359915998000, + "text": "Wouldn't returning an object containing the array, instead of the array directly, also solve the problem?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32424082fcc3049e91755", + "creator": "abraham", + "createdAt": 1360041133000, + "text": "@JakubP. Storing and maintaining CSRF-tokens at Google's scale requires a large amount of infrastructure and cost.", + "upvotes": 333, + "upvoterUsernames": [], + "downvotes": 134, + "downvoterUsernames": [] + }, + { + "_id": "62f32424082fcc3049e91757", + "creator": "PhistucK", + "createdAt": 1658829767000, + "text": "@KarlKnechtel - that would break the web, unfortunately. :( It will not as an opt-in, but it is useless as an opt-in.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c0082fcc3049e90401", + "creator": "JSON C11", + "createdAt": 1583112294000, + "text": "
\n

After authentication is in place, JSON hijacking protection can take a\n variety of forms. Google appends while(1) into their JSON data, so\n that if any malicious script evaluates it, the malicious script enters\n an infinite loop.

\n
\n\n

Reference: Web Security Testing Cookbook: Systematic Techniques to Find Problems Fast

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e903f9", + "creator": "Gizmo", + "createdAt": 1487271089000, + "text": "probably a follow-up question: Why does google prepend )]}' now instead of while(1);? Would the answers be the same?", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903fa", + "creator": "Mardoxx", + "createdAt": 1494102462000, + "text": "Would prevent eval, but not with an infinite loop.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f321c0082fcc3049e903fb", + "creator": "Gras Double", + "createdAt": 1499547320000, + "text": "This )]}' may also be to save bytes, like facebook used for(;;); which saves one byte :)", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 583302, + "uvac": 583308 + } + }, + { + "_id": "62f321bb082fcc3049e8fefb", + "title": "How to print a number with commas as thousands separators in JavaScript", + "title-lowercase": "how to print a number with commas as thousands separators in javascript", + "creator": "Elias Zamaria", + "createdAt": 1274744551000, + "status": "open", + "text": "

I am trying to print an integer in JavaScript with commas as thousands separators. For example, I want to show the number 1234567 as \"1,234,567\". How would I go about doing this?

\n\n

Here is how I am doing it:

\n\n
function numberWithCommas(x) {\n    x = x.toString();\n    var pattern = /(-?\\d+)(\\d{3})/;\n    while (pattern.test(x))\n        x = x.replace(pattern, \"$1,$2\");\n    return x;\n}\n
\n\n

Is there a simpler or more elegant way to do it? It would be nice if it works with floats also, but that is not necessary. It does not need to be locale-specific to decide between periods and commas.

\n", + "upvotes": 2901, + "upvoterUsernames": [], + "downvotes": 481, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1820035, + "answers": 51, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90dfc", + "creator": "jasonmp85", + "createdAt": 1274745508000, + "text": "

I think your solution is one of the shorter ones I've seen for this. I don't think there are any standard JavaScript functions to do this sort of thing, so you're probably on your own.

\n\n

I checked the CSS 3 specifications to see whether it's possible to do this in CSS, but unless you want every digit in its own <span>, I don't think that's possible.

\n\n

I did find one project on Google Code that looked promising: flexible-js-formatting. I haven't used it, but it looks pretty flexible and has unit tests using JsUnit. The developer also has a lot of posts (though old) about this topic.

\n\n

Be sure to consider international users: lots of nations use a space as the separator and use the comma for separating the decimal from the integral part of the number.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32895082fcc3049e92901", + "creator": "Chris Betti", + "createdAt": 1386774870000, + "text": "Could you post the solution using CSS only & spans?", + "upvotes": 266, + "upvoterUsernames": [], + "downvotes": 266, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dfd", + "creator": "Noah Freitas", + "createdAt": 1335977499000, + "text": "

Here's a simple function that inserts commas for thousand separators. It uses array functions rather than a RegEx.

\n\n
/**\n * Format a number as a string with commas separating the thousands.\n * @param num - The number to be formatted (e.g. 10000)\n * @return A string representing the formatted number (e.g. \"10,000\")\n */\nvar formatNumber = function(num) {\n    var array = num.toString().split('');\n    var index = -3;\n    while (array.length + index > 0) {\n        array.splice(index, 0, ',');\n        // Decrement by 4 since we just added another unit to the array.\n        index -= 4;\n    }\n    return array.join('');\n};\n
\n\n

CodeSandbox link with examples: https://codesandbox.io/s/p38k63w0vq

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32895082fcc3049e92904", + "creator": "Arad", + "createdAt": 1561233929000, + "text": "Thank you, sir. This is exactly what I needed.", + "upvotes": 3443, + "upvoterUsernames": [], + "downvotes": 3443, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90dfe", + "creator": "user1437663", + "createdAt": 1338908590000, + "text": "

This is a variation of @mikez302's answer, but modified to support numbers with decimals (per @neu-rah's feedback that numberWithCommas(12345.6789) -> \"12,345.6,789\" instead of \"12,345.6789\"

\n\n
function numberWithCommas(n) {\n    var parts=n.toString().split(\".\");\n    return parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\") + (parts[1] ? \".\" + parts[1] : \"\");\n}\n
\n", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90dff", + "creator": "None", + "createdAt": 1347933047000, + "text": "

Thanks to everyone for their replies. I have built off of some of the answers to make a more \"one-size-fits-all\" solution.

\n\n

The first snippet adds a function that mimics PHP's number_format() to the Number prototype. If I am formatting a number, I usually want decimal places so the function takes in the number of decimal places to show. Some countries use commas as the decimal and decimals as the thousands separator so the function allows these separators to be set.

\n\n
Number.prototype.numberFormat = function(decimals, dec_point, thousands_sep) {\n    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';\n    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';\n\n    var parts = this.toFixed(decimals).split('.');\n    parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, thousands_sep);\n\n    return parts.join(dec_point);\n}\n
\n\n

You would use this as follows:

\n\n
var foo = 5000;\nconsole.log(foo.numberFormat(2)); // us format: 5,000.00\nconsole.log(foo.numberFormat(2, ',', '.')); // european format: 5.000,00\n
\n\n

I found that I often needed to get the number back for math operations, but parseFloat converts 5,000 to 5, simply taking the first sequence of integer values. So I created my own float conversion function and added it to the String prototype.

\n\n
String.prototype.getFloat = function(dec_point, thousands_sep) {\n    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';\n    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';\n\n    var parts = this.split(dec_point);\n    var re = new RegExp(\"[\" + thousands_sep + \"]\");\n    parts[0] = parts[0].replace(re, '');\n\n    return parseFloat(parts.join(dec_point));\n}\n
\n\n

Now you can use both functions as follows:

\n\n
var foo = 5000;\nvar fooString = foo.numberFormat(2); // The string 5,000.00\nvar fooFloat = fooString.getFloat(); // The number 5000;\n\nconsole.log((fooString.getFloat() + 1).numberFormat(2)); // The string 5,001.00\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32896082fcc3049e92908", + "creator": "chovy", + "createdAt": 1389341286000, + "text": "can you make an npm module for this?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92909", + "creator": "Ariel", + "createdAt": 1404806170000, + "text": "@J.Money The .toString is unnecessary, toFixed already returns a string.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e9290a", + "creator": "None", + "createdAt": 1440335917000, + "text": "@vsync The most important reason of all... browser compatibility.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e01", + "creator": "AbhinavRanjan", + "createdAt": 1353331070000, + "text": "

I think this function will take care of all the issues related to this problem.

\n\n
function commaFormat(inputString) {\n    inputString = inputString.toString();\n    var decimalPart = \"\";\n    if (inputString.indexOf('.') != -1) {\n        //alert(\"decimal number\");\n        inputString = inputString.split(\".\");\n        decimalPart = \".\" + inputString[1];\n        inputString = inputString[0];\n        //alert(inputString);\n        //alert(decimalPart);\n\n    }\n    var outputString = \"\";\n    var count = 0;\n    for (var i = inputString.length - 1; i >= 0 && inputString.charAt(i) != '-'; i--) {\n        //alert(\"inside for\" + inputString.charAt(i) + \"and count=\" + count + \" and outputString=\" + outputString);\n        if (count == 3) {\n            outputString += \",\";\n            count = 0;\n        }\n        outputString += inputString.charAt(i);\n        count++;\n    }\n    if (inputString.charAt(0) == '-') {\n        outputString += \"-\";\n    }\n    //alert(outputString);\n    //alert(outputString.split(\"\").reverse().join(\"\"));\n    return outputString.split(\"\").reverse().join(\"\") + decimalPart;\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e00", + "creator": "bartburkhardt", + "createdAt": 1348670627000, + "text": "

I added tofixed to Aki143S's solution.\nThis solution uses dots for thousands separators and comma for the precision.

\n\n
function formatNumber( num, fixed ) { \n    var decimalPart;\n\n    var array = Math.floor(num).toString().split('');\n    var index = -3; \n    while ( array.length + index > 0 ) { \n        array.splice( index, 0, '.' );              \n        index -= 4;\n    }\n\n    if(fixed > 0){\n        decimalPart = num.toFixed(fixed).split(\".\")[1];\n        return array.join('') + \",\" + decimalPart; \n    }\n    return array.join(''); \n};\n
\n\n

Examples;

\n\n
formatNumber(17347, 0)  = 17.347\nformatNumber(17347, 3)  = 17.347,000\nformatNumber(1234563.4545, 3)  = 1.234.563,454\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e02", + "creator": "Zakhar", + "createdAt": 1353685590000, + "text": "

For Integers I used a very simple method:

\n\n
var myNumber = 99999,\n    myString = myNumber + \"\";\n\nmyString.length > 3 ? return myString.substring(0, myString.length - 3) + \",\" + \n    myString.substring(myString.length - 3) : return myString;\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e03", + "creator": "Paulo Buchsbaum", + "createdAt": 1358021823000, + "text": "

The solution from @user1437663 is great.

\n\n

Who really understands the solution is being prepared to understand complex regular expressions.

\n\n

A small improvement to make it more readable:

\n\n
function numberWithCommas(x) {\n    var parts = x.toString().split(\".\");\n    return parts[0].replace(/\\B(?=(\\d{3})+(?=$))/g, \",\") + (parts[1] ? \".\" + parts[1] : \"\");\n}\n
\n\n

The pattern starts with \\B to avoid use comma at the beginning of a word. Interestingly, the pattern is returned empty because \\B does not advance the \"cursor\" (the same applies to $).

\n\n

O \\B is followed by a less known resources but is a powerful feature from Perl's regular expressions.

\n\n
            Pattern1 (? = (Pattern2) ).\n
\n\n

The magic is that what is in parentheses (Pattern2) is a pattern that follows the previous pattern (Pattern1) but without advancing the cursor and also is not part of the pattern returned. It is a kind of future pattern. This is similar when someone looks forward but really doesn't walk!

\n\n

In this case pattern2 is

\n\n
\\d{3})+(?=$)\n
\n\n

It means 3 digits (one or more times) followed by the end of the string ($)

\n\n

Finally, Replace method changes all occurrences of the pattern found (empty string) for comma. This only happens in cases where the remaining piece is a multiple of 3 digits \n(such cases where future cursor reach the end of the origin).

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e05", + "creator": "runsun", + "createdAt": 1375304351000, + "text": "

The following code uses char scan, so there's no regex.

\n\n
function commafy( num){\n  var parts = (''+(num<0?-num:num)).split(\".\"), s=parts[0], L, i=L= s.length, o='';\n  while(i--){ o = (i===0?'':((L-i)%3?'':',')) \n                  +s.charAt(i) +o }\n  return (num<0?'-':'') + o + (parts[1] ? '.' + parts[1] : ''); \n}\n
\n\n

It shows promising performance: http://jsperf.com/number-formatting-with-commas/5

\n\n

2015.4.26: Minor fix to resolve issue when num<0. See https://jsfiddle.net/runsun/p5tqqvs3/

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32896082fcc3049e92910", + "creator": "wrossmck", + "createdAt": 1394728670000, + "text": "this doesn't work with commafy(-123456) it gives -,123,456", + "upvotes": 234, + "upvoterUsernames": [], + "downvotes": 234, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92912", + "creator": "fregante", + "createdAt": 1443129289000, + "text": "This is great! Thanks for putting together the jsperf", + "upvotes": 414, + "upvoterUsernames": [], + "downvotes": 414, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92914", + "creator": "NiCk Newman", + "createdAt": 1452836815000, + "text": "This snippet is an absolute monster, out performs everything.", + "upvotes": 165, + "upvoterUsernames": [], + "downvotes": 165, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e04", + "creator": "uKolka", + "createdAt": 1373922119000, + "text": "

I'm surprised nobody mentioned Number.prototype.toLocaleString.\nIt's implemented in JavaScript 1.5 (which was introduced in 1999) so it's basically supported across all major browsers.

\n

\r\n
\r\n
var n = 34523453.345;\nconsole.log(n.toLocaleString());    // \"34,523,453.345\"
\r\n
\r\n
\r\n

\n

It also works in Node.js as of v0.12 via inclusion of Intl

\n

If you want something different, Numeral.js might be interesting.

\n", + "upvotes": 3485, + "upvoterUsernames": [], + "downvotes": 742, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32896082fcc3049e92916", + "creator": "ROMANIA_engineer", + "createdAt": 1410901936000, + "text": "For var n = 12345.54321 Chrome prints 12,345.543 without 2 decimals.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92918", + "creator": "hoju", + "createdAt": 1434077632000, + "text": "toLocaleString() has poor mobile support, better to use your own javascript function for now", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e9291a", + "creator": "Josh", + "createdAt": 1476907206000, + "text": "@jcollum Could you specify which version of Node you're referring to? I'm working with v4.4.7 and it's works perfectly.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e9291b", + "creator": "A-Diddy", + "createdAt": 1506305296000, + "text": "As of this comment, .toLocaleString() doesn't appear to work in the latest version of Chrome (v60.0).", + "upvotes": 2764, + "upvoterUsernames": [], + "downvotes": 2764, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e9291d", + "creator": "Stephen", + "createdAt": 1506504442000, + "text": "@A-Diddy just tried this in Chrtome 61 and it works fine", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e9291e", + "creator": "mmla", + "createdAt": 1549489609000, + "text": "Not a good option if you're a functional programmer because it does not work with literal number objects, just variables.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92920", + "creator": "Im Batman", + "createdAt": 1568095226000, + "text": "Note. this won't work with Android if you are developing React Native app.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92922", + "creator": "Ivan Juarez", + "createdAt": 1600732519000, + "text": "Take your Like~!!", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32896082fcc3049e92924", + "creator": "pishpish", + "createdAt": 1603634090000, + "text": "Keep in mind that toLocaleString is implementation specific - you will get different results across browsers/environments.", + "upvotes": 201, + "upvoterUsernames": [], + "downvotes": 201, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e06", + "creator": "xd6_", + "createdAt": 1384826645000, + "text": "

Yet another..(for int's as the question asks)

\n\n
function insertCommas(str)\n{\n    var a = str.split(\"\");\n    a.reverse();\n\n    var t, i = 0, arr = Array();\n\n    while (t = a.shift())\n    {\n       if (((i++ % 3) == 0) && arr.length > 0)\n           arr.unshift(\",\");\n       arr.unshift(t);\n    }\n\n    return arr.join(\"\");\n}\n
\n", + "upvotes": 131, + "upvoterUsernames": [], + "downvotes": 131, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e07", + "creator": "Wayne", + "createdAt": 1389208392000, + "text": "

Lots of good answers already. Here's another, just for fun:

\n\n
function format(num, fix) {\n    var p = num.toFixed(fix).split(\".\");\n    return p[0].split(\"\").reduceRight(function(acc, num, i, orig) {\n        if (\"-\" === num && 0 === i) {\n            return num + acc;\n        }\n        var pos = orig.length - i - 1\n        return  num + (pos && !(pos % 3) ? \",\" : \"\") + acc;\n    }, \"\") + (p[1] ? \".\" + p[1] : \"\");\n}\n
\n\n

Some examples:

\n\n
format(77.03453, 2); // \"77.03\"\nformat(78436589374); // \"78,436,589,374\"\nformat(784, 4);      // \"784.0000\"\nformat(-123456);     // \"-123,456\"\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32896082fcc3049e92928", + "creator": "wrossmck", + "createdAt": 1394728652000, + "text": "this doesn't work with format(-123456) it gives -,123,456", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e08", + "creator": "Shital Shah", + "createdAt": 1390671657000, + "text": "

if you are dealing with currency values and formatting a lot then it might be worth to add tiny accounting.js which handles lot of edge cases and localization:

\n\n
// Default usage:\naccounting.formatMoney(12345678); // $12,345,678.00\n\n// European formatting (custom symbol and separators), could also use options object as second param:\naccounting.formatMoney(4999.99, \"€\", 2, \".\", \",\"); // €4.999,99\n\n// Negative values are formatted nicely, too:\naccounting.formatMoney(-500000, \"£ \", 0); // £ -500,000\n\n// Simple `format` string allows control of symbol position [%v = value, %s = symbol]:\naccounting.formatMoney(5318008, { symbol: \"GBP\",  format: \"%v %s\" }); // 5,318,008.00 GBP\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e0a", + "creator": "phette23", + "createdAt": 1398476596000, + "text": "

The thousands separator can be inserted in an international-friendly manner using the browser's Intl object:

\n\n
Intl.NumberFormat().format(1234);\n// returns \"1,234\" if the user's locale is en_US, for example\n
\n\n

See MDN's article on NumberFormat for more, you can specify locale behavior or default to the user's. This is a little more foolproof because it respects local differences; many countries use periods to separate digits while a comma denotes the decimals.

\n\n

Intl.NumberFormat isn't available in all browsers yet, but it works in latest Chrome, Opera, & IE. Firefox's next release should support it. Webkit doesn't seem to have a timeline for implementation.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e9292a", + "creator": "Will Bowman", + "createdAt": 1471978719000, + "text": "Had issues formatting large numbers with toLocaleString, this worked great (with polyfill)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e09", + "creator": "Mosiur", + "createdAt": 1394604520000, + "text": "

Here is good solution with less coding...

\n\n
var y = \"\";\nvar arr = x.toString().split(\"\");\nfor(var i=0; i<arr.length; i++)\n{\n    y += arr[i];\n    if((arr.length-i-1)%3==0 && i<arr.length-1) y += \",\";\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e9292d", + "creator": "wrossmck", + "createdAt": 1394728550000, + "text": "this doesn't work for -123456 it gives -1,234,56", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9292e", + "creator": "Petr Havlik", + "createdAt": 1400802831000, + "text": "this is just crazy :)", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e92930", + "creator": "Shawn J. Molloy", + "createdAt": 1408822110000, + "text": "Haha I agree with petr its fun to look at though.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e92931", + "creator": "Debashis", + "createdAt": 1464268336000, + "text": "this allows characters like abcdef and so on.. which should be restricted", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e92933", + "creator": "Maor Ben", + "createdAt": 1619166221000, + "text": "You could also loop through x.toString().length and use x[i]", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e0b", + "creator": "Saksham", + "createdAt": 1405055998000, + "text": "

For indian numeric system

\n\n
var number = \"323483.85\"\nvar decimal = number.split(\".\");\nvar res = (decimal[0].length>3? numberWithCommas(decimal[0].substring(0,decimal[0].length-3))+ ',' :decimal[0]) + (decimal[0].length>3?decimal[0].substring(decimal[0].length-3,decimal[0].length):'') + '.' + decimal[1];\n
\n\n

Output: 3,23,483.85

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e0f", + "creator": "Beder Acosta Borges", + "createdAt": 1423151043000, + "text": "

Let me try to improve uKolka's answer and maybe help others save some time.

\n\n

Use Numeral.js.

\n\n

\r\n
\r\n
document.body.textContent = numeral(1234567).format('0,0');
\r\n
<script src=\"//cdnjs.cloudflare.com/ajax/libs/numeral.js/1.4.5/numeral.min.js\"></script>
\r\n
\r\n
\r\n

\n\n

You should go with Number.prototype.toLocaleString() only if its browser compatibilty is not an issue.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e92937", + "creator": "steampowered", + "createdAt": 1434555507000, + "text": "this inspired me to npm install numeral", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e0d", + "creator": "Tutankhamen", + "createdAt": 1408430821000, + "text": "
function formatNumber (num) {\n    return num.toString().replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, \"$1,\")\n}\n\nprint(formatNumber(2665));      // 2,665\nprint(formatNumber(102665));    // 102,665\nprint(formatNumber(111102665)); // 111,102,665\n
\n", + "upvotes": 109, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e9293a", + "creator": "Elias Zamaria", + "createdAt": 1408486463000, + "text": "What does this do that my answer doesn't? The regex looks slightly different but it looks like it should do the same thing.", + "upvotes": 237, + "upvoterUsernames": [], + "downvotes": 237, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9293b", + "creator": "traditional", + "createdAt": 1412706195000, + "text": "This is elegant. Exactly what I was looking for.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9293d", + "creator": "Debashis", + "createdAt": 1464268152000, + "text": "this allows characters like abcdef and so on.. which should be restricted.", + "upvotes": 243, + "upvoterUsernames": [], + "downvotes": 243, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9293e", + "creator": "kenberkeley", + "createdAt": 1500892242000, + "text": "123456789.123456789.toString().replace(/(\\d)(?=(\\d{3})+\\.)/g‌, '$1,') => 123,456,789.12345679", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e92940", + "creator": "Andy Borgmann", + "createdAt": 1624913143000, + "text": "This was my favorite of the solutions.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e10", + "creator": "user3664916", + "createdAt": 1426228310000, + "text": "

I think this is the shortest regular expression that does it:

\n\n
/\\B(?=(\\d{3})+\\b)/g\n\n\"123456\".replace(/\\B(?=(\\d{3})+\\b)/g, \",\")\n
\n\n

I checked it on a few numbers and it worked.

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e92943", + "creator": "Kyle Chadha", + "createdAt": 1442599447000, + "text": "Works well for currency though! Just round your digits prior to adding commas.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e92945", + "creator": "Shrijan Tiwari", + "createdAt": 1497002799000, + "text": "It Adds , After Decimal point : 12.3456".replace(/\\B(?=(\\d{3})+\\b)/g, ",") == 12.3,456", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e0c", + "creator": "Felipe Buccioni", + "createdAt": 1405317056000, + "text": "

An alternative way, supporting decimals, different separators and negatives.

\n\n
var number_format = function(number, decimal_pos, decimal_sep, thousand_sep) {\n    var ts      = ( thousand_sep == null ? ',' : thousand_sep )\n        , ds    = ( decimal_sep  == null ? '.' : decimal_sep )\n        , dp    = ( decimal_pos  == null ? 2   : decimal_pos )\n\n        , n     = Math.floor(Math.abs(number)).toString()\n\n        , i     = n.length % 3 \n        , f     = ((number < 0) ? '-' : '') + n.substr(0, i)\n    ;\n\n    for(;i<n.length;i+=3) {\n        if(i!=0) f+=ts;\n        f+=n.substr(i,3);\n    }\n\n    if(dp > 0) \n        f += ds + parseFloat(number).toFixed(dp).split('.')[1]\n\n    return f;\n}\n
\n\n

Some corrections by @Jignesh Sanghani, don't forget to upvote his comment.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e92948", + "creator": "Dennis Heiden", + "createdAt": 1458571832000, + "text": "Perfect for me, just added a new line to remove formatting before processing.", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9294a", + "creator": "mjs", + "createdAt": 1566316469000, + "text": "flawed. number grows. an exampel call would have been great!", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9294b", + "creator": "mjs", + "createdAt": 1566316576000, + "text": "switching ceil to floor fixed that but unsure what other issues will arise.", + "upvotes": 754, + "upvoterUsernames": [], + "downvotes": 754, + "downvoterUsernames": [] + }, + { + "_id": "62f32897082fcc3049e9294c", + "creator": "mjs", + "createdAt": 1573073644000, + "text": "Try Math.floor(-75.1) ;)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e0e", + "creator": "Ronnie Overby", + "createdAt": 1416951666000, + "text": "

I Wrote this one before stumbling on this post. No regex and you can actually understand the code.

\n\n

\r\n
\r\n
$(function(){\r\n  \r\n  function insertCommas(s) {\r\n\r\n    // get stuff before the dot\r\n    var d = s.indexOf('.');\r\n    var s2 = d === -1 ? s : s.slice(0, d);\r\n\r\n    // insert commas every 3 digits from the right\r\n    for (var i = s2.length - 3; i > 0; i -= 3)\r\n      s2 = s2.slice(0, i) + ',' + s2.slice(i);\r\n\r\n    // append fractional part\r\n    if (d !== -1)\r\n      s2 += s.slice(d);\r\n\r\n    return s2;\r\n\r\n  }\r\n  \r\n  \r\n  $('#theDudeAbides').text( insertCommas('1234567.89012' ) );\r\n  \r\n  \r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\r\n\r\n<div id=\"theDudeAbides\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32897082fcc3049e9294e", + "creator": "ladieu", + "createdAt": 1629914927000, + "text": "only works if you pass it a string.. an easy fix but just wanted to note it", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e11", + "creator": "Eluny", + "createdAt": 1427128196000, + "text": "

I've adapted your code to work in TextBox (Input type=\"text\") so we can enter and delete digits in real time without losing cursor. It's works also if you select range when you delete. And you can use arrows and home/end buttons freely.
\nThanks for saving my time!

\n\n
//function controls number format as \"1,532,162.3264321\"\nfunction numberWithCommas(x) {\n    var e = e || window.event;\n    if (e.keyCode >= '35' && e.keyCode <= '40') return; //skip arrow-keys\n    var selStart = x.selectionStart, selEnd = x.selectionEnd; //save cursor positions\n    var parts = x.value.toString().split(\".\");\n    var part0len = parts[0].length; //old length to check if new ',' would be added. Need for correcting new cursor position (+1 to right).\n\n    //if user deleted ',' - remove previous number instead (without selection)\n    if (x.selectionLength == 0 && (e.keyCode == 8 || e.keyCode == 46)) {//if pressed 8-backspace or 46-delete button\n        var delPos = parts[0].search(/\\d{4}/);\n        if (delPos != -1) {//if found 4 digits in a row (',' is deleted)\n            if (e.keyCode == 8) {//if backspace flag\n                parts[0] = parts[0].slice(0, selStart - 1) + parts[0].slice(selEnd, parts[0].length);\n                selEnd--;\n                if (selStart > selEnd) selStart = selEnd;\n            } else {\n                parts[0] = parts[0].slice(0, selStart) + parts[0].slice(selEnd + 1, parts[0].length);\n                selStart++;\n                if (selEnd < selStart) selEnd = selStart;\n            }\n        }\n    }\n\n   var hasMinus = parts[0][0] == '-';\n   parts[0] = (hasMinus ? '-' : '') + parts[0].replace(/[^\\d]*/g, \"\"); //I'd like to clear old ',' to avoid things like 1,2,3,5,634.443216\n   parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\"); //sets ',' between each 3 digits\n   if (part0len < parts[0].length) { //move cursor to right if added new ','\n       selStart++;\n       selEnd++;\n   } else if (part0len > parts[0].length) { //..or if removed last one ','\n       selStart--;\n       selEnd--;\n   }\n   x.value = parts.join(\".\");\n   x.setSelectionRange(selStart, selEnd); //restoring cursor position\n}\nfunction saveSelectionLength(x) {\n    x.selectionLength = x.selectionEnd - x.selectionStart;\n}\n
\n\n

To use this just added two events - onKeyUp and onKeyDown

\n\n
<asp:TextBox runat=\"server\" ID=\"val\" Width=\"180px\" onKeyUp=\"numberWithCommas(this);\" onKeyDown=\"saveSelectionLength(this);\"/>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e13", + "creator": "Marcin Zukowski", + "createdAt": 1459143604000, + "text": "

I thought I'd share a little trick which I'm using for large number formatting.\nInstead of inserting commas or spaces, I insert an empty but visible span in between the \"thousands\". This makes thousands easily visible, but it allows to copy/paste the input in the original format, without commas/spaces.

\n\n
// This function accepts an integer, and produces a piece of HTML that shows it nicely with \n// some empty space at \"thousand\" markers. \n// Note, these space are not spaces, if you copy paste, they will not be visible.\nfunction valPrettyPrint(orgVal) {\n  // Save after-comma text, if present\n  var period = orgVal.indexOf(\".\");\n  var frac = period >= 0 ? orgVal.substr(period) : \"\";\n  // Work on input as an integer\n  var val = \"\" + Math.trunc(orgVal);\n  var res = \"\";\n  while (val.length > 0) {\n    res = val.substr(Math.max(0, val.length - 3), 3) + res;\n    val = val.substr(0, val.length - 3);\n    if (val.length > 0) {\n        res = \"<span class='thousandsSeparator'></span>\" + res;\n    }\n  }\n  // Add the saved after-period information\n  res += frac;\n  return res;\n}\n
\n\n

With this CSS:

\n\n
.thousandsSeparator {\n  display : inline;\n  padding-left : 4px;\n}\n
\n\n

See an example JSFiddle.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e92952", + "creator": "Marcin Zukowski", + "createdAt": 1465678729000, + "text": "Oh wait. It just adds a comma before. Your still render using the Javascript from my example. So I'm not sure what you mean.", + "upvotes": 199, + "upvoterUsernames": [], + "downvotes": 199, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e12", + "creator": "vsync", + "createdAt": 1440233524000, + "text": "

Below are two different browser APIs that can transform Numbers into structured Strings. Keep in mind that not all users' machines have a locale that uses commas in numbers. To enforce commas to the output, any "western" locale may be used, such as en-US

\n
let number = 1234567890; // Example number to be converted\n
\n

⚠️ Mind that javascript has a maximum integer value of 9007199254740991

\n
\n

toLocaleString

\n
// default behaviour on a machine with a local that uses commas for numbers\nlet number = 1234567890;\nnumber.toLocaleString(); // "1,234,567,890"\n\n// With custom settings, forcing a "US" locale to guarantee commas in output\nlet number2 = 1234.56789; // floating point example\nnumber2.toLocaleString('en-US', {maximumFractionDigits:2}) // "1,234.57"\n
\n

NumberFormat

\n
let number = 1234567890;\nlet nf = new Intl.NumberFormat('en-US');\nnf.format(number); // "1,234,567,890"\n
\n
\n

From what I checked (Firefox at least) they are both more or less same regarding performance.

\n

Live demo: https://codepen.io/vsync/pen/MWjdbgL?editors=1000

\n", + "upvotes": 754, + "upvoterUsernames": [], + "downvotes": 317, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e92954", + "creator": "vsync", + "createdAt": 1458405536000, + "text": "Browsers support is always mentioned at the bottom of each MDN page, which I've linked to.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32898082fcc3049e92955", + "creator": "dandavis", + "createdAt": 1466500590000, + "text": "basic toLocaleString works on safari, options don't", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32898082fcc3049e92956", + "creator": "Mahmood Afzalzadeh", + "createdAt": 1605090912000, + "text": "Saved my time and toLocaleString is useful and works on FireFox and Chrome very good.", + "upvotes": 1325, + "upvoterUsernames": [], + "downvotes": 1325, + "downvoterUsernames": [] + }, + { + "_id": "62f32898082fcc3049e92958", + "creator": "Nader Gharibian Fard", + "createdAt": 1610435194000, + "text": "tnx brother.. var nf = new Intl.NumberFormat(); nf.format(number); // "1,234,567,890" it's work", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e15", + "creator": "Ethan Chen", + "createdAt": 1483739512000, + "text": "

If you happen to be using AngularJS, there's this currency filter that may definitely help: http://www.w3schools.com/angular/ng_filter_currency.asp

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e14", + "creator": "Sinan", + "createdAt": 1463634665000, + "text": "

Number.prototype.toLocaleString() would have been awesome if it was provided natively by all browsers (Safari).

\n\n

I checked all other answers but noone seemed to polyfill it. Here is a poc towards that, which is actually a combination of first two answers; if toLocaleString works it uses it, if it doesn't it uses a custom function.

\n\n

\r\n
\r\n
var putThousandsSeparators;\r\n\r\nputThousandsSeparators = function(value, sep) {\r\n  if (sep == null) {\r\n    sep = ',';\r\n  }\r\n  // check if it needs formatting\r\n  if (value.toString() === value.toLocaleString()) {\r\n    // split decimals\r\n    var parts = value.toString().split('.')\r\n    // format whole numbers\r\n    parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, sep);\r\n    // put them back together\r\n    value = parts[1] ? parts.join('.') : parts[0];\r\n  } else {\r\n    value = value.toLocaleString();\r\n  }\r\n  return value;\r\n};\r\n\r\nalert(putThousandsSeparators(1234567.890));
\r\n
\r\n
\r\n

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e9295b", + "creator": "Sinan", + "createdAt": 1488406631000, + "text": "alright, this needed another edit :) I agree, but now at least it should work for the most cases.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e16", + "creator": "Peter S McIntyre", + "createdAt": 1487630473000, + "text": "

This should work now ... edited to add decimal places if the number is a decimal.

\n\n
<script>\n  function makedollars(mynumber)\n      {\n       mynumber = mynumber.toString();\n    var numberend=\"\";\n\n  if(mynumber.split('.').length>1){\n       var mynumbersplit = mynumber.split('.');\n     mynumber = mynumbersplit[0];\n   numberend= mynumbersplit[1];\n\n  }\n\n      var  mn = mynumber.length;\n\n      if (mn <= 3) { return mynumber + numberend; }\n      var grps = [];\n\n        while (mn > 3)\n        {  \n            grps.push(mynumber.substring(mn,mn - 3));\n            mn = mn - 3;\n        }\n        grps.push(mynumber.substring(mn,mn - 3));\n      grps.reverse();\n\n        grps.join(\",\");\n        if(numberend!=\"\"){ grps =  grps +\".\"+numberend;}\n\n        return grps; \n              }\n\n\n </script>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e9295d", + "creator": "Peter S McIntyre", + "createdAt": 1506135480000, + "text": "I should have split on "." Then added the second part back In after. I forgot about decimals.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e17", + "creator": "Kiry Meas", + "createdAt": 1497119929000, + "text": "
var formatNumber = function (number) {\n  var splitNum;\n  number = Math.abs(number);\n  number = number.toFixed(2);\n  splitNum = number.split('.');\n  splitNum[0] = splitNum[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n  return splitNum.join(\".\");\n}\n
\n\n

EDIT:\nThe function only work with positive number. for exmaple:

\n\n
var number = -123123231232;\nformatNumber(number)\n
\n\n

Output: \"123,123,231,232\"

\n\n

But to answer the question above toLocaleString() method just solves the problem.

\n\n
var number = 123123231232;\n    number.toLocaleString()\n
\n\n

Output: \"123,123,231,232\"

\n\n

Cheer!

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e9295f", + "creator": "Ralph David Abernathy", + "createdAt": 1516992602000, + "text": "Nice script, but it does not work with negative numbers.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e18", + "creator": "Matt Scheurich", + "createdAt": 1498663384000, + "text": "

Here's my try:

\n\n

EDIT: Added in decimals

\n\n

\r\n
\r\n
function splitMille(n, separator = ',') {\r\n  // Cast to string\r\n  let num = (n + '')\r\n\r\n  // Test for and get any decimals (the later operations won't support them)\r\n  let decimals = ''\r\n  if (/\\./.test(num)) {\r\n    // This regex grabs the decimal point as well as the decimal numbers\r\n    decimals = num.replace(/^.*(\\..*)$/, '$1')\r\n  }\r\n  \r\n  // Remove decimals from the number string\r\n  num = num.replace(decimals, '')\r\n    // Reverse the number string through Array functions\r\n    .split('').reverse().join('')\r\n    // Split into groups of 1-3 characters (with optional supported character \"-\" for negative numbers)\r\n    .match(/[0-9]{1,3}-?/g)\r\n    // Add in the mille separator character and reverse back\r\n    .join(separator).split('').reverse().join('')\r\n\r\n  // Put the decimals back and output the formatted number\r\n  return `${num}${decimals}`\r\n}\r\n\r\nlet testA = splitMille(1234)\r\nlet testB = splitMille(-1234)\r\nlet testC = splitMille(123456.789)\r\nlet testD = splitMille(9007199254740991)\r\nlet testE = splitMille(1000.0001)\r\n\r\nconsole.log('Results!\\n\\tA: %s\\n\\tB: %s\\n\\tC: %s\\n\\tD: %s\\n\\tE: %s', testA, testB, testC, testD, testE)
\r\n
\r\n
\r\n

\n", + "upvotes": 3142, + "upvoterUsernames": [], + "downvotes": 3142, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e19", + "creator": "arosolino", + "createdAt": 1506104847000, + "text": "

Here is a one line function with int & decimal support. I left some code in to convert the number to a string as well.

\n\n
    function numberWithCommas(x) {\n        return (x=x+'').replace(new RegExp('\\\\B(?=(\\\\d{3})+'+(~x.indexOf('.')?'\\\\.':'$')+')','g'),',');\n    }\n
\n", + "upvotes": 1377, + "upvoterUsernames": [], + "downvotes": 1377, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e1a", + "creator": "Dustin Sun", + "createdAt": 1509494179000, + "text": "

Intl.NumberFormat

\n\n

Native JS function. Supported by IE11, Edge, latest Safari, Chrome, Firefox, Opera, Safari on iOS and Chrome on Android.

\n\n
var number = 3500;\n\nconsole.log(new Intl.NumberFormat().format(number));\n// → '3,500' if in US English locale\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e92964", + "creator": "Dustin Sun", + "createdAt": 1510676613000, + "text": "My answer was updated in response to your comments. Thanks!", + "upvotes": 1070, + "upvoterUsernames": [], + "downvotes": 1070, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e1b", + "creator": "Du-Lacoste", + "createdAt": 1512014687000, + "text": "

You can either use this procedure to format your currency needing.

\n\n
var nf = new Intl.NumberFormat('en-US', {\n  style: 'currency',\n  currency: 'USD',\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2\n});\nnf.format(123456.789); // ‘$123,456.79’\n
\n\n

For more info you can access this link.

\n\n

https://www.justinmccandless.com/post/formatting-currency-in-javascript/

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32898082fcc3049e92966", + "creator": "Jared Smith", + "createdAt": 1529610485000, + "text": "This is the correct, portable, native answer. Wish I could upvote more than once.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e1c", + "creator": "Waruna Manjula", + "createdAt": 1524944792000, + "text": "
\n

\n
\n\n
function addCommas(nStr) {\n    nStr += '';\n    var x = nStr.split('.');\n    var x1 = x[0];\n    var x2 = x.length > 1 ? '.' + x[1] : '';\n    var rgx = /(\\d+)(\\d{3})/;\n    while (rgx.test(x1)) {\n            x1 = x1.replace(rgx, '$1' + ',' + '$2');\n    }\n    return x1 + x2;\n}\n\naddCommas(parseFloat(\"1099920.23232\").toFixed(2)); //Output  1,099,920.23\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e1d", + "creator": "Sverrisson", + "createdAt": 1526255030000, + "text": "

After not finding a modern and comprehensive solution here, I have written an arrow function (without regex) to solve the formatting problem and it allows the caller to provide number of fraction digits as well as the period and thousand separator for Europe and rest of the world.

\n\n
\n

Examples:

\n\n
numberFormatter(1234567890.123456) => 1,234,567,890\nnumberFormatter(1234567890.123456, 4) => 1,234,567,890.1235\nnumberFormatter(1234567890.123456, 4, '.', ',') => 1.234.567.890,1235 Europe\n
\n
\n\n

Here is the function written in ES6 (modern syntax):

\n\n
const numberFormatter = (number, fractionDigits = 0, thousandSeperator = ',', fractionSeperator = '.') => {\n    if (number!==0 && !number || !Number.isFinite(number)) return number\n    const frDigits = Number.isFinite(fractionDigits)? Math.min(Math.max(fractionDigits, 0), 7) : 0\n    const num = number.toFixed(frDigits).toString()\n\n    const parts = num.split('.')\n    let digits = parts[0].split('').reverse()\n    let sign = ''\n    if (num < 0) {sign = digits.pop()}\n    let final = []\n    let pos = 0\n\n    while (digits.length > 1) {\n        final.push(digits.shift())\n        pos++\n        if (pos % 3 === 0) {final.push(thousandSeperator)}\n    }\n    final.push(digits.shift())\n    return `${sign}${final.reverse().join('')}${frDigits > 0 ? fractionSeperator : ''}${frDigits > 0 && parts[1] ? parts[1] : ''}`\n}\n
\n\n

It has been tested for negative, bad input and NaN cases. If the input is NaN then it simply returns it.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e1e", + "creator": "Geoffrey Kimani", + "createdAt": 1527174689000, + "text": "

You can use for loops to add commas to the number

\n\n
function convertNumber(){\n    var _cash = cash.toString()\n    var _formattedCash = ''\n    var count = 0\n\n    for (let i = _cash.length; i >= 0; i--) {\n        _formattedCash += _cash.substring(i,i+1)\n\n        if(count == 3 && i > 0){\n            _formattedCash += ','\n            count = 0\n        }\n        count++\n    }\n\n    var _format = ''\n\n    for (let i = _formattedCash.length; i >= 0; i--) {\n        _format += _formattedCash.substring(i, i + 1)\n    }\n\n    return 'Ksh ' + _format;\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e1f", + "creator": "aleclarson", + "createdAt": 1527708351000, + "text": "

I've found an approach that works in every situation. CodeSandbox example

\n\n
function commas(n) {\n  if (n < 1000) {\n    return n + ''\n  } else {\n    // Convert to string.\n    n += ''\n\n    // Skip scientific notation.\n    if (n.indexOf('e') !== -1) {\n      return n\n    }\n\n    // Support fractions.\n    let i = n.indexOf('.')\n    let f = i == -1 ? '' : n.slice(i)\n    if (f) n = n.slice(0, i)\n\n    // Add commas.\n    i = n.length\n    n = n.split('')\n    while (i > 3) n.splice((i -= 3), 0, ',')\n    return n.join('') + f\n  }\n}\n
\n\n

This is like Noah Freitas' answer, but with support for fractions and scientific notation.

\n\n

I think toLocaleString is the best choice, if performance is not a concern.

\n\n

edit: Here's a CodeSandbox with some examples: https://codesandbox.io/s/zmvxjpj6x

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32899082fcc3049e9296c", + "creator": "Elias Zamaria", + "createdAt": 1528045104000, + "text": "I tried commas(12345.67) and got "12,345.6700000000000728".", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32899082fcc3049e9296d", + "creator": "aleclarson", + "createdAt": 1528054628000, + "text": "@EliasZamaria whoops! forgot about imprecise arithmetic. fixed :)", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e20", + "creator": "Bathri Nathan", + "createdAt": 1552385074000, + "text": "

Use This code to handle currency format for india. Country code can be changed to handle other country currency.

\n\n
let amount =350256.95\nvar formatter = new Intl.NumberFormat('en-IN', {\n  minimumFractionDigits: 2,\n});\n\n// Use it.\n\nformatter.format(amount);\n
\n\n

output:

\n\n
3,50,256.95\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32899082fcc3049e9296f", + "creator": "Bathri Nathan", + "createdAt": 1552386749000, + "text": "@ShanteshwarInde i will add additional context to improve the answer sure", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e21", + "creator": "Igor Bykov", + "createdAt": 1552610876000, + "text": "

Just for future Googlers (or not necessarily 'Googlers'):

\n\n

All of solutions mentioned above are wonderful, however, RegExp might be awfully bad thing to use in a situation like that.

\n\n

So, yes, you might use some of the options proposed or even write something primitive yet useful like:

\n\n
const strToNum = str => {\n\n   //Find 1-3 digits followed by exactly 3 digits & a comma or end of string\n   let regx = /(\\d{1,3})(\\d{3}(?:,|$))/;\n   let currStr;\n\n   do {\n       currStr = (currStr || str.split(`.`)[0])\n           .replace( regx, `$1,$2`)\n   } while (currStr.match(regx)) //Stop when there's no match & null's returned\n\n   return ( str.split(`.`)[1] ) ?\n           currStr.concat(`.`, str.split(`.`)[1]) :\n           currStr;\n\n};\n\nstrToNum(`123`) // => 123\nstrToNum(`123456`) // => 123,456\nstrToNum(`-1234567.0987`) // => -1,234,567.0987\n
\n\n

The regexp that's used here is fairly simple and the loop will go precisely the number of times it takes to get the job done.

\n\n

And you might optimize it far better, \"DRYify\" code & so on.

\n\n

Yet,

\n\n
(-1234567.0987).toLocaleString();\n
\n\n

(in most situations) would be a far better choice.

\n\n

The point is not in the speed of execution or in cross-browser compatibility.

\n\n

In situations when you'd like to show the resulting number to user, .toLocaleString() method gives you superpower to speak the same language with the user of your website or app (whatever her/his language is).

\n\n

This method according to ECMAScript documentation was introduced in 1999, and I believe that the reason for that was the hope that the Internet at some point will connect people all around the world, so, some \"internalization\" tools were needed.

\n\n

Today the Internet does connect all of us, so, it is important to remember that the world is a way more complex that we might imagine & that (/almost) all of us are here, in the Internet.

\n\n

Obviously, considering the diversity of people, it is impossible to guarantee perfect UX for everybody because we speak different languages, value different things, etc. And exactly because of this, it is even more important to try to localize things as much as it's possible.

\n\n

So, considering that there're some particular standards for representation of date, time, numbers, etc. & that we have a tool to display those things in the format preferred by the final user, isn't that rare and almost irresponsible not to use that tool (especially in situations when we want to display this data to the user)?

\n\n

For me, using RegExp instead of .toLocaleString() in situation like that sounds a little bit like creating a clock app with JavaScript & hard-coding it in such a way so it'll display Prague time only (which would be quite useless for people who don't live in Prague) even though the default behaviour of

\n\n
new Date();\n
\n\n

is to return the data according to final user's clock.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32899082fcc3049e92971", + "creator": "OG Sean", + "createdAt": 1565900302000, + "text": "why did you write the function with const and => ?", + "upvotes": 1609, + "upvoterUsernames": [], + "downvotes": 1609, + "downvoterUsernames": [] + }, + { + "_id": "62f32899082fcc3049e92973", + "creator": "serraosays", + "createdAt": 1620132489000, + "text": "This is the right answer, people are wildly overcomplicating this. toLocaleString() is built for this exact use case.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e22", + "creator": "Oliver", + "createdAt": 1564311107000, + "text": "

You can also use the Intl.NumberFormat constructor. Here is how you can do it.

\n\n
 resultNumber = new Intl.NumberFormat('en-IN', { maximumSignificantDigits: 3 }).format(yourNumber); \n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32899082fcc3049e92976", + "creator": "Suraj Dalvi", + "createdAt": 1590669638000, + "text": "this not working node js. It is not giving response in Indian format", + "upvotes": 700, + "upvoterUsernames": [], + "downvotes": 700, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e23", + "creator": "Lonnie Best", + "createdAt": 1565241324000, + "text": "

My answer is the only answer that completely replaces jQuery with a much more sensible alternative:

\n\n
function $(dollarAmount)\n{\n    const locale = 'en-US';\n    const options = { style: 'currency', currency: 'USD' };\n    return Intl.NumberFormat(locale, options).format(dollarAmount);\n}\n
\n\n

This solution not only adds commas, but it also rounds to the nearest penny in the event that you input an amount like $(1000.9999) you'll get $1,001.00. Additionally, the value you input can safely be a number or a string; it doesn't matter.

\n\n

If you're dealing with money, but don't want a leading dollar sign shown on the amount, you can also add this function, which uses the previous function but removes the $:

\n\n
function no$(dollarAmount)\n{\n    return $(dollarAmount).replace('$','');\n}\n
\n\n

If you're not dealing with money, and have varying decimal formatting requirements, here's a more versatile function:

\n\n
function addCommas(number, minDecimalPlaces = 0, maxDecimalPlaces = Math.max(3,minDecimalPlaces))\n{\n    const options = {};\n    options.maximumFractionDigits = maxDecimalPlaces;\n    options.minimumFractionDigits = minDecimalPlaces;\n    return Intl.NumberFormat('en-US',options).format(number);\n}\n
\n\n

Oh, and by the way, the fact that this code does not work in some old version of Internet Explorer is completely intentional. I try to break IE anytime that I can catch it not supporting modern standards.

\n\n

Please remember that excessive praise, in the comment section, is considered off-topic. Instead, just shower me with up-votes.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e25", + "creator": "djulien", + "createdAt": 1574037662000, + "text": "

For anyone who likes 1-liners and a single regex, but doesn't want to use split(), here is an enhanced version of the regex from other answers that handles (ignores) decimal places:

\n\n
    var formatted = (x+'').replace(/(\\..*)$|(\\d)(?=(\\d{3})+(?!\\d))/g, (digit, fract) => fract || digit + ',');\n
\n\n

The regex first matches a substring starting with a literal \".\" and replaces it with itself (\"fract\"), and then matches any digit followed by multiples of 3 digits and puts \",\" after it.

\n\n

For example, x = 12345678.12345678 will give formatted = '12,345,678.12345678'.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e24", + "creator": "Javier Elices", + "createdAt": 1572897876000, + "text": "

I am quite impressed by the number of answers this question has got. I like the answer by uKolka:

\n\n
n.toLocaleString()\n
\n\n

But unfortunately, in some locales like Spanish, it does not work (IMHO) as expected for numbers below 10,000:

\n\n
Number(1000).toLocaleString('ES-es')\n
\n\n

Gives 1000 and not 1.000.

\n\n

See toLocaleString not working on numbers less than 10000 in all browsers to know why.

\n\n

So I had to use the answer by Elias Zamaria choosing the right thousands separator character:

\n\n
n.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, Number(10000).toLocaleString().substring(2, 3))\n
\n\n

This one works well as a one-liner for both locales that use , or . as the thousands separator and starts working from 1,000 in all cases.

\n\n
Number(1000).toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, Number(10000).toLocaleString().substring(2, 3))\n
\n\n

Gives 1.000 with a Spanish locale context.

\n\n

Should you want to have absolute control over the way a number is formatted, you may also try the following:

\n\n
let number   = 1234.567\nlet decimals = 2\nlet decpoint = '.' // Or Number(0.1).toLocaleString().substring(1, 2)\nlet thousand = ',' // Or Number(10000).toLocaleString().substring(2, 3)\n\nlet n = Math.abs(number).toFixed(decimals).split('.')\nn[0] = n[0].split('').reverse().map((c, i, a) =>\n  i > 0 && i < a.length && i % 3 == 0 ? c + thousand : c\n).reverse().join('')\nlet final = (Math.sign(number) < 0 ? '-' : '') + n.join(decpoint)\n\nconsole.log(final)\n
\n\n

Gives 1,234.57.

\n\n

This one does not need a regular expression. It works by adjusting the number to the desired amount of decimals with toFixed first, then dividing it around the decimal point . if there is one. The left side is then turned into an array of digits which is reversed. Then a thousands separator is added every three digits from the start and the result reversed again. The final result is the union of the two parts. The sign of the input number is removed with Math.abs first and then put back if necessary.

\n\n

It is not a one-liner but not much longer and easily turned into a function. Variables have been added for clarity, but those may be substituted by their desired values if known in advance. You may use the expressions that use toLocaleString as a way to find out the right characters for the decimal point and the thousands separator for the current locale (bear in mind that those require a more modern Javascript.)

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e26", + "creator": "Nadir Abbas", + "createdAt": 1604972147000, + "text": "

You can create a function on the Number prototype

\n
Number.prototype.format = function (s, d) {\n  return (\n    this.toString()\n      .split(".")\n      .map((n, i) =>\n        i\n          ? n\n          : n\n              .split("")\n              .map((n, i) => (i % 3 || !i ? n : s + n))\n              .join("")\n      )\n      .join(d)\n  );\n};\n\nconsole.log((8800.00).format(',', '.'))\n// 8,880.00\n\n// French notation\nconsole.log((8800.00).format(' ', ','))\n// 8 880,00\n
\n", + "upvotes": 987, + "upvoterUsernames": [], + "downvotes": 987, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e28", + "creator": "Maor Ben", + "createdAt": 1619071967000, + "text": "
let formatNumber = (number) => {\n    let str = String(number)\n\n    return str.split('').reduce(\n        (a, b, i) => a + (i && !((str.length - i) % 3) ? ',' : '') + b,\n        ''\n    )\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e29", + "creator": "Black", + "createdAt": 1620903656000, + "text": "

Use this for german layout with points as thousand separator and comma as decimal separator:

\n
function numberWithCommas(x)\n{\n    var parts = x.toString().split(".");\n    parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, ".");\n    return parts.join(",");\n}\n
\n

Full usage example:

\n

\r\n
\r\n
function show()\n{\n  var val = jQuery(\"#number\").val();\n  jQuery(\"#output\").htmlNumber(val);\n}\n\nfunction numberWithCommas(x)\n{\n    var parts = x.toString().split(\".\");\n    parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, \".\");\n    return parts.join(\",\");\n}\n\njQuery.fn.extend({\n    htmlNumber: function(value) {\n        this.html(numberWithCommas(value));\n        return this;\n    }\n});
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n\nEnter value: <input id=\"number\" type=\"number\">\n<p id=\"output\">{{x}}</p>\n<button onclick=\"show()\" id=\"calc\">Show</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e2a", + "creator": "anarchist", + "createdAt": 1624202107000, + "text": "

maybe it can solve it in a simple way:

\n

console.log(parseFloat(parseFloat(n.split(',').join('.')).toFixed(2)))

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c6082fcc3049e92efd", + "creator": "Elias Zamaria", + "createdAt": 1624227072000, + "text": "What is that supposed to do? I get errors like "123.split is not a function".", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 123, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e2b", + "creator": "Shubham Gupta", + "createdAt": 1628596565000, + "text": "
<input type="text" onKeyup="CommaWithDecimal(this.value, this.id)" class="form-control" id="number_format" placeholder="0.00" size="8" value="">\n
\n
\n

This function used for number formatting as well as decimal upto two value.\nIn this function handle all scenario for the input number.

\n
\n
function CommaWithDecimal(x,fid,valuetoAdd = 0) {\n\nx = x.replace(/[^0-9.,]/g, "");\nx = x.trim();\nvar isValueAdded = 0;\nif(x == '' && valuetoAdd != 0){\n  x = valuetoAdd.toString();\n  isValueAdded = 1;\n}\n\nif(x != ''){\n    if(parseInt(x) <= 0){\n        $('input[id$="'+fid+'"]').val(''); //not allow zero\n    }else if((x.indexOf(".") != -1 || x.indexOf(",") != -1) && x.length == 1){ //not allowed ,  and .\n        $('input[id$="'+fid+'"]').val('');\n    }else{\n\n        var isPoint = x.indexOf(".");\n        x = x.replace(/^0+/, ''); //remove leading zeros\n        x = x.replace(/\\,/g,''); //remove comma\n        x = x.replace('.00',''); //remove .00 from last\n        var pointArr = x.split('.');\n        var lastPointValue = 0;\n\n        if(pointArr.length > 1){\n            lastPointValue = pointArr[1];\n            if(lastPointValue != '' && lastPointValue.length >= 1){\n                lastPointValue = lastPointValue.substr(0, 2);\n\n            }\n        }\n\n        var x = pointArr[0];\n        if(x == ''){\n          x = 0;\n        }\n        if(isValueAdded == 0){\n          x = parseInt(x)+valuetoAdd;\n        }\n        x = x.toString();\n        var lastThree = x.substring(x.length-3);\n        var otherNumbers = x.substring(0,x.length-3);\n        if(otherNumbers != '')\n            lastThree = ',' + lastThree;\n        var res = otherNumbers.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ",") + lastThree;\n        if(isPoint != -1){\n            res = res+"."+lastPointValue;\n        }\n        $('input[id$="'+fid+'"]').val(res);\n    }\n}else{\n    $('input[id$="'+fid+'"]').val('');\n }\n}\n
\n
\n

Use this code and pass only function name with id and value in input field and see the result on your screen. As well as you can apply multiple places in same form just calling the function only. No need to extra code for multiple input field.

\n
\n
\n

Output - \"Output

\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e27", + "creator": "Robo Robok", + "createdAt": 1608225684000, + "text": "

If you're looking for a short and sweet solution:

\n
const number = 12345678.99;\n\nconst numberString = String(number).replace(\n    /^\\d+/,\n    number => [...number].map(\n        (digit, index, digits) => (\n            !index || (digits.length - index) % 3 ? '' : ','\n        ) + digit\n    ).join('')\n);\n\n// numberString: 12,345,678.99\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c6082fcc3049e92f00", + "creator": "Elias Zamaria", + "createdAt": 1608235493000, + "text": "That is telling me "Uncaught TypeError: number is not iterable". Maybe you need to call toString on the number.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c6082fcc3049e92f01", + "creator": "Robo Robok", + "createdAt": 1608235950000, + "text": "@EliasZamaria sorry, I used a string in my case. Updated my answer to convert to string.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c6082fcc3049e92f03", + "creator": "Elias Zamaria", + "createdAt": 1608242240000, + "text": "I tried 12.34 as the number and it returned 12,.34.", + "upvotes": 2691, + "upvoterUsernames": [], + "downvotes": 2691, + "downvoterUsernames": [] + }, + { + "_id": "62f329c6082fcc3049e92f04", + "creator": "Robo Robok", + "createdAt": 1608246489000, + "text": "I thought it was only meant to work with decimals. Updated for you.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90e2e", + "creator": "areg_noid", + "createdAt": 1648057645000, + "text": "

Related to @elias-zamaria and @t.j.crowder

\n

A negative lookbehind for Safari browser is can't use <. So, it would be (?!\\.\\d*)

\n
function numberWithCommas(n) {\n  return n.toString().replace(/\\B(?!\\.\\d*)(?=(\\d{3})+(?!\\d))/g, ",");\n}\n
\n

It works for Safari and Firefox

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e2d", + "creator": "The Lazy Dev Otaku", + "createdAt": 1645561072000, + "text": "
function processNumbers(x) {\n   // For large numbers to avoid getting results like 6.096,347,109,971,357e+27\n   x = BigInt(x);\n   return x.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90e2c", + "creator": "Ice_mank", + "createdAt": 1628785038000, + "text": "
var number = 2561556862056.12;\n\nconsole.log(new Intl.NumberFormat().format(number));\n
\n

This is the easiest way I found

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a0d082fcc3049e92fcd", + "creator": "Elias Zamaria", + "createdAt": 1628800148000, + "text": "FYI it looks like that is already mentioned here: stackoverflow.com/a/32154217/28324", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 9, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90d76", + "creator": "Boffin", + "createdAt": 1425942299000, + "text": "Number(x).toLocaleString()", + "upvotes": 371, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d77", + "creator": "Vitaly Zdanevich", + "createdAt": 1439730645000, + "text": "@Boffin this is not working for input type number (because of commas) - in accepted answer we can replace comma to dot and input type number will work", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d78", + "creator": "Tim", + "createdAt": 1495828496000, + "text": "However, it does appear to work in iOS safari.", + "upvotes": 232, + "upvoterUsernames": [], + "downvotes": 232, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d79", + "creator": "Bort", + "createdAt": 1506011354000, + "text": "toLocaleString is inconsistent and should not be used. For example - on Firefox this will return 1,234 but on IE this will add decimals: 1,234.00", + "upvotes": 427, + "upvoterUsernames": [], + "downvotes": 427, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d7a", + "creator": "atomless", + "createdAt": 1510245904000, + "text": "@RayToal it works fine now. Very strange!", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d7b", + "creator": "Khalid Habib", + "createdAt": 1515604510000, + "text": "Refer here for simple solution stackoverflow.com/a/48192732/1533783.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d7c", + "creator": "mplungjan", + "createdAt": 1588157550000, + "text": "@chadsteele not working on older browsers such as Safari 9", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d7d", + "creator": "Martijn", + "createdAt": 1619004987000, + "text": "Number.prototype.toLocaleString does work in Safari 14, in 2021.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d7e", + "creator": "Clonkex", + "createdAt": 1625636185000, + "text": "@Martijn Thank you for satisfying my curiosity :)", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1822945, + "uvac": 1822996 + } + }, + { + "_id": "62f321bb082fcc3049e8fec8", + "title": "Loop through an array in JavaScript", + "title-lowercase": "loop through an array in javascript", + "creator": "Mark Szymanski", + "createdAt": 1276128267000, + "status": "open", + "text": "

In Java, you can use a for loop to traverse objects in an array as follows:

\n
String[] myStringArray = {"Hello", "World"};\nfor (String s : myStringArray) {\n    // Do something\n}\n
\n

Can I do the same in JavaScript?

\n", + "upvotes": 7186, + "upvoterUsernames": [], + "downvotes": 3414, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 4832599, + "answers": 39, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e90512", + "creator": "sebarmeli", + "createdAt": 1291706689000, + "text": "

In JavaScript it's not advisable to loop through an Array with a for-in loop, but it's better to use a for loop such as:

\n
for(var i=0, len=myArray.length; i < len; i++){}\n
\n

It's optimized as well ("caching" the array length). If you'd like to learn more, read my post on the subject.

\n", + "upvotes": 204, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90511", + "creator": "hasen", + "createdAt": 1276128556000, + "text": "

You can use map, which is a functional programming technique that's also available in other languages like Python and Haskell.

\n\n
[1,2,3,4].map( function(item) {\n     alert(item);\n})\n
\n\n

The general syntax is:

\n\n
array.map(func)\n
\n\n

In general func would take one parameter, which is an item of the array. But in the case of JavaScript, it can take a second parameter which is the item's index, and a third parameter which is the array itself.

\n\n

The return value of array.map is another array, so you can use it like this:

\n\n
var x = [1,2,3,4].map( function(item) {return item * 10;});\n
\n\n

And now x is [10,20,30,40].

\n\n

You don't have to write the function inline. It could be a separate function.

\n\n
var item_processor = function(item) {\n      // Do something complicated to an item\n}\n\nnew_list = my_list.map(item_processor);\n
\n\n

which would be sort-of equivalent to:

\n\n
 for (item in my_list) {item_processor(item);}\n
\n\n

Except you don't get the new_list.

\n", + "upvotes": 662, + "upvoterUsernames": [], + "downvotes": 210, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b2082fcc3049e91970", + "creator": "harto", + "createdAt": 1276129247000, + "text": "That particular example is probably better implemented using Array.forEach. map is for generating a new array.", + "upvotes": 188, + "upvoterUsernames": [], + "downvotes": 85, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90514", + "creator": "Timo Huovinen", + "createdAt": 1325754913000, + "text": "

Use the while loop...

\n
var i = 0, item, items = ['one', 'two', 'three'];\nwhile(item = items[i++]){\n    console.log(item);\n}\n
\n

It logs: 'one', 'two', and 'three'

\n

And for the reverse order, an even more efficient loop:

\n
var items = ['one', 'two', 'three'], i = items.length;\nwhile(i--){\n    console.log(items[i]);\n}\n
\n

It logs: 'three', 'two', and 'one'

\n

Or the classical for loop:

\n
var items = ['one', 'two', 'three']\nfor(var i=0, l = items.length; i < l; i++){\n    console.log(items[i]);\n}\n
\n

It logs: 'one','two','three'

\n

Reference: Google Closure: How not to write JavaScript

\n", + "upvotes": 131, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e91972", + "creator": "Chris Cooper", + "createdAt": 1334587368000, + "text": "The first example of the "while" syntax won't work if any of the array elements is falsy.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90513", + "creator": "Gabriel", + "createdAt": 1305586324000, + "text": "

There is a way to do it where you have very little implicit scope in your loop and do away with extra variables.

\n
var i = 0,\n     item;\n\n// Note this is weak to sparse arrays or falsey values\nfor ( ; item = myStringArray[i++] ; ){\n    item; // This is the string at the index.\n}\n
\n

Or if you really want to get the id and have a really classical for loop:

\n
var i = 0,\n    len = myStringArray.length; // Cache the length\n\nfor ( ; i < len ; i++ ){\n    myStringArray[i]; // Don't use this if you plan on changing the length of the array\n}\n
\n

Modern browsers all support iterator methods forEach, map, reduce, filter and a host of other methods on the Array prototype.

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e91974", + "creator": "Noz", + "createdAt": 1406744089000, + "text": "@Gabriel I believe JavaScript already supports the map function on arrays, no need to introduce an additional lib for that.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90515", + "creator": "kirilloid", + "createdAt": 1334494218000, + "text": "

There's a method to iterate over only own object properties, not including prototype's ones:

\n\n
for (var i in array) if (array.hasOwnProperty(i)) {\n    // Do something with array[i]\n}\n
\n\n

but it still will iterate over custom-defined properties.

\n\n

In JavaScript any custom property could be assigned to any object, including an array.

\n\n

If one wants to iterate over sparsed array, for (var i = 0; i < array.length; i++) if (i in array) or array.forEach with es5shim should be used.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e91977", + "creator": "Daniel Sokolowski", + "createdAt": 1412865654000, + "text": "And how about using for (var i in array) if (++i) ?", + "upvotes": 129, + "upvoterUsernames": [], + "downvotes": 129, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90516", + "creator": "Andrew Thomson", + "createdAt": 1334619226000, + "text": "

I would thoroughly recommend making use of the Underscore.js library. It provides you with various functions that you can use to iterate over arrays/collections.

\n

For instance:

\n
_.each([1, 2, 3], function(num){ alert(num); });\n=> alerts each number in turn...\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90517", + "creator": "Muhammad Alvin", + "createdAt": 1334760366000, + "text": "

It's not 100% identical, but similar:

\n

\r\n
\r\n
   var myStringArray = ['Hello', 'World']; // The array uses [] not {}\n    for (var i in myStringArray) {\n        console.log(i + ' -> ' + myStringArray[i]); // i is the index/key, not the item\n    }
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90518", + "creator": "Phrogz", + "createdAt": 1338827163000, + "text": "

If you want a terse way to write a fast loop and you can iterate in reverse:

\n\n
for (var i=myArray.length;i--;){\n  var item=myArray[i];\n}\n
\n\n

This has the benefit of caching the length (similar to for (var i=0, len=myArray.length; i<len; ++i) and unlike for (var i=0; i<myArray.length; ++i)) while being fewer characters to type.

\n\n

There are even some times when you ought to iterate in reverse, such as when iterating over a live NodeList where you plan on removing items from the DOM during iteration.

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e9197b", + "creator": "danwellman", + "createdAt": 1367048023000, + "text": "falsish? You mean falsey. Let's all stick the proper terminology to avoid confusion ;)", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90519", + "creator": "justingordon", + "createdAt": 1350800448000, + "text": "

If you're using the jQuery library, consider using \nhttp://api.jquery.com/jQuery.each/

\n\n

From the documentation:

\n\n
\n

jQuery.each( collection, callback(indexInArray, valueOfElement) )

\n \n

Returns: Object

\n \n

Description: A generic iterator function, which can be used to\n seamlessly iterate over both objects and arrays. Arrays and array-like\n objects with a length property (such as a function's arguments object)\n are iterated by numeric index, from 0 to length-1. Other objects are\n iterated via their named properties.

\n \n

The $.each() function is not the same as $(selector).each(), which is\n used to iterate, exclusively, over a jQuery object. The $.each()\n function can be used to iterate over any collection, whether it is a\n map (JavaScript object) or an array. In the case of an array, the\n callback is passed an array index and a corresponding array value each\n time. (The value can also be accessed through the this keyword, but\n Javascript will always wrap the this value as an Object even if it is\n a simple string or number value.) The method returns its first\n argument, the object that was iterated.

\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9051a", + "creator": "Stijn de Witt", + "createdAt": 1362059968000, + "text": "

I did not yet see this variation, which I personally like the best:

\n\n

Given an array:

\n\n
var someArray = [\"some\", \"example\", \"array\"];\n
\n\n

You can loop over it without ever accessing the length property:

\n\n
for (var i=0, item; item=someArray[i]; i++) {\n  // item is \"some\", then \"example\", then \"array\"\n  // i is the index of item in the array\n  alert(\"someArray[\" + i + \"]: \" + item);\n}\n
\n\n

See this JsFiddle demonstrating that: http://jsfiddle.net/prvzk/

\n\n

This only works for arrays that are not sparse. Meaning that there actually is a value at each index in the array. However, I found that in practice I hardly ever use sparse arrays in JavaScript... In such cases it's usually a lot easier to use an object as a map/hashtable. If you do have a sparse array, and want to loop over 0 .. length-1, you need the for (var i=0; i<someArray.length; ++i) construct, but you still need an if inside the loop to check whether the element at the current index is actually defined.

\n\n

Also, as CMS mentions in a comment below, you can only use this on arrays that don't contain any falsish values. The array of strings from the example works, but if you have empty strings, or numbers that are 0 or NaN, etc. the loop will break off prematurely. Again in practice this is hardly ever a problem for me, but it is something to keep in mind, which makes this a loop to think about before you use it... That may disqualify it for some people :)

\n\n

What I like about this loop is:

\n\n\n\n

The reason this works is that the array specification mandates that when you read an item from an index >= the array's length, it will return undefined. When you write to such a location it will actually update the length.

\n\n

For me, this construct most closely emulates the Java 5 syntax that I love:

\n\n
for (String item : someArray) {\n}\n
\n\n

... with the added benefit of also knowing about the current index inside the loop

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e9197f", + "creator": "daniel1426", + "createdAt": 1395325025000, + "text": "The loop condition could be (item=someArray[i]) !== undefined.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9051c", + "creator": "Marlon Bernardes", + "createdAt": 1376236445000, + "text": "

for (const s of myStringArray) {

\n

(Directly answering your question: now you can!)

\n

Most other answers are right, but they do not mention (as of this writing) that ECMAScript  6  2015 is bringing a new mechanism for doing iteration, the for..of loop.

\n

This new syntax is the most elegant way to iterate an array in JavaScript (as long you don't need the iteration index).

\n

It currently works with Firefox 13+, Chrome 37+ and it does not natively work with other browsers (see browser compatibility below). Luckily we have JavaScript compilers (such as Babel) that allow us to use next-generation features today.

\n

It also works on Node.js (I tested it on version 0.12.0).

\n

Iterating an array

\n
// You could also use "let" or "const" instead of "var" for block scope.\nfor (var letter of ["a", "b", "c"]) {\n   console.log(letter);\n}\n
\n

Iterating an array of objects

\n
const band = [\n  {firstName : 'John', lastName: 'Lennon'},\n  {firstName : 'Paul', lastName: 'McCartney'}\n];\n\nfor(const member of band){\n  console.log(member.firstName + ' ' + member.lastName);\n}\n
\n

Iterating a generator:

\n

(example extracted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)

\n
function* fibonacci() { // A generator function\n  let [prev, curr] = [1, 1];\n  while (true) {\n    [prev, curr] = [curr, prev + curr];\n    yield curr;\n  }\n}\n\nfor (const n of fibonacci()) {\n  console.log(n);\n  // Truncate the sequence at 1000\n  if (n >= 1000) {\n    break;\n  }\n}\n
\n
\n

Compatibility table:\nhttp://kangax.github.io/compat-table/es6/#test-for..of_loops

\n

Specification: http://wiki.ecmascript.org/doku.php?id=harmony:iterators

\n

}

\n", + "upvotes": 184, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9051b", + "creator": "staticd", + "createdAt": 1375776205000, + "text": "
var x = [4, 5, 6];\nfor (i = 0, j = x[i]; i < x.length; j = x[++i]) {\n    console.log(i,j);\n}\n
\n\n

A lot cleaner...

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b3082fcc3049e91982", + "creator": "Sapphire_Brick", + "createdAt": 1584457041000, + "text": "That's not very clean compared to z.forEach(j => console.log(j));.", + "upvotes": 315, + "upvoterUsernames": [], + "downvotes": 315, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9051d", + "creator": "Zaheer Ahmed", + "createdAt": 1389473636000, + "text": "

The optimized approach is to cache the length of array and using the single variable pattern, initializing all variables with a single var keyword.

\n
var i, max, myStringArray = ["Hello", "World"];\nfor (i = 0, max = myStringArray.length; i < max; i++) {\n    alert(myStringArray[i]);\n\n    // Do something\n}\n
\n

If the order of iteration does not matter then you should try reversed loop. It is the fastest as it reduces overhead condition testing and decrement is in one statement:

\n
var i,myStringArray = ["item1","item2"];\nfor (i =  myStringArray.length; i--) {\n    alert(myStringArray[i]);\n}\n
\n

Or better and cleaner to use a while loop:

\n
var myStringArray = ["item1","item2"],i = myStringArray.length;\nwhile(i--) {\n   // Do something with fruits[i]\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9051e", + "creator": "molokoloco", + "createdAt": 1394244399000, + "text": "

The most elegant and fast way

\n
var arr = [1, 2, 3, 1023, 1024];\nfor (var value; value = arr.pop();) {\n    value + 1\n}\n
\n

http://jsperf.com/native-loop-performance/8

\n
\n

Edited (because I was wrong)

\n
\n

Comparing methods for looping through an array of 100000 items and do a minimal operation with the new value each time.

\n\n

Preparation:

\n
<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>\n<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>\n<script>\n    Benchmark.prototype.setup = function() {\n        // Fake function with minimal action on the value\n        var tmp = 0;\n        var process = function(value) {\n            tmp = value; // Hold a reference to the variable (prevent engine optimisation?)\n        };\n        \n        // Declare the test Array\n        var arr = [];\n        for (var i = 0; i < 100000; i++)\n            arr[i] = i;\n    };\n</script>\n
\n

Tests:

\n
<a href="http://jsperf.com/native-loop-performance/16" \n   title="http://jsperf.com/native-loop-performance/16"\n><img src="http://i.imgur.com/YTrO68E.png" title="Hosted by imgur.com" /></a>\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b4082fcc3049e91984", + "creator": "Deniz Ozger", + "createdAt": 1395848171000, + "text": "This loop doesn't seem to follow order of items in the array.", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 175, + "downvoterUsernames": [] + }, + { + "_id": "62f324b4082fcc3049e91986", + "creator": "Stijn de Witt", + "createdAt": 1400175276000, + "text": "@bergi is right. This loop wipes out the array as it loops through it. Not what you want in most cases.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b4082fcc3049e91987", + "creator": "njzk2", + "createdAt": 1406645965000, + "text": "breaks on falsey items.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90520", + "creator": "RizN81", + "createdAt": 1406120367000, + "text": "

There are various way to loop through array in JavaScript.

\n\n

Generic loop:

\n\n
var i;\nfor (i = 0; i < substr.length; ++i) {\n    // Do something with `substr[i]`\n}\n
\n\n

ES5's forEach:

\n\n
substr.forEach(function(item) {\n    // Do something with `item`\n});\n
\n\n

jQuery.each:

\n\n
jQuery.each(substr, function(index, item) {\n    // Do something with `item` (or `this` is also `item` if you like)\n});\n
\n\n

Have a look this for detailed information or you can also check MDN for looping through an array in JavaScript & using jQuery check jQuery for each.

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9051f", + "creator": "Sambhav Sharma", + "createdAt": 1399645308000, + "text": "

Well, how about this:

\n\n
for (var key in myStringArray) {\n    console.log(myStringArray[key]);\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b4082fcc3049e9198b", + "creator": "Sambhav Sharma", + "createdAt": 1399645923000, + "text": "well, thanks for the update.. and what about the situation when we don't care about the order of the array? Will this still be discouraged?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90522", + "creator": "Daniel K.", + "createdAt": 1449889904000, + "text": "

Sure it's inefficient and many despise it, but it's one of the closest to the mentioned:

\n\n
var myStringArray = [\"Hello\",\"World\"];\nmyStringArray.forEach(function(f){\n    // Do something\n})\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b4082fcc3049e9198d", + "creator": "user513951", + "createdAt": 1449890848000, + "text": "This exact functionality is already part of Mark Reed's answer.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90521", + "creator": "Joter", + "createdAt": 1413876720000, + "text": "

For example, I used in a Firefox console:

\n
[].forEach.call(document.getElementsByTagName('pre'), function(e){ \n   console.log(e);\n})\n
\n

You can use querySelectorAll to get same result

\n

\r\n
\r\n
document.querySelectorAll('pre').forEach( (e) => { \n   console.log(e.textContent);\n})
\r\n
<pre>text 1</pre>\n<pre>text 2</pre>\n<pre>text 3</pre>
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90524", + "creator": "Redoman", + "createdAt": 1459305434000, + "text": "

If you want to use jQuery, it has a nice example in its documentation:

\n\n
 $.each([ 52, 97 ], function( index, value ) {\n      alert( index + \": \" + value );\n });\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90523", + "creator": "Juanjo Salvador", + "createdAt": 1458209613000, + "text": "

Short answer: yes. You can do with this:

\n\n
var myArray = [\"element1\", \"element2\", \"element3\", \"element4\"];\n\nfor (i = 0; i < myArray.length; i++) {\n  console.log(myArray[i]);\n}\n
\n\n

In a browser console, you can see something like \"element1\", \"element2\", etc., printed.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90525", + "creator": "user6139250", + "createdAt": 1459423701000, + "text": "

It is better to use a sequential for loop:

\n\n
for (var i = 0; i < myStringArray.length; i++) {\n    // Do something\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b4082fcc3049e91993", + "creator": "Sapphire_Brick", + "createdAt": 1569610506000, + "text": "Why is it better? of course, you can do that in java as well, bu he asked about a foreach loop.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90526", + "creator": "Shubham Khatri", + "createdAt": 1461254622000, + "text": "

There are a couple of ways to do it in JavaScript. The first two examples are JavaScript samples. The third one makes use of a JavaScript library, that is, jQuery making use of the .each() function.

\n\n

\r\n
\r\n
var myStringArray = [\"hello\", \"World\"];\r\nfor(var i in myStringArray) {\r\n  alert(myStringArray[i]);\r\n}
\r\n
\r\n
\r\n

\n\n

\r\n
\r\n
var myStringArray = [\"hello\", \"World\"];\r\nfor (var i=0; i < myStringArray.length; i++) {\r\n  alert(myStringArray[i]);\r\n}
\r\n
\r\n
\r\n

\n\n

\r\n
\r\n
var myStringArray = [\"hello\", \"World\"];\r\n$.each(myStringArray, function(index, value){\r\n  alert(value);\r\n})
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b4082fcc3049e91995", + "creator": "brk", + "createdAt": 1480915556000, + "text": "for...in should be avoided for Array-like objects", + "upvotes": 488, + "upvoterUsernames": [], + "downvotes": 488, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90527", + "creator": "Amit Jamwal", + "createdAt": 1461920704000, + "text": "
var myStringArray = [\"hello\", \"World\"];\nmyStringArray.forEach(function(val, index){\n   console.log(val, index);\n})\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90528", + "creator": "Dan Chill", + "createdAt": 1464289866000, + "text": "
var obj = [\"one\",\"two\",\"three\"];\n\nfor(x in obj){\n    console.log(obj[x]);\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90529", + "creator": "bzim", + "createdAt": 1470086298000, + "text": "

Array loop:

\n\n
for(var i = 0; i < things.length; i++){\n    var thing = things[i];\n    console.log(thing);\n}\n
\n\n

Object loop:

\n\n
for(var prop in obj){\n    var propValue = obj[prop];\n    console.log(propValue);\n}\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9052a", + "creator": "Alongkorn", + "createdAt": 1476441118000, + "text": "

In JavaScript, there are so many solutions to loop an array.

\n\n

The code below are popular ones

\n\n

\r\n
\r\n
/** Declare inputs */\r\nconst items = ['Hello', 'World']\r\n\r\n/** Solution 1. Simple for */\r\nconsole.log('solution 1. simple for')\r\n\r\nfor (let i = 0; i < items.length; i++) {\r\n  console.log(items[i])\r\n}\r\n\r\nconsole.log()\r\nconsole.log()\r\n\r\n/** Solution 2. Simple while */\r\nconsole.log('solution 2. simple while')\r\n\r\nlet i = 0\r\nwhile (i < items.length) {\r\n  console.log(items[i++])\r\n}\r\n\r\nconsole.log()\r\nconsole.log()\r\n\r\n/** Solution 3. forEach*/\r\nconsole.log('solution 3. forEach')\r\n\r\nitems.forEach(item => {\r\n  console.log(item)\r\n})\r\n\r\nconsole.log()\r\nconsole.log()\r\n\r\n/** Solution 4. for-of*/\r\nconsole.log('solution 4. for-of')\r\n\r\nfor (const item of items) {\r\n  console.log(item)\r\n}\r\n\r\nconsole.log()\r\nconsole.log()
\r\n
\r\n
\r\n

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9052b", + "creator": "Espen", + "createdAt": 1477471899000, + "text": "

The best way in my opinion is to use the Array.forEach function. If you cannot use that I would suggest to get the polyfill from MDN. To make it available, it is certainly the safest way to iterate over an array in JavaScript.

\n\n

Array.prototype.forEach()

\n\n

So as others has suggested, this is almost always what you want:

\n\n
var numbers = [1,11,22,33,44,55,66,77,88,99,111];\nvar sum = 0;\nnumbers.forEach(function(n){\n  sum += n;\n});\n
\n\n

This ensures that anything you need in the scope of processing the array stays within that scope, and that you are only processing the values of the array, not the object properties and other members, which is what for .. in does.

\n\n

Using a regular C-style for loop works in most cases. It is just important to remember that everything within the loop shares its scope with the rest of your program, the { } does not create a new scope.

\n\n

Hence:

\n\n
var sum = 0;\nvar numbers = [1,11,22,33,44,55,66,77,88,99,111];\n\nfor(var i = 0; i<numbers.length; ++i){\n  sum += numbers[i];\n}\n\nalert(i);\n
\n\n

will output \"11\" - which may or may not be what you want.

\n\n

A working jsFiddle example:\nhttps://jsfiddle.net/workingClassHacker/pxpv2dh5/7/

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9052c", + "creator": "Alireza", + "createdAt": 1495853861000, + "text": "

Yes, you can do the same in JavaScript using a loop, but not limited to that. There are many ways to do a loop over arrays in JavaScript. Imagine you have this array below, and you'd like to do a loop over it:

\n
var arr = [1, 2, 3, 4, 5];\n
\n

These are the solutions:

\n

1) For loop

\n

A for loop is a common way looping through arrays in JavaScript, but it is no considered as the fastest solutions for large arrays:

\n
for (var i=0, l=arr.length; i<l; i++) {\n  console.log(arr[i]);\n}\n
\n

2) While loop

\n

A while loop is considered as the fastest way to loop through long arrays, but it is usually less used in the JavaScript code:

\n
let i=0;\n\nwhile (arr.length>i) {\n    console.log(arr[i]);\n    i++;\n}\n
\n

3) Do while
\nA do while is doing the same thing as while with some syntax difference as below:

\n
let i=0;\ndo {\n  console.log(arr[i]);\n  i++;\n}\nwhile (arr.length>i);\n
\n

These are the main ways to do JavaScript loops, but there are a few more ways to do that.

\n

Also we use a for in loop for looping over objects in JavaScript.

\n

Also look at the map(), filter(), reduce(), etc. functions on an Array in JavaScript. They may do things much faster and better than using while and for.

\n

This is a good article if you like to learn more about the asynchronous functions over arrays in JavaScript.

\n
\n

Functional programming has been making quite a splash in the\ndevelopment world these days. And for good reason: Functional\ntechniques can help you write more declarative code that is easier to\nunderstand at a glance, refactor, and test.

\n

One of the cornerstones of functional programming is its special use\nof lists and list operations. And those things are exactly what the\nsound like they are: arrays of things, and the stuff you do to them.\nBut the functional mindset treats them a bit differently than you\nmight expect.

\n

This article will take a close look at what I like to call the "big\nthree" list operations: map, filter, and reduce. Wrapping your head\naround these three functions is an important step towards being able\nto write clean functional code, and opens the doors to the vastly\npowerful techniques of functional and reactive programming.

\n

It also means you'll never have to write a for loop again.

\n
\n

Read more>> here:

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9052e", + "creator": "BILAL AHMAD", + "createdAt": 1505340663000, + "text": "

Just a simple one-line solution:

\n\n

\r\n
\r\n
arr = [\"table\", \"chair\"];\r\n\r\n// Solution\r\narr.map((e) => {\r\n  console.log(e);\r\n  return e;\r\n});
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b5082fcc3049e9199c", + "creator": "Michel Jung", + "createdAt": 1507288168000, + "text": "You'd rather want to use .forEach() and drop the return e;", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9052d", + "creator": "Andrii Starusiev", + "createdAt": 1504384532000, + "text": "

It seems that all the variants were listed, except forEach by lodash:

\n\n
_.forEach([1, 2], (value) => {\n  console.log(value);\n});\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90530", + "creator": "Shijo Rs", + "createdAt": 1539091023000, + "text": "

\r\n
\r\n
var array = ['hai', 'hello', 'how', 'are', 'you']\r\n$(document).ready(function () {\r\n  $('#clickButton').click(function () {\r\n    for (var i = 0; i < array.length; i++) {\r\n      alert(array[i])\r\n    }\r\n  })\r\n})
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js\"></script>\r\n<input id=\"clickButton\" value=\"click Me\" type=\"button\"/>\r\n<div id=\"show\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b5082fcc3049e9199f", + "creator": "domdambrogia", + "createdAt": 1551826002000, + "text": "Was it really necessary to bring jQuery or HTML into this?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f324b5082fcc3049e919a1", + "creator": "Peter Mortensen", + "createdAt": 1604046520000, + "text": "@domdambrogia: It is a meme.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9052f", + "creator": "colxi", + "createdAt": 1534808161000, + "text": "

If anybody is interested in the performance side of the multiple mechanisms available for Array iterations, I've prepared the following JSPerf tests:

\n\n

https://jsperf.com/fastest-array-iterator

\n\n

\"Performance

\n\n

Results:

\n\n

The traditional for() iterator, is by far the fastest method, especially when used with the array length cached.

\n\n
let arr = [1,2,3,4,5];\n\nfor(let i=0, size=arr.length; i<size; i++){\n    // Do something\n}\n
\n\n

The Array.prototype.forEach() and the Array.prototype.map() methods are the slowest approximations, probably as a consequence of the function call overhead.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b5082fcc3049e919a3", + "creator": "DarckBlezzer", + "createdAt": 1544465956000, + "text": "is better use i = i +1 instead of i++", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [] + }, + { + "_id": "62f324b5082fcc3049e919a5", + "creator": "colxi", + "createdAt": 1558099431000, + "text": "@PowerStat can you provide a link or reference about that ? I've never aheard about it, sounds interesting...", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90531", + "creator": "Sapphire_Brick", + "createdAt": 1565830209000, + "text": "

The formal (and perhaps old) way is Array.prototype.forEach(...):

\n
var arr = ["apple", "banana", "cherry", "mango"];\narr.forEach(function(item, index, _) {\n   console.log("[" + index + "] = '" + item + "'");\n});\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90532", + "creator": "Kamil Kiełczewski", + "createdAt": 1576737609000, + "text": "

Esoteric mutable way

\n

\r\n
\r\n
let a= [\"Hello\", \"World\"];\n\nwhile(a.length) { console.log( a.shift() ); }
\r\n
\r\n
\r\n

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b6082fcc3049e919a7", + "creator": "Sapphire_Brick", + "createdAt": 1579142947000, + "text": "that's the Haskell-y way to do it; keep taking the first one. clever, but probably slow.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919a8", + "creator": "Kamil Kiełczewski", + "createdAt": 1595585200000, + "text": "@Sapphire_Brick actually it is quite fast - here is test", + "upvotes": 193, + "upvoterUsernames": [], + "downvotes": 193, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919a9", + "creator": "Sapphire_Brick", + "createdAt": 1595625860000, + "text": "Of course a simple program like this is fast, but how does it scale in comparison to for(...;...;...) or for(... of ...)?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919aa", + "creator": "Arthur S", + "createdAt": 1604952191000, + "text": "This isn't esoteric. Its simply unnecessary and makes an assumption that the a variable will not be used in further code.", + "upvotes": 182, + "upvoterUsernames": [], + "downvotes": 182, + "downvoterUsernames": [] + }, + { + "_id": "62f324b6082fcc3049e919ac", + "creator": "Kamil Kiełczewski", + "createdAt": 1614576084000, + "text": "@Pitouli you are right - I rollback answer to its initial form. When I have more time then I will perform benchamarks again", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90534", + "creator": "Satish Chandra Gupta", + "createdAt": 1622874110000, + "text": "

6 different methods to loop through the array

\n

You can loop through an array by many different methods. I have sorted my 6 favorite methods from top to bottom.

\n

1. Using for loop

\n

When it's to simply loop through an array, the for loop is my first choice.

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\nfor (let i = 0; i < array.length; i++) {\n  console.log(array[i]);\n}
\r\n
\r\n
\r\n

\n

2. Using forEach loop

\n

forEach loop is a modern way to loop through the array. Also, it gives more flexibility and control over the array and elements.

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\narray.forEach((element) => {\n  console.log(element);\n});
\r\n
\r\n
\r\n

\n

3. Using for...of

\n

for...of loop gives you direct access to the array elements.

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\nfor (let element of array) {\n  console.log(element);\n}
\r\n
\r\n
\r\n

\n

4. Using for...in loop

\n

for...in gives you a key using which you can access array elements.

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\nfor(let index in array){\n  console.log(array[index]);\n}
\r\n
\r\n
\r\n

\n

5. Using while loop

\n

while loop is can be used to loop through the array as well.

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\nlet length = array.length;\nwhile(length > 0){\n  console.log(array[array.length - length]);\n  length--;\n}
\r\n
\r\n
\r\n

\n

6. Using do...while loop

\n

Likewise, I use do...while loop

\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5];\nlet length = array.length;\ndo {\n  console.log(array[array.length - length]);\n  length--;\n}\nwhile (length > 0)
\r\n
\r\n
\r\n

\n", + "upvotes": 148, + "upvoterUsernames": [], + "downvotes": 61, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90533", + "creator": "TheComputerWizard", + "createdAt": 1606521900000, + "text": "
//Make array\nvar array = ["1","2","3","4","5","6","7","8","9","10"]\n//Loop\nfor(var i = 0; i < array.length; i++){\n console.log((i+1) + " --> " + array[i])\n}\n
\n

For the ACTUAL number for i, you need to change (i+1) to i or (i), if you want.
\nHope this helped.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90535", + "creator": "Aleksandr Golovatyi", + "createdAt": 1623765788000, + "text": "

There are 4 ways of array iteration:

\n
// 1: for\n\nfor (let i = 0; i < arr.length; ++i) {\n  console.log(arr[i]);\n}\n\n// 2: forEach\n\narr.forEach((v, i) => console.log(v));\n\n// 3: for in\n\nfor (let i in arr) {\n  console.log(arr[i]);\n}\n\n// 4: for of\n\nfor (const v of arr) {\n  console.log(v);\n}\n
\n

Summary: 1 and 3 solutions create extra variable, 2 - create extra function context. The best way is 4th - "for of".

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f324b6082fcc3049e919ae", + "creator": "YesItsMe", + "createdAt": 1627956282000, + "text": "do you care to elaborate why 4 "for of" is the best over the others", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90536", + "creator": "Alfredo Rahn Linde", + "createdAt": 1636516096000, + "text": "

Consider this:

\n
const ITEMS = ['One','Two','Three']\nlet item=-1\n\n//Then, you get looping with every call of:\n\nITEMS[item=item==ITEMS.length-1?0:item+1]\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90537", + "creator": "Arun s", + "createdAt": 1659706156000, + "text": "

There are multiple ways to do it in javascript. Here are the common ways of handling arrays.

\n

Method 1:

\n
const students = ["Arun","Jos","John","Kiran"]\nfor (var index = 0; index < students.length; index++) {\n  console.log(students[index]);\n}\n
\n

Method 2:

\n

students.forEach((student, index) => console.log(student));

\n

Method 3:

\n
for (const student of students) {\n      console.log(student);\n}\n  \n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c0082fcc3049e90451", + "creator": "alex", + "createdAt": 1450433386000, + "text": "related - stackoverflow.com/q/6208964/31671", + "upvotes": 121, + "upvoterUsernames": [], + "downvotes": 121, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 4839786, + "uvac": 4839825 + } + }, + { + "_id": "62f321bb082fcc3049e8fee1", + "title": "How can I check for "undefined" in JavaScript?", + "title-lowercase": "how can i check for "undefined" in javascript?", + "creator": "makerofthings7", + "createdAt": 1280771609000, + "status": "open", + "text": "

What is the most appropriate way to test if a variable is undefined in JavaScript?

\n

I've seen several possible ways:

\n
if (window.myVariable)\n
\n

Or

\n
if (typeof(myVariable) != "undefined")\n
\n

Or

\n
if (myVariable) // This throws an error if undefined. Should this be in Try/Catch?\n
\n", + "upvotes": 3781, + "upvoterUsernames": [], + "downvotes": 880, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3278666, + "answers": 15, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e90971", + "creator": "Guffa", + "createdAt": 1275855796000, + "text": "

If it is undefined, it will not be equal to a string that contains the characters \"undefined\", as the string is not undefined.

\n\n

You can check the type of the variable:

\n\n
if (typeof(something) != \"undefined\") ...\n
\n\n

Sometimes you don't even have to check the type. If the value of the variable can't evaluate to false when it's set (for example if it's a function), then you can just evalue the variable. Example:

\n\n
if (something) {\n  something(param);\n}\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e9212e", + "creator": "Tim Down", + "createdAt": 1275855833000, + "text": "No need for the parentheses: typeof is an operator, not a function.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9212f", + "creator": "Nick Craver", + "createdAt": 1275855872000, + "text": "@Tim - It can be used both ways.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90970", + "creator": "Jacob Relkin", + "createdAt": 1275855737000, + "text": "

You can use typeof, like this:

\n\n
if (typeof something != \"undefined\") {\n    // ...\n}\n
\n", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e92131", + "creator": "James", + "createdAt": 1275855950000, + "text": "Or just something !== undefined, assuming you've already done var undefined, pre-cautiously.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92132", + "creator": "Thomas Eding", + "createdAt": 1553225951000, + "text": "This is inaccurate. You absolutely don't need to use typeof.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92133", + "creator": "Thomas Eding", + "createdAt": 1557415150000, + "text": "@Kamafeather Look at the many other answers to see that there are alternatives. Hence you don't need to use typeof.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90972", + "creator": "Mathias Bynens", + "createdAt": 1275855997000, + "text": "
if (typeof foo == 'undefined') {\n // Do something\n};\n
\n\n

Note that strict comparison (!==) is not necessary in this case, since typeof will always return a string.

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e92135", + "creator": "James", + "createdAt": 1275858330000, + "text": "What's with the semi-colon (};)?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92136", + "creator": "Gumbo", + "createdAt": 1275859568000, + "text": "@J-P: The semicolon after the closing brace is just an empty statement.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92137", + "creator": "James", + "createdAt": 1275861061000, + "text": "@Gumbo, sorry, what I meant to ask was: "What purpose is the semi-colon serving?"", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90973", + "creator": "Anurag", + "createdAt": 1280771898000, + "text": "

If you are interested in finding out whether a variable has been declared regardless of its value, then using the in operator is the safest way to go. Consider this example:

\n\n
// global scope\nvar theFu; // theFu has been declared, but its value is undefined\ntypeof theFu; // \"undefined\"\n
\n\n

But this may not be the intended result for some cases, since the variable or property was declared but just not initialized. Use the in operator for a more robust check.

\n\n
\"theFu\" in window; // true\n\"theFoo\" in window; // false\n
\n\n

If you are interested in knowing whether the variable hasn't been declared or has the value undefined, then use the typeof operator, which is guaranteed to return a string:

\n\n
if (typeof myVar !== 'undefined')\n
\n\n

Direct comparisons against undefined are troublesome as undefined can be overwritten.

\n\n
window.undefined = \"foo\";\n\"foo\" == undefined // true\n
\n\n

As @CMS pointed out, this has been patched in ECMAScript 5th ed., and undefined is non-writable.

\n\n

if (window.myVar) will also include these falsy values, so it's not very robust:

\n\n
\nfalse\n0\n\"\"\nNaN\nnull\nundefined\n
\n\n

Thanks to @CMS for pointing out that your third case - if (myVariable) can also throw an error in two cases. The first is when the variable hasn't been defined which throws a ReferenceError.

\n\n
// abc was never declared.\nif (abc) {\n    // ReferenceError: abc is not defined\n} \n
\n\n

The other case is when the variable has been defined, but has a getter function which throws an error when invoked. For example,

\n\n
// or it's a property that can throw an error\nObject.defineProperty(window, \"myVariable\", { \n    get: function() { throw new Error(\"W00t?\"); }, \n    set: undefined \n});\nif (myVariable) {\n    // Error: W00t?\n}\n
\n", + "upvotes": 3766, + "upvoterUsernames": [], + "downvotes": 676, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e92138", + "creator": "Christian C. Salvadó", + "createdAt": 1280772733000, + "text": "@Anurag, the third case will throw a ReferenceError if myVariable is not declared...", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92139", + "creator": "Anurag", + "createdAt": 1280772905000, + "text": "@CMS - thanks, I somehow imagined myVariable was declared somewhere, just without a value.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9213b", + "creator": "Thomas Eding", + "createdAt": 1280773971000, + "text": "I abhor your argument against using undefined. (Also, you want triple equal signs for comparision against it.)", + "upvotes": 2588, + "upvoterUsernames": [], + "downvotes": 2588, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9213c", + "creator": "makerofthings7", + "createdAt": 1280776189000, + "text": "Can "typeof" be redefined?", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9213e", + "creator": "MooGoo", + "createdAt": 1280777519000, + "text": "typeof is a language statement, it cannot be redefined any more than if/else/while/for/function etc. could be.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92140", + "creator": "makerofthings7", + "createdAt": 1280778243000, + "text": "@MooGoo If thats the case, then I'll infer that language statements in general can't be redefined ;)", + "upvotes": 431, + "upvoterUsernames": [], + "downvotes": 431, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92141", + "creator": "makerofthings7", + "createdAt": 1280778319000, + "text": "Would anyone know the minification difference between npup's approach and trinthis's approach of myVar === undefined", + "upvotes": 1413, + "upvoterUsernames": [], + "downvotes": 1413, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92142", + "creator": "makerofthings7", + "createdAt": 1288735830000, + "text": "Anurag - Thanks for the update... you have the best answer and I'd +1 you again if possible..", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92143", + "creator": "Jonathan DS", + "createdAt": 1334756016000, + "text": "is it okay to check truth with if(window.myVar) ?", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92145", + "creator": "Paul S.", + "createdAt": 1353603996000, + "text": "undefined is immutable in modern browsers. Setting window.undefined does nothing.", + "upvotes": 138, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92147", + "creator": "andig", + "createdAt": 1421741606000, + "text": "Can I compare with undefined instead of using typeof()=='undefined'?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92149", + "creator": "Ry-", + "createdAt": 1423528544000, + "text": "@andig: Yes. Never use typeof to check for undefined.", + "upvotes": 437, + "upvoterUsernames": [], + "downvotes": 437, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9214a", + "creator": "asifaftab87", + "createdAt": 1434966387000, + "text": "typeof myVar is really useful.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9214c", + "creator": "Diego Faria", + "createdAt": 1452266236000, + "text": "In modern browsers, you can override undefined only inside a scope that is not global.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9214e", + "creator": "serge", + "createdAt": 1537368463000, + "text": "function(x){// how here to identify if x is set or not?}", + "upvotes": 187, + "upvoterUsernames": [], + "downvotes": 187, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90975", + "creator": "Tim Down", + "createdAt": 1280772311000, + "text": "

2020 Update

\n\n

One of my reasons for preferring a typeof check (namely, that undefined can be redefined) became irrelevant with the mass adoption of ECMAScript 5. The other, that you can use typeof to check the type of an undeclared variable, was always niche. Therefore, I'd now recommend using a direct comparison in most situations:

\n\n
myVariable === undefined\n
\n\n

Original answer from 2010

\n\n

Using typeof is my preference. It will work when the variable has never been declared, unlike any comparison with the == or === operators or type coercion using if. (undefined, unlike null, may also be redefined in ECMAScript 3 environments, making it unreliable for comparison, although nearly all common environments now are compliant with ECMAScript 5 or above).

\n\n
if (typeof someUndeclaredVariable == \"undefined\") {\n    // Works\n}\n\nif (someUndeclaredVariable === undefined) { \n    // Throws an error\n}\n
\n", + "upvotes": 634, + "upvoterUsernames": [], + "downvotes": 305, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e9214f", + "creator": "Jamie Pate", + "createdAt": 1372441481000, + "text": "'xyz' in window or 'xyz' in self are much better", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92151", + "creator": "Tim Down", + "createdAt": 1372454415000, + "text": "@JamiePate: What about window.foo = undefined? "foo" in window will return true.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92152", + "creator": "Jamie Pate", + "createdAt": 1372476393000, + "text": "don't do that, use null instead. if you have to you can 'xyz' in window && window.xyz !== undefined", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90974", + "creator": "Cristian Sanchez", + "createdAt": 1280772141000, + "text": "

I use it as a function parameter and exclude it on function execution that way I get the \"real\" undefined. Although it does require you to put your code inside a function. I found this while reading the jQuery source.

\n\n
undefined = 2;\n\n(function (undefined) {\n   console.log(undefined); // prints out undefined\n   // and for comparison:\n   if (undeclaredvar === undefined) console.log(\"it works!\")\n})()\n
\n\n

Of course you could just use typeof though. But all my code is usually inside a containing function anyways, so using this method probably saves me a few bytes here and there.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90976", + "creator": "Thomas Eding", + "createdAt": 1280773611000, + "text": "

I personally use

\n\n
myVar === undefined\n
\n\n

Warning: Please note that === is used over == and that myVar has been previously declared (not defined).

\n\n
\n\n

I do not like typeof myVar === \"undefined\". I think it is long winded and unnecessary. (I can get the same done in less code.)

\n\n

Now some people will keel over in pain when they read this, screaming: \"Wait! WAAITTT!!! undefined can be redefined!\"

\n\n

Cool. I know this. Then again, most variables in Javascript can be redefined. Should you never use any built-in identifier that can be redefined?

\n\n

If you follow this rule, good for you: you aren't a hypocrite.

\n\n

The thing is, in order to do lots of real work in JS, developers need to rely on redefinable identifiers to be what they are. I don't hear people telling me that I shouldn't use setTimeout because someone can

\n\n
window.setTimeout = function () {\n    alert(\"Got you now!\");\n};\n
\n\n

Bottom line, the \"it can be redefined\" argument to not use a raw === undefined is bogus.

\n\n

(If you are still scared of undefined being redefined, why are you blindly integrating untested library code into your code base? Or even simpler: a linting tool.)

\n\n
\n\n

Also, like the typeof approach, this technique can \"detect\" undeclared variables:

\n\n
if (window.someVar === undefined) {\n    doSomething();\n}\n
\n\n

But both these techniques leak in their abstraction. I urge you not to use this or even

\n\n
if (typeof myVar !== \"undefined\") {\n    doSomething();\n}\n
\n\n

Consider:

\n\n
var iAmUndefined;\n
\n\n

To catch whether or not that variable is declared or not, you may need to resort to the in operator. (In many cases, you can simply read the code O_o).

\n\n
if (\"myVar\" in window) {\n    doSomething();\n}\n
\n\n

But wait! There's more! What if some prototype chain magic is happening…? Now even the superior in operator does not suffice. (Okay, I'm done here about this part except to say that for 99% of the time, === undefined (and ****cough**** typeof) works just fine. If you really care, you can read about this subject on its own.)

\n", + "upvotes": 1674, + "upvoterUsernames": [], + "downvotes": 217, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c1082fcc3049e92154", + "creator": "drzaus", + "createdAt": 1390939677000, + "text": "i clarified your answer based on the back-and-forth with @Laurent so that "casual readers" won't be confused.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92155", + "creator": "TheZ", + "createdAt": 1411683174000, + "text": "If you're worried about undefined being redefined just do myVar === void 0", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92157", + "creator": "Spongman", + "createdAt": 1412296716000, + "text": "very bad advice. some library could define a global 'undefined' and kill your code. use the 'typeof' method, it's safe.", + "upvotes": 429, + "upvoterUsernames": [], + "downvotes": 429, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e92159", + "creator": "Tim Down", + "createdAt": 1425752468000, + "text": "@Andy: Definitely, which is one reason not to use undefined on the left hand side of a comparison.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9215a", + "creator": "Andy", + "createdAt": 1425958217000, + "text": "@TimDown yeah, I certainly hope no one has a strange habit of putting null or undefined on the lhs, in any programming language!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9215b", + "creator": "Thomas Eding", + "createdAt": 1455654590000, + "text": "@GrandOpener: -Wall -Werror. QED. Now the code doesn't have to read like something that came out of Voynich manuscript.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9215c", + "creator": "Steve Tolba", + "createdAt": 1539155896000, + "text": "Actually the undefined without quotes worked for me", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [] + }, + { + "_id": "62f326c1082fcc3049e9215d", + "creator": "Mahdi Khalili", + "createdAt": 1562750257000, + "text": "its not working for me in chrome , uncaught refrence error when you never defined myvar but typedef myvar works", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e90977", + "creator": "anmarti", + "createdAt": 1353602843000, + "text": "

Since none of the other answers helped me, I suggest doing this. It worked for me in Internet Explorer 8:

\n\n
if (typeof variable_name.value === 'undefined') {\n    // variable_name is undefined\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90979", + "creator": "Zenexer", + "createdAt": 1383281559000, + "text": "

Update 2018-07-25

\n\n

It's been nearly five years since this post was first made, and JavaScript has come a long way. In repeating the tests in the original post, I found no consistent difference between the following test methods:

\n\n\n\n

Even when I modified the tests to prevent Chrome from optimizing them away, the differences were insignificant. As such, I'd now recommend abc === undefined for clarity.

\n\n

Relevant content from chrome://version:

\n\n\n\n

Original post 2013-11-01

\n\n

In Google Chrome, the following was ever so slightly faster than a typeof test:

\n\n
if (abc === void 0) {\n    // Undefined\n}\n
\n\n

The difference was negligible. However, this code is more concise, and clearer at a glance to someone who knows what void 0 means. Note, however, that abc must still be declared.

\n\n

Both typeof and void were significantly faster than comparing directly against undefined. I used the following test format in the Chrome developer console:

\n\n
var abc;\nstart = +new Date();\nfor (var i = 0; i < 10000000; i++) {\n    if (TEST) {\n        void 1;\n    }\n}\nend = +new Date();\nend - start;\n
\n\n

The results were as follows:

\n\n
Test: | abc === undefined      abc === void 0      typeof abc == 'undefined'\n------+---------------------------------------------------------------------\nx10M  |     13678 ms               9854 ms                 9888 ms\n  x1  |    1367.8 ns              985.4 ns                988.8 ns\n
\n\n

Note that the first row is in milliseconds, while the second row is in nanoseconds. A difference of 3.4 nanoseconds is nothing. The times were pretty consistent in subsequent tests.

\n", + "upvotes": 140, + "upvoterUsernames": [], + "downvotes": 69, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c2082fcc3049e92160", + "creator": "Zenexer", + "createdAt": 1392709728000, + "text": "I think the best compromise between clarity and speed, given these numbers (which are from a while ago), is the typeof test.", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9097a", + "creator": "Marthijn", + "createdAt": 1387449742000, + "text": "

In this article I read that frameworks like Underscore.js use this function:

\n\n
function isUndefined(obj){\n    return obj === void 0;\n}\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90978", + "creator": "Vikas", + "createdAt": 1365828552000, + "text": "

On the contrary of @Thomas Eding answer:

\n\n

If I forget to declare myVar in my code, then I'll get myVar is not defined.

\n\n

Let's take a real example:

\n\n

I've a variable name, but I am not sure if it is declared somewhere or not.

\n\n

Then @Anurag's answer will help:

\n\n
var myVariableToCheck = 'myVar';\nif (window[myVariableToCheck] === undefined)\n    console.log(\"Not declared or declared, but undefined.\");\n\n// Or you can check it directly \nif (window['myVar'] === undefined) \n    console.log(\"Not declared or declared, but undefined.\");\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9097b", + "creator": "Hrishi", + "createdAt": 1387535080000, + "text": "

Personally, I always use the following:

\n\n
var x;\nif( x === undefined) {\n    //Do something here\n}\nelse {\n   //Do something else here\n}\n
\n\n

The window.undefined property is non-writable in all modern browsers (JavaScript 1.8.5 or later). From Mozilla's documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined, I see this: One reason to use typeof() is that it does not throw an error if the variable has not been defined.

\n\n

I prefer to have the approach of using

\n\n
x === undefined \n
\n\n

because it fails and blows up in my face rather than silently passing/failing if x has not been declared before. This alerts me that x is not declared. I believe all variables used in JavaScript should be declared.

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c2082fcc3049e92162", + "creator": "FruitBreak", + "createdAt": 1448895511000, + "text": "Great point about wanting undeclared variable to blow up - this does not happen with typeof.", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9097c", + "creator": "Joseph Gabriel", + "createdAt": 1392816054000, + "text": "

The most reliable way I know of checking for undefined is to use void 0.

\n\n

This is compatible with newer and older browsers, alike, and cannot be overwritten like window.undefined can in some cases.

\n\n
if( myVar === void 0){\n    //yup it's undefined\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326c2082fcc3049e92165", + "creator": "Brian M. Hunt", + "createdAt": 1411495113000, + "text": "This is underrated and IMHO a preferable way to check for something being undefined.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326c2082fcc3049e92167", + "creator": "Thomas Eding", + "createdAt": 1508906389000, + "text": "Absolutely correct, but I imagine if undefined !== void 0, you likely have other serious problems in said codebase.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326c2082fcc3049e92168", + "creator": "Kamafeather", + "createdAt": 1557353397000, + "text": "And it exposes to throwing error when myVar has not been previously declared.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e9097d", + "creator": "Gaurav", + "createdAt": 1393958257000, + "text": "
    var x;\n    if (x === undefined) {\n        alert (\"I am declared, but not defined.\")\n    };\n    if (typeof y === \"undefined\") {\n        alert (\"I am not even declared.\")\n    };\n\n    /* One more thing to understand: typeof ==='undefined' also checks \n       for if a variable is declared, but no value is assigned. In other \n       words, the variable is declared, but not defined. */\n\n    // Will repeat above logic of x for typeof === 'undefined'\n    if (x === undefined) {\n        alert (\"I am declared, but not defined.\")\n    };\n    /* So typeof === 'undefined' works for both, but x === undefined \n       only works for a variable which is at least declared. */\n\n    /* Say if I try using typeof === undefined (not in quotes) for \n       a variable which is not even declared, we will get run a \n       time error. */\n\n    if (z === undefined) {\n        alert (\"I am neither declared nor defined.\")\n    };\n    // I got this error for z ReferenceError: z is not defined \n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e9097e", + "creator": "sourcecode", + "createdAt": 1407481787000, + "text": "
// x has not been defined before\nif (typeof x === 'undefined') { // Evaluates to true without errors.\n   // These statements execute.\n}\n\nif (x === undefined) { // Throws a ReferenceError\n\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321c5082fcc3049e9096c", + "creator": "Nick Craver", + "createdAt": 1275855995000, + "text": "Do you want to check for only undefined, or null as well?", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e9096d", + "creator": "Daniel Schaffer", + "createdAt": 1280772593000, + "text": "@Robert - that question has an accepted answer that answers here have proven to be wrong", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e9096e", + "creator": "DCShannon", + "createdAt": 1456371342000, + "text": "That "duplicate" is about object properties, so some of the answers don't apply very well to this question, asking about variables.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321c5082fcc3049e9096f", + "creator": "Steve Tolba", + "createdAt": 1539155994000, + "text": "myVariable === undefined", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3282451, + "uvac": 3282466 + } + }, + { + "_id": "62f321bb082fcc3049e8ff01", + "title": "Get the size of the screen, current web page and browser window", + "title-lowercase": "get the size of the screen, current web page and browser window", + "creator": "turtledove", + "createdAt": 1281335338000, + "status": "open", + "text": "

How can I get windowWidth, windowHeight, pageWidth, pageHeight, screenWidth, screenHeight, pageX, pageY, screenX, screenY which will work in all major browsers?

\n\n

\"screenshot

\n", + "upvotes": 3148, + "upvoterUsernames": [], + "downvotes": 812, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1990808, + "answers": 18, + "answerItems": [ + { + "_id": "62f321cd082fcc3049e90ebd", + "creator": "dude", + "createdAt": 1328934352000, + "text": "
function wndsize(){\n  var w = 0;var h = 0;\n  //IE\n  if(!window.innerWidth){\n    if(!(document.documentElement.clientWidth == 0)){\n      //strict mode\n      w = document.documentElement.clientWidth;h = document.documentElement.clientHeight;\n    } else{\n      //quirks mode\n      w = document.body.clientWidth;h = document.body.clientHeight;\n    }\n  } else {\n    //w3c\n    w = window.innerWidth;h = window.innerHeight;\n  }\n  return {width:w,height:h};\n}\nfunction wndcent(){\n  var hWnd = (arguments[0] != null) ? arguments[0] : {width:0,height:0};\n  var _x = 0;var _y = 0;var offsetX = 0;var offsetY = 0;\n  //IE\n  if(!window.pageYOffset){\n    //strict mode\n    if(!(document.documentElement.scrollTop == 0)){offsetY = document.documentElement.scrollTop;offsetX = document.documentElement.scrollLeft;}\n    //quirks mode\n    else{offsetY = document.body.scrollTop;offsetX = document.body.scrollLeft;}}\n    //w3c\n    else{offsetX = window.pageXOffset;offsetY = window.pageYOffset;}_x = ((wndsize().width-hWnd.width)/2)+offsetX;_y = ((wndsize().height-hWnd.height)/2)+offsetY;\n    return{x:_x,y:_y};\n}\nvar center = wndcent({width:350,height:350});\ndocument.write(center.x+';<br>');\ndocument.write(center.y+';<br>');\ndocument.write('<DIV align=\"center\" id=\"rich_ad\" style=\"Z-INDEX: 10; left:'+center.x+'px;WIDTH: 350px; POSITION: absolute; TOP: '+center.y+'px; HEIGHT: 350px\"><!--К сожалению, у Вас не установлен flash плеер.--></div>');\n
\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ebe", + "creator": "sidonaldson", + "createdAt": 1343749945000, + "text": "

This has everything you need to know: Get viewport/window size

\n\n

but in short:

\n\n
var win = window,\n    doc = document,\n    docElem = doc.documentElement,\n    body = doc.getElementsByTagName('body')[0],\n    x = win.innerWidth || docElem.clientWidth || body.clientWidth,\n    y = win.innerHeight|| docElem.clientHeight|| body.clientHeight;\nalert(x + ' × ' + y);\n
\n\n

Fiddle

\n\n

Please stop editing this answer. It's been edited 22 times now by different people to match their code format preference. It's also been pointed out that this isn't required if you only want to target modern browsers - if so you only need the following:

\n\n
const width  = window.innerWidth || document.documentElement.clientWidth || \ndocument.body.clientWidth;\nconst height = window.innerHeight|| document.documentElement.clientHeight|| \ndocument.body.clientHeight;\n\nconsole.log(width, height);\n
\n", + "upvotes": 1763, + "upvoterUsernames": [], + "downvotes": 666, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3291f082fcc3049e92a6c", + "creator": "a paid nerd", + "createdAt": 1390793575000, + "text": "Why not g = document.body ?", + "upvotes": 124, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a6e", + "creator": "Michael Mikowski", + "createdAt": 1390862054000, + "text": "@apaidnerd: Standards defying browsers like IE8 do not support document.body. IE9, however, does.", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 42, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a70", + "creator": "Nux", + "createdAt": 1411847321000, + "text": "@MichaelMikowski That is not true! Even IE5 supports document.body.", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a71", + "creator": "wybe", + "createdAt": 1527982062000, + "text": "I would just like to very quierly remark that one-letter variable names are never helpful.", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ebc", + "creator": "Ankit Jaiswal", + "createdAt": 1281335963000, + "text": "

You can get the size of the window or document with jQuery:

\n\n
// Size of browser viewport.\n$(window).height();\n$(window).width();\n\n// Size of HTML document (same as pageHeight/pageWidth in screenshot).\n$(document).height();\n$(document).width();\n
\n\n

For screen size you can use the screen object:

\n\n
window.screen.height;\nwindow.screen.width;\n
\n", + "upvotes": 2997, + "upvoterUsernames": [], + "downvotes": 1394, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3291f082fcc3049e92a74", + "creator": "turtledove", + "createdAt": 1281407100000, + "text": "thanks, and is there any way to get pageX, pageY, screenX, screenY?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a76", + "creator": "Ankit Jaiswal", + "createdAt": 1397450582000, + "text": "@mrplants These are all in pixels.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a78", + "creator": "Ankit Jaiswal", + "createdAt": 1461202073000, + "text": "@Alberto the original question when asked was asking about a Jquery solution and has been updated several times since then.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a79", + "creator": "Adi Prasetyo", + "createdAt": 1462033277000, + "text": "could you update which explain browser viewport, html document and screen?", + "upvotes": 704, + "upvoterUsernames": [], + "downvotes": 704, + "downvoterUsernames": [] + }, + { + "_id": "62f3291f082fcc3049e92a7b", + "creator": "Adam Arold", + "createdAt": 1483453421000, + "text": "Stop giving jquery specific answers. The OP DID NOT ask for a jquery answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ebf", + "creator": "Daniel W.", + "createdAt": 1371722763000, + "text": "

A non-jQuery way to get the available screen dimension. window.screen.width/height has already been put up, but for responsive webdesign and completeness sake I think its worth to mention those attributes:

\n\n
alert(window.screen.availWidth);\nalert(window.screen.availHeight);\n
\n\n

http://www.quirksmode.org/dom/w3c_cssom.html#t10 :

\n\n
\n

availWidth and availHeight - The available width and height on the\n screen (excluding OS taskbars and such).

\n
\n", + "upvotes": 164, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec0", + "creator": "serfer2", + "createdAt": 1408617843000, + "text": "

You can also get the WINDOW width and height, avoiding browser toolbars and other stuff. It is the real usable area in browser's window.

\n\n

To do this, use:\nwindow.innerWidth and window.innerHeight properties (see doc at w3schools).

\n\n

In most cases it will be the best way, in example, to display a perfectly centred floating modal dialog. It allows you to calculate positions on window, no matter which resolution orientation or window size is using the browser.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec1", + "creator": "confile", + "createdAt": 1422639873000, + "text": "

Here is a cross browser solution with pure JavaScript (Source):

\n\n
var width = window.innerWidth\n|| document.documentElement.clientWidth\n|| document.body.clientWidth;\n\nvar height = window.innerHeight\n|| document.documentElement.clientHeight\n|| document.body.clientHeight;\n
\n", + "upvotes": 701, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32920082fcc3049e92a7e", + "creator": "WinEunuuchs2Unix", + "createdAt": 1632959992000, + "text": "@Randy Can't an old Windows XP machine simply run a new version of Fire-Fox or whatever?", + "upvotes": 173, + "upvoterUsernames": [], + "downvotes": 173, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ec2", + "creator": "hashchange", + "createdAt": 1425508200000, + "text": "

If you need a truly bulletproof solution for the document width and height (the pageWidth and pageHeight in the picture), you might want to consider using a plugin of mine, jQuery.documentSize.

\n\n

It has just one purpose: to always return the correct document size, even in scenarios when jQuery and other methods fail. Despite its name, you don't necessarily have to use jQuery – it is written in vanilla Javascript and works without jQuery, too.

\n\n

Usage:

\n\n
var w = $.documentWidth(),\n    h = $.documentHeight();\n
\n\n

for the global document. For other documents, e.g. in an embedded iframe you have access to, pass the document as a parameter:

\n\n
var w = $.documentWidth( myIframe.contentDocument ),\n    h = $.documentHeight( myIframe.contentDocument );\n
\n\n

Update: now for window dimensions, too

\n\n

Ever since version 1.1.0, jQuery.documentSize also handles window dimensions.

\n\n

That is necessary because

\n\n\n\n

jQuery.documentSize provides $.windowWidth() and $.windowHeight(), which solve these issues. For more, please check out the documentation.

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec3", + "creator": "Aabha Pandey", + "createdAt": 1429687795000, + "text": "

But when we talk about responsive screens and if we want to handle it using jQuery for some reason,

\n\n
window.innerWidth, window.innerHeight\n
\n\n

gives the correct measurement. Even it removes the scroll-bar's extra space and we don't need to worry about adjusting that space :)

\n", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec4", + "creator": "Achim Krauß", + "createdAt": 1444897858000, + "text": "

In some cases related with responsive layout $(document).height() can return wrong data that displays view port height only. \nFor example when some div#wrapper has height:100%, that #wrapper can be stretched by some block inside it. But it's height still will be like viewport height. In such situation you might use

\n\n
$('#wrapper').get(0).scrollHeight\n
\n\n

That represents actual size of wrapper.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec6", + "creator": "Andi Giga", + "createdAt": 1459416355000, + "text": "

I wrote a small javascript bookmarklet you can use to display the size. You can easily add it to your browser and whenever you click it you will see the size in the right corner of your browser window.

\n\n

Here you find information how to use a bookmarklet \nhttps://en.wikipedia.org/wiki/Bookmarklet

\n\n

Bookmarklet

\n\n
javascript:(function(){!function(){var i,n,e;return n=function(){var n,e,t;return t=\"background-color:azure; padding:1rem; position:fixed; right: 0; z-index:9999; font-size: 1.2rem;\",n=i('<div style=\"'+t+'\"></div>'),e=function(){return'<p style=\"margin:0;\">width: '+i(window).width()+\" height: \"+i(window).height()+\"</p>\"},n.html(e()),i(\"body\").prepend(n),i(window).resize(function(){n.html(e())})},(i=window.jQuery)?(i=window.jQuery,n()):(e=document.createElement(\"script\"),e.src=\"http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js\",e.onload=n,document.body.appendChild(e))}()}).call(this);\n
\n\n

Original Code

\n\n

The original code is in coffee:

\n\n
(->\n  addWindowSize = ()->\n    style = 'background-color:azure; padding:1rem; position:fixed; right: 0; z-index:9999; font-size: 1.2rem;'\n    $windowSize = $('<div style=\"' + style + '\"></div>')\n\n    getWindowSize = ->\n      '<p style=\"margin:0;\">width: ' + $(window).width() + ' height: ' + $(window).height() + '</p>'\n\n    $windowSize.html getWindowSize()\n    $('body').prepend $windowSize\n    $(window).resize ->\n      $windowSize.html getWindowSize()\n      return\n\n  if !($ = window.jQuery)\n    # typeof jQuery=='undefined' works too\n    script = document.createElement('script')\n    script.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js'\n    script.onload = addWindowSize\n    document.body.appendChild script\n  else\n    $ = window.jQuery\n    addWindowSize()\n)()\n
\n\n

Basically the code is prepending a small div which updates when you resize your window.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec5", + "creator": "Zach Barham", + "createdAt": 1446785249000, + "text": "

You can use the Screen object to get this.

\n\n

The following is an example of what it would return:

\n\n
Screen {\n    availWidth: 1920,\n    availHeight: 1040,\n    width: 1920,\n    height: 1080,\n    colorDepth: 24,\n    pixelDepth: 24,\n    top: 414,\n    left: 1920,\n    availTop: 414,\n    availLeft: 1920\n}\n
\n\n

To get your screenWidth variable, just use screen.width, same with screenHeight, you would just use screen.height.

\n\n

To get your window width and height, it would be screen.availWidth or screen.availHeight respectively.

\n\n

For the pageX and pageY variables, use window.screenX or Y. Note that this is from the VERY LEFT/TOP OF YOUR LEFT/TOP-est SCREEN. So if you have two screens of width 1920 then a window 500px from the left of the right screen would have an X value of 2420 (1920+500). screen.width/height, however, display the CURRENT screen's width or height.

\n\n

To get the width and height of your page, use jQuery's $(window).height() or $(window).width().

\n\n

Again using jQuery, use $(\"html\").offset().top and $(\"html\").offset().left for your pageX and pageY values.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec8", + "creator": "solanki...", + "createdAt": 1468038152000, + "text": "

To check height and width of your current loaded page of any website using \"console\" or after clicking \"Inspect\".

\n\n

step 1: Click the right button of mouse and click on 'Inspect' and then click 'console'

\n\n

step 2: Make sure that your browser screen should be not in 'maximize' mode. If the browser screen is in 'maximize' mode, you need to first click the maximize button (present either at right or left top corner) and un-maximize it.

\n\n

step 3: Now, write the following after the greater than sign ('>') i.e.

\n\n
       > window.innerWidth\n            output : your present window width in px (say 749)\n\n       > window.innerHeight\n            output : your present window height in px (say 359)\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec7", + "creator": "pyrsmk", + "createdAt": 1460535416000, + "text": "

I developed a library for knowing the real viewport size for desktops and mobiles browsers, because viewport sizes are inconsistents across devices and cannot rely on all the answers of that post (according to all the research I made about this) : https://github.com/pyrsmk/W

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eca", + "creator": "Prashant Yalatwar", + "createdAt": 1561532348000, + "text": "

This how I managed to get the screen width in React JS Project:

\n\n

If width is equal to 1680 then return 570 else return 200

\n\n
var screenWidth = window.screen.availWidth;\n\n<Label style={{ width: screenWidth == \"1680\" ? 570 : 200, color: \"transparent\" }}>a  </Label>\n
\n\n

Screen.availWidth

\n", + "upvotes": 317, + "upvoterUsernames": [], + "downvotes": 317, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ecb", + "creator": "Hamza Iftikhar", + "createdAt": 1575365170000, + "text": "

Complete guide related to Screen sizes

\n\n

JavaScript

\n\n

For height:

\n\n
document.body.clientHeight  // Inner height of the HTML document body, including padding \n                            // but not the horizontal scrollbar height, border, or margin\n\nscreen.height               // Device screen height (i.e. all physically visible stuff)\nscreen.availHeight          // Device screen height minus the operating system taskbar (if present)\nwindow.innerHeight          // The current document's viewport height, minus taskbars, etc.\nwindow.outerHeight          // Height the current window visibly takes up on screen \n                            // (including taskbars, menus, etc.)\n
\n\n

Note: When the window is maximized this will equal screen.availHeight

\n\n

For width:

\n\n
document.body.clientWidth   // Full width of the HTML page as coded, minus the vertical scroll bar\nscreen.width                // Device screen width (i.e. all physically visible stuff)\nscreen.availWidth           // Device screen width, minus the operating system taskbar (if present)\nwindow.innerWidth           // The browser viewport width (including vertical scroll bar, includes padding but not border or margin)\nwindow.outerWidth           // The outer window width (including vertical scroll bar,\n                            // toolbars, etc., includes padding and border but not margin)\n
\n\n

Jquery

\n\n

For height:

\n\n
$(document).height()    // Full height of the HTML page, including content you have to \n                        // scroll to see\n\n$(window).height()      // The current document's viewport height, minus taskbars, etc.\n$(window).innerHeight() // The current document's viewport height, minus taskbars, etc.\n$(window).outerHeight() // The current document's viewport height, minus taskbars, etc.                         \n
\n\n

For width:

\n\n
$(document).width()     // The browser viewport width, minus the vertical scroll bar\n$(window).width()       // The browser viewport width (minus the vertical scroll bar)\n$(window).innerWidth()  // The browser viewport width (minus the vertical scroll bar)\n$(window).outerWidth()  // The browser viewport width (minus the vertical scroll bar)\n
\n\n

Reference: https://help.optimizely.com/Build_Campaigns_and_Experiments/Use_screen_measurements_to_design_for_responsive_breakpoints

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ec9", + "creator": "user8202629", + "createdAt": 1505277381000, + "text": "

here is my solution!

\n\n

\r\n
\r\n
// innerWidth\r\nconst screen_viewport_inner = () => {\r\n    let w = window,\r\n        i = `inner`;\r\n    if (!(`innerWidth` in window)) {\r\n        i = `client`;\r\n        w = document.documentElement || document.body;\r\n    }\r\n    return {\r\n        width: w[`${i}Width`],\r\n        height: w[`${i}Height`]\r\n    }\r\n};\r\n\r\n\r\n// outerWidth\r\nconst screen_viewport_outer = () => {\r\n    let w = window,\r\n        o = `outer`;\r\n    if (!(`outerWidth` in window)) {\r\n        o = `client`;\r\n        w = document.documentElement || document.body;\r\n    }\r\n    return {\r\n        width: w[`${o}Width`],\r\n        height: w[`${o}Height`]\r\n    }\r\n};\r\n\r\n// style\r\nconst console_color = `\r\n    color: rgba(0,255,0,0.7);\r\n    font-size: 1.5rem;\r\n    border: 1px solid red;\r\n`;\r\n\r\n\r\n\r\n// testing\r\nconst test = () => {\r\n    let i_obj = screen_viewport_inner();\r\n    console.log(`%c screen_viewport_inner = \\n`, console_color, JSON.stringify(i_obj, null, 4));\r\n    let o_obj = screen_viewport_outer();\r\n    console.log(`%c screen_viewport_outer = \\n`, console_color, JSON.stringify(o_obj, null, 4));\r\n};\r\n\r\n// IIFE\r\n(() => {\r\n    test();\r\n})();
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ecc", + "creator": "Prashant Gupta", + "createdAt": 1578753324000, + "text": "

With the introduction of globalThis in ES2020 you can use properties like.

\n\n

For screen size:

\n\n
globalThis.screen.availWidth \nglobalThis.screen.availHeight\n
\n\n

For Window Size

\n\n
globalThis.outerWidth\nglobalThis.outerHeight\n
\n\n

For Offset:

\n\n
globalThis.pageXOffset\nglobalThis.pageYOffset\n
\n\n

...& so on.

\n\n

\r\n
\r\n
alert(\"Screen Width: \"+ globalThis.screen.availWidth +\"\\nScreen Height: \"+ globalThis.screen.availHeight)
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ecd", + "creator": "DarkTrick", + "createdAt": 1617352063000, + "text": "

Graphical answer:\n\"Graphical\n(............)

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321cd082fcc3049e90eb9", + "creator": "befzz", + "createdAt": 1386793796000, + "text": "pageHeight(on a pic) u can get with: document.body.scrollHeight", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90eba", + "creator": "Déjà vu", + "createdAt": 1406800761000, + "text": "Interesting: ryanve.com/lab/dimensions", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ebb", + "creator": "Roy Prins", + "createdAt": 1488358130000, + "text": "As others have commented under the answers, there are better solutions than the accepted answer. Please consider changing the accepted answer.", + "upvotes": 2937, + "upvoterUsernames": [], + "downvotes": 2937, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1993959, + "uvac": 1993977 + } + }, + { + "_id": "62f321bb082fcc3049e8fed7", + "title": "How do I format a date in JavaScript?", + "title-lowercase": "how do i format a date in javascript?", + "creator": "leora", + "createdAt": 1282606106000, + "status": "open", + "text": "

How do I format a Date object to a string?

\n", + "upvotes": 6091, + "upvoterUsernames": [], + "downvotes": 2942, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 5703163, + "answers": 54, + "answerItems": [ + { + "_id": "62f321c4082fcc3049e907c6", + "creator": "RobertPitt", + "createdAt": 1282606547000, + "text": "

Use the date.format library:

\n\n
var dateFormat = require('dateformat');\nvar now = new Date();\ndateFormat(now, \"dddd, mmmm dS, yyyy, h:MM:ss TT\");\n
\n\n

returns:

\n\n
Saturday, June 9th, 2007, 5:46:21 PM \n
\n\n

dateformat on npm

\n\n

http://jsfiddle.net/phZr7/1/

\n", + "upvotes": 1196, + "upvoterUsernames": [], + "downvotes": 521, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907c8", + "creator": "simo", + "createdAt": 1340477382000, + "text": "

Well, what I wanted was to convert today's date to a MySQL friendly date string like 2012-06-23, and to use that string as a parameter in one of my queries. The simple solution I've found is this:

\n
var today = new Date().toISOString().slice(0, 10);\n
\n

Keep in mind that the above solution does not take into account your timezone offset.

\n

You might consider using this function instead:

\n
function toJSONLocal (date) {\n    var local = new Date(date);\n    local.setMinutes(date.getMinutes() - date.getTimezoneOffset());\n    return local.toJSON().slice(0, 10);\n}\n
\n

This will give you the correct date in case you are executing this code around the start/end of the day.

\n

\r\n
\r\n
var date = new Date();\n\nfunction toLocal(date) {\n  var local = new Date(date);\n  local.setMinutes(date.getMinutes() - date.getTimezoneOffset());\n  return local.toJSON();\n}\n\nfunction toJSONLocal(date) {\n  var local = new Date(date);\n  local.setMinutes(date.getMinutes() - date.getTimezoneOffset());\n  return local.toJSON().slice(0, 10);\n}\n\n// check out your devtools console\nconsole.log(date.toJSON());\nconsole.log(date.toISOString());\nconsole.log(toLocal(date));\n\nconsole.log(toJSONLocal(date));
\r\n
\r\n
\r\n

\n\n", + "upvotes": 1072, + "upvoterUsernames": [], + "downvotes": 529, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e2082fcc3049e91ed4", + "creator": "Vajk Hermecz", + "createdAt": 1445551291000, + "text": "You can do new Date(date + " UTC") to trick the timezone, and you can eliminate the setMinutes line. Man, javascript is dirty", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ed6", + "creator": "Alex Shaffer", + "createdAt": 1456406846000, + "text": "Y10K compatible version: var today = new Date().toISOString().slice(0,-14) :)", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ed8", + "creator": "rofrol", + "createdAt": 1464879478000, + "text": "Or like this new Date().toISOString().split('T')[0]", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91eda", + "creator": "Gerrie van Wyk", + "createdAt": 1524686093000, + "text": "new Date().toISOString().slice(0, 16).replace('T',' ') to include time", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91edc", + "creator": "nsandersen", + "createdAt": 1564064488000, + "text": "@slang True, but the lifespan of my program isn't Y10K compatible either :). I'll bank on there being AI to sort this out by then.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ede", + "creator": "Salman A", + "createdAt": 1654079908000, + "text": "This is simply a bad idea. toISOString will change the date to UTC so it could be +/- 1 day at given time of day.", + "upvotes": 603, + "upvoterUsernames": [], + "downvotes": 603, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907c7", + "creator": "Sébastien", + "createdAt": 1325223187000, + "text": "

I think you can just use the non-standard Date method toLocaleFormat(formatString)

\n

formatString: A format string in the same format expected by the strftime() function in C.

\n
var today = new Date();\ntoday.toLocaleFormat('%d-%b-%Y'); // 30-Dec-2011\n
\n

References:

\n\n

Edited to add: toLocaleFormat has now been deprecated, as the link above indicates.

\n", + "upvotes": 264, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e2082fcc3049e91ee0", + "creator": "fitzgeraldsteele", + "createdAt": 1339448545000, + "text": "toLocaleFormat() appears to only work in Firefox. Both IE and Chrome are failing for me.", + "upvotes": 282, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ee2", + "creator": "Filipe Madureira", + "createdAt": 1593591273000, + "text": "Property 'toLocaleFormat' does not exist on type 'Date'.", + "upvotes": 6496, + "upvoterUsernames": [], + "downvotes": 6496, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ee4", + "creator": "barshopen", + "createdAt": 1627551640000, + "text": "This function is deprecated these days", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ee6", + "creator": "Lle.4", + "createdAt": 1639513309000, + "text": "Deprecated as of Dec 2021", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e2082fcc3049e91ee8", + "creator": "Mohsen Alyafei", + "createdAt": 1647005844000, + "text": "This is no longer a valid Javascript method for date formatting", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907ca", + "creator": "hasen", + "createdAt": 1350943850000, + "text": "

Sugar.js has excellent extensions to the Date object, including a Date.format method.

\n\n

Examples from the documentation:

\n\n
Date.create().format('{Weekday} {Month} {dd}, {yyyy}');\n\nDate.create().format('{12hr}:{mm}{tt}')\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907c9", + "creator": "Thulasiram", + "createdAt": 1340971374000, + "text": "

Add the jQuery UI plugin to your page:

\n\n
function DateFormate(dateFormate, datetime) {\n    return $.datepicker.formatDate(dateFormate, datetime);\n};\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e3082fcc3049e91eea", + "creator": "Bennett McElwee", + "createdAt": 1441767938000, + "text": "Don't add jQuery and jQuery UI just to format a date!", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e3082fcc3049e91eeb", + "creator": "Alexander", + "createdAt": 1454505935000, + "text": "why not plain jquery? $.format.date(new Date(), 'yyyy-MM-dd HH:mm:ss')", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f325e3082fcc3049e91eec", + "creator": "Samuel Lindblom", + "createdAt": 1641593651000, + "text": "@Alexander That is not plain jquery, that's another plugin.", + "upvotes": 6747, + "upvoterUsernames": [], + "downvotes": 6747, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907cc", + "creator": "Tony Brix", + "createdAt": 1405436790000, + "text": "

Here is a script that does exactly what you want

\n\n

https://github.com/UziTech/js-date-format

\n\n
var d = new Date(\"2010-8-10\");\ndocument.write(d.format(\"DD-MMM-YYYY\"));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907cb", + "creator": "Dmitry Pavlov", + "createdAt": 1404913947000, + "text": "

If you are already using jQuery UI in your project you could do it this way:

\n\n
var formatted = $.datepicker.formatDate(\"M d, yy\", new Date(\"2014-07-08T09:02:21.377\"));\n\n// formatted will be 'Jul 8, 2014'\n
\n\n

Some datepicker date format options to play with are available here.

\n", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907cd", + "creator": "Prasad DLV", + "createdAt": 1405932591000, + "text": "

A JavaScript solution without using any external libraries:

\n\n
var now = new Date()\nmonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\nvar formattedDate = now.getDate() + \"-\" + months[now.getMonth()] + \"-\" + now.getFullYear()\nalert(formattedDate)\n
\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ce", + "creator": "user956584", + "createdAt": 1412268054000, + "text": "

@Sébastien -- alternative all browser support

\n
new Date(parseInt(496407600)*1000).toLocaleDateString('de-DE', {\nyear: 'numeric',\nmonth: '2-digit',\nday: '2-digit'\n}).replace(/\\./g, '/');\n
\n

Documentation:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString

\n
\n

High-order tagged template literal example based on Date.toLocaleDateString:

\n
const date = new Date(Date.UTC(2020, 4, 2, 3, 23, 16, 738));\nconst fmt = (dt, lc = "en-US") => (str, ...expr) =>\n    str.map((str, i) => str + (expr[i]?dt.toLocaleDateString(lc, expr[i]) :'')).join('')\n\nconsole.log(fmt(date)`${{year: 'numeric'}}-${{month: '2-digit'}}-${{day: '2-digit'}}`);\n// expected output: "2020-05-02"\n
\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e3082fcc3049e91ef2", + "creator": "Roberto14", + "createdAt": 1425040165000, + "text": "Instead of doing .replace(), you could simply use 'en-GB' as locale. :)", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907cf", + "creator": "webzy", + "createdAt": 1416231175000, + "text": "

If you are using jQuery UI in your code, there is an inbuilt function called formatDate(). I am using it this way to format today's date:

\n\n
var testdate = Date();\ntestdate = $.datepicker.formatDate( \"d-M-yy\",new Date(testdate));\nalert(testdate);\n
\n\n

You can see many other examples of formatting date in the jQuery UI documentation.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d0", + "creator": "Mite Mitreski", + "createdAt": 1419416123000, + "text": "

Plain JavaScript is the best pick for small onetimers.

\n\n

On the other hand, if you need more date stuff, MomentJS is a great solution.

\n\n

For example:

\n\n
moment().format('YYYY-MM-DD HH:m:s');     // now() -> 2015-03-24 14:32:20\nmoment(\"20111031\", \"YYYYMMDD\").fromNow(); // 3 years ago\nmoment(\"20120620\", \"YYYYMMDD\").fromNow(); // 3 years ago\nmoment().startOf('day').fromNow();        // 11 hours ago\nmoment().endOf('day').fromNow();          // in 13 hours\n
\n", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d1", + "creator": "pat capozzi", + "createdAt": 1429652044000, + "text": "

Use:

\n\n
thisDate = new Date(parseInt(jsonDateString.replace('/Date(', '')));\nformattedDate = (thisDate.getMonth() + 1) + \"/\" + (thisDate.getDate()+1) + \"/\" + thisDate.getFullYear();\n
\n\n

This takes a JSON date, \"/Date(1429573751663)/\" and produces as the formatted string:

\n\n

\"4/21/2015\"

\n", + "upvotes": 145, + "upvoterUsernames": [], + "downvotes": 145, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e3082fcc3049e91ef5", + "creator": "Mark Amery", + "createdAt": 1446249938000, + "text": "There is no such thing as a JSON date.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907d2", + "creator": "sebastian.i", + "createdAt": 1431759753000, + "text": "

If you need to quickly format your date using plain JavaScript, use getDate, getMonth + 1, getFullYear, getHours and getMinutes:

\n\n
var d = new Date();\n\nvar datestring = d.getDate()  + \"-\" + (d.getMonth()+1) + \"-\" + d.getFullYear() + \" \" +\nd.getHours() + \":\" + d.getMinutes();\n\n// 16-5-2015 9:50\n
\n\n

Or, if you need it to be padded with zeros:

\n\n
var datestring = (\"0\" + d.getDate()).slice(-2) + \"-\" + (\"0\"+(d.getMonth()+1)).slice(-2) + \"-\" +\n    d.getFullYear() + \" \" + (\"0\" + d.getHours()).slice(-2) + \":\" + (\"0\" + d.getMinutes()).slice(-2);\n\n// 16-05-2015 09:50\n
\n", + "upvotes": 1135, + "upvoterUsernames": [], + "downvotes": 427, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e3082fcc3049e91ef7", + "creator": "Benny Jobigan", + "createdAt": 1547548231000, + "text": "you can also pad zeros with .toString().padStart(2, '0')", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f325e3082fcc3049e91ef9", + "creator": "JHH", + "createdAt": 1558092811000, + "text": "@BennyJobigan It should be mentioned that String.padStart() is only available from ECMAScript 2017.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907d4", + "creator": "Peter", + "createdAt": 1447230717000, + "text": "

If you are already using ExtJS in your project you could use Ext.Date:

\n\n
var date = new Date();\nExt.Date.format(date, \"d-M-Y\");\n
\n\n

returns:

\n\n
\"11-Nov-2015\"\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d3", + "creator": "JD Smith", + "createdAt": 1444843500000, + "text": "

Requested format in one line - no libraries and no Date methods, just regex:

\n
var d = (new Date()).toString().replace(/\\S+\\s(\\S+)\\s(\\d+)\\s(\\d+)\\s.*/,'$2-$1-$3');\n// date will be formatted as "14-Oct-2015" (pass any date object in place of 'new Date()')\n
\n

In my testing, this works reliably in the major browsers (Chrome, Safari, Firefox and IE.) As @RobG pointed out, the output of Date.prototype.toString() is implementation-dependent, so for international or non-browser implementations, just test the output to be sure it works right in your JavaScript engine. You can even add some code to test the string output and make sure it's matching what you expect before you do the regex replace.

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d5", + "creator": "SaidB", + "createdAt": 1447231619000, + "text": "

I use the following. It is simple and works fine.

\n\n
 var dtFormat = require('dtformat');\n   var today = new Date();\n   dtFormat(today, \"dddd, mmmm dS, yyyy, h:MM:ss TT\");\n
\n\n

Or this:

\n\n
var now = new Date()\nmonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\nvar formattedDate = now.getDate()  + \"-\" + months[now.getMonth()] + \"-\" + now.getFullYear()\nalert(formattedDate)\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e4082fcc3049e91efd", + "creator": "Rahul Tagore", + "createdAt": 1573194660000, + "text": "Second one worked for me, without including any library. Thanks :)", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907d6", + "creator": "Gene R", + "createdAt": 1447835818000, + "text": "

There is a new library, smarti.to.js, for localized formatting of JavaScript numbers, dates and JSON dates (Microsoft or ISO8601).

\n\n

Example:

\n\n
new Date('2015-1-1').to('dd.MM.yy')         // Outputs 01.01.2015\n\"2015-01-01T10:11:12.123Z\".to('dd.MM.yy')   // Outputs 01.01.2015\n
\n\n

There are also custom short patterns defined in the localization file (smarti.to.{culture}.js). Example (smarti.to.et-EE.js):

\n\n
new Date('2015-1-1').to('d')                // Outputs 1.01.2015\n
\n\n

And a multiformatting ability:

\n\n
smarti.format('{0:n2} + {1:n2} = {2:n2}', 1, 2, 3)   // Output: 1,00 + 2,00 = 3,00\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d7", + "creator": "JP de la Torre", + "createdAt": 1450715693000, + "text": "

Short, widely compatible approach:

\n\n
function formatDate(date) {\n    date.toISOString()\n    .replace(/^(\\d+)-(\\d+)-(\\d+).*$/, // Only extract Y-M-D\n        function (a,y,m,d) {\n            return [\n                d, // Day\n                ['Jan','Feb','Mar','Apr','May','Jun',  // Month Names\n                'Jul','Ago','Sep','Oct','Nov','Dec']\n                [m-1], // Month\n                y  // Year\n            ].join('-') // Stitch together\n        })\n}\n
\n\n

Or, as a single line:\n

\n\n
date.toISOString().replace(/^(\\d+)-(\\d+)-(\\d+)T(\\d+):(\\d+):(\\d+).(\\d+)Z$/, function (a,y,m,d) {return [d,['Jan','Feb','Mar','Apr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'][m-1],y].join('-')})\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d8", + "creator": "Arjun Nayak", + "createdAt": 1451556069000, + "text": "

Try this:

\n\n

\r\n
\r\n
function init(){\r\n    var d = new Date();\r\n    var day = d.getDate();\r\n    var x = d.toDateString().substr(4, 3);\r\n    var year = d.getFullYear();\r\n    document.querySelector(\"#mydate\").innerHTML = day + '-' + x + '-' + year;\r\n}\r\nwindow.onload = init;
\r\n
<div id=\"mydate\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907d9", + "creator": "John Slegers", + "createdAt": 1453669752000, + "text": "

In modern browsers (*), you can just do this:

\n\n
var today = new Date().toLocaleDateString('en-GB', {\n    day : 'numeric',\n    month : 'short',\n    year : 'numeric'\n}).split(' ').join('-');\n
\n\n

Output if executed today (january 24ᵗʰ, 2016):

\n\n
'24-Jan-2016'\n
\n\n
\n\n

(*) According to MDN, \"modern browsers\" means Chrome 24+, Firefox 29+, Internet Explorer 11, Edge 12+, Opera 15+ & Safari nightly build.

\n", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907da", + "creator": "Henadzi Rabkin", + "createdAt": 1455055255000, + "text": "
var today = new Date();\nvar formattedToday = today.toLocaleDateString() + ' ' + today.toLocaleTimeString();\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907db", + "creator": "Ronnie Royston", + "createdAt": 1462314677000, + "text": "

2.39KB minified. One file. https://github.com/rhroyston/clock-js
\n
\n10-Aug-2010 would be:

\n\n
var str = clock.month\nstr.charAt(0).toUpperCase() + str.slice(1,3); //gets you \"Aug\"\nconsole.log(clock.day + '-' + str + '-' + clock.year); //gets you 10-Aug-2010\n
\n\n


\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907dc", + "creator": "Amit Kumar Gupta", + "createdAt": 1469897908000, + "text": "

This is how I implemented for my npm plugins

\n\n
var monthNames = [\n  \"January\", \"February\", \"March\",\n  \"April\", \"May\", \"June\", \"July\",\n  \"August\", \"September\", \"October\",\n  \"November\", \"December\"\n];\n\nvar Days = [\n  \"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\",\n  \"Thursday\", \"Friday\", \"Saturday\"\n];\n\nvar formatDate = function(dt,format){\n  format = format.replace('ss', pad(dt.getSeconds(),2));\n  format = format.replace('s', dt.getSeconds());\n  format = format.replace('dd', pad(dt.getDate(),2));\n  format = format.replace('d', dt.getDate());\n  format = format.replace('mm', pad(dt.getMinutes(),2));\n  format = format.replace('m', dt.getMinutes());\n  format = format.replace('MMMM', monthNames[dt.getMonth()]);\n  format = format.replace('MMM', monthNames[dt.getMonth()].substring(0,3));\n  format = format.replace('MM', pad(dt.getMonth()+1,2));\n  format = format.replace(/M(?![ao])/, dt.getMonth()+1);\n  format = format.replace('DD', Days[dt.getDay()]);\n  format = format.replace(/D(?!e)/, Days[dt.getDay()].substring(0,3));\n  format = format.replace('yyyy', dt.getFullYear());\n  format = format.replace('YYYY', dt.getFullYear());\n  format = format.replace('yy', (dt.getFullYear()+\"\").substring(2));\n  format = format.replace('YY', (dt.getFullYear()+\"\").substring(2));\n  format = format.replace('HH', pad(dt.getHours(),2));\n  format = format.replace('H', dt.getHours());\n  return format;\n}\n\npad = function(n, width, z) {\n  z = z || '0';\n  n = n + '';\n  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e5082fcc3049e91f04", + "creator": "lbrahim", + "createdAt": 1478074268000, + "text": "Which package are you referring to?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907dd", + "creator": "Dave", + "createdAt": 1471957068000, + "text": "

If you fancy a short, human-readable, function - this is easily adjustable to suit you.

\n\n

The timeStamp parameter is milliseconds from 1970 - it is returned by new Date().getTime() and many other devices...

\n\n

OK, I changed my mind. I included an extra function for zero padding. Curses!

\n\n
 function zeroPad(aNumber) {\n     return (\"0\"+aNumber).slice(-2);\n }\n function humanTime(timeStamp) {\n    var M = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n    var D = new Date(timeStamp); // 23 Aug 2016 16:45:59 <-- Desired format.\n    return D.getDate() + \" \" + M[D.getMonth()] + \" \" + D.getFullYear() + \" \" + D.getHours() + \":\" + zeroPad(d.getMinutes()) + \":\" + zeroPad(D.getSeconds());\n }\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907de", + "creator": "Dave", + "createdAt": 1471960775000, + "text": "

Inspired by JD Smith's marvellous regular expression solution, I suddenly had this head-splitting idea:

\n\n

\r\n
\r\n
var D = Date().toString().split(\" \");\r\nconsole.log(D[2] + \"-\" + D[1] + \"-\" + D[3]);
\r\n
\r\n
\r\n

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e5082fcc3049e91f08", + "creator": "JD Smith", + "createdAt": 1472766380000, + "text": "Nice variation if you need it right in the DOM like that!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907df", + "creator": "Vijay Maheriya", + "createdAt": 1472464096000, + "text": "

We have lots of solutions for this, but I think the best of them is Moment.js. So I personally suggest to use Moment.js for date and time operations.

\n\n

\r\n
\r\n
console.log(moment().format('DD-MMM-YYYY'));
\r\n
<script src=\"//cdnjs.cloudflare.com/ajax/libs/moment.js/2.14.1/moment.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e1", + "creator": "vdegenne", + "createdAt": 1503924580000, + "text": "

Using an ECMAScript Edition 6 (ES6/ES2015) string template:

\n\n
let d = new Date();\nlet formatted = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;\n
\n\n

If you need to change the delimiters:

\n\n
const delimiter = '/';\nlet formatted = [d.getFullYear(), d.getMonth() + 1, d.getDate()].join(delimiter);\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e0", + "creator": "Alireza", + "createdAt": 1500469133000, + "text": "

OK, we have got something called Intl which is very useful for formatting a date in JavaScript these days:

\n\n

Your date as below:

\n\n
var date = '10/8/2010';\n
\n\n

And you change to Date by using new Date() like below:

\n\n
date = new Date(date);\n
\n\n

And now you can format it any way you like using a list of locales like below:

\n\n
date = new Intl.DateTimeFormat('en-AU').format(date); // Australian date format: \"8/10/2010\" \n
\n\n


\n\n
date = new Intl.DateTimeFormat('en-US').format(date); // USA date format: \"10/8/2010\" \n
\n\n


\n\n
date = new Intl.DateTimeFormat('ar-EG').format(date);  // Arabic date format: \"٨‏/١٠‏/٢٠١٠\"\n
\n\n

If you exactly want the format you mentioned above, you can do:

\n\n
date = new Date(Date.UTC(2010, 7, 10, 0, 0, 0));\nvar options = {year: \"numeric\", month: \"short\", day: \"numeric\"};\ndate = new Intl.DateTimeFormat(\"en-AU\", options).format(date).replace(/\\s/g, '-');\n
\n\n

And the result is going to be:

\n\n
\"10-Aug-2010\"\n
\n\n

For more see the Intl API and Intl.DateTimeFormat documentation.

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f325e5082fcc3049e91f0a", + "creator": "Pants", + "createdAt": 1565440166000, + "text": "Not supported by IE", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c4082fcc3049e907e2", + "creator": "Agi Hammerthief", + "createdAt": 1509093819000, + "text": "

The following code will allow you to format the date to either DD-MM-YYYY (27-12-2017) or DD MMM YYYY (27 Dec 2017) :

\n
/** Pad number to fit into nearest power of 10 */\nfunction padNumber(number, prependChar, count) {\n  var out = '' + number; var i;\n  if (number < Math.pow(10, count))\n    while (out.length < ('' + Math.pow(10, count)).length) out = prependChar + out;\n  \n  return out;\n}\n\n/* Format the date to 'DD-MM-YYYY' or 'DD MMM YYYY' */\nfunction dateToDMY(date, useNumbersOnly) {\n  var months = [\n    'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', \n    'Nov', 'Dec'\n  ];\n\n  return '' + padNumber(date.getDate(), '0', 1) + \n   (useNumbersOnly? '-' + padNumber(date.getMonth() + 1, '0', 1) + '-' : ' ' + months[date.getMonth()] + ' ')\n    + date.getFullYear();\n}\n
\n

Change the order of date.getFullYear() and padNumber(date.getDate(), '0', 1) to make a dateToYMD() function.

\n

See repl.it example for details.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e3", + "creator": "Combine", + "createdAt": 1513448848000, + "text": "

I know someone might say that this is silly solution, but it does do the trick by removing the unnecessary information from the date string.

\n\n

yourDateObject produces:

\n\n

Wed Dec 13 2017 20:40:40 GMT+0200 (EET)

\n\n

yourDateObject.toString().slice(0, 15); produces:

\n\n

Wed Dec 13 2017

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e5", + "creator": "Kirk Strobeck", + "createdAt": 1515983606000, + "text": "

\r\n
\r\n
new Date().toLocaleDateString()\r\n\r\n// \"3/21/2018\"
\r\n
\r\n
\r\n

\n\n

More documentation at developer.mozilla.org

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e4", + "creator": "Mr Nsubuga", + "createdAt": 1515407253000, + "text": "

This may help with the problem:

\n

\r\n
\r\n
var d = new Date();\n\nvar options = {   \n    day: 'numeric',\n    month: 'long', \n    year: 'numeric'\n};\n\nconsole.log(d.toLocaleDateString('en-ZA', options));
\r\n
\r\n
\r\n

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e6", + "creator": "Basj", + "createdAt": 1516219732000, + "text": "

This is the main answer modified to have 3-char months, and 2-digit year:

\n\n

\r\n
\r\n
function formatDate(date) {\r\n    var monthNames = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\r\n    var day = date.getDate(), monthIndex = date.getMonth(), year = date.getFullYear().toString().substr(-2);\r\n    return day + ' ' + monthNames[monthIndex] + ' ' + year;\r\n}\r\n\r\ndocument.write(formatDate(new Date()));
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e7", + "creator": "Hinrich", + "createdAt": 1525705253000, + "text": "

For any one looking for a really simple ES6 solution to copy, paste and adopt:

\n\n

\r\n
\r\n
const dateToString = d => `${d.getFullYear()}-${('00' + (d.getMonth() + 1)).slice(-2)}-${('00' + d.getDate()).slice(-2)}` \r\n\r\n// how to use:\r\nconst myDate = new Date(Date.parse('04 Dec 1995 00:12:00 GMT'))\r\nconsole.log(dateToString(myDate)) // 1995-12-04
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e8", + "creator": "Matias Osmerini", + "createdAt": 1535659474000, + "text": "

Other way that you can format the date:

\n\n
function formatDate(dDate,sMode){\n    var today = dDate;\n    var dd = today.getDate();\n    var mm = today.getMonth()+1; //January is 0!\n    var yyyy = today.getFullYear();\n    if(dd<10) {\n        dd = '0'+dd\n    }\n    if(mm<10) {\n        mm = '0'+mm\n    }\n    if (sMode+\"\"==\"\"){\n        sMode = \"dd/mm/yyyy\";\n    }\n    if (sMode == \"yyyy-mm-dd\"){\n        return  yyyy + \"-\" + mm + \"-\" + dd + \"\";\n    }\n    if (sMode == \"dd/mm/yyyy\"){\n        return  dd + \"/\" + mm + \"/\" + yyyy;\n    }\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ea", + "creator": "onmyway133", + "createdAt": 1541505299000, + "text": "

You don't need any libraries. Just extract date components and construct the string. Here is how to get YYYY-MM-DD format. Also note the month index \"January is 0, February is 1, and so on.\"

\n\n
// @flow\n\ntype Components = {\n  day: number,\n  month: number,\n  year: number\n}\n\nexport default class DateFormatter {\n  // YYYY-MM-DD\n  static YYYY_MM_DD = (date: Date): string => {\n    const components = DateFormatter.format(DateFormatter.components(date))\n    return `${components.year}-${components.month}-${components.day}`\n  }\n\n  static format = (components: Components) => {\n    return {\n      day: `${components.day}`.padStart(2, '0'),\n      month: `${components.month}`.padStart(2, '0'),\n      year: components.year\n    }\n  }\n\n  static components = (date: Date) => {\n    return {\n      day: date.getDate(),\n      month: date.getMonth() + 1,\n      year: date.getFullYear()\n    }\n  }\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907e9", + "creator": "lewdev", + "createdAt": 1539403009000, + "text": "

yy = 2-digit year;\nyyyy = full year

\n\n

M = digit month;\nMM = 2-digit month;\nMMM = short month name;\nMMMM = full month name

\n\n

EEEE = full weekday name;\nEEE = short weekday name

\n\n

d = digit day;\ndd = 2-digit day

\n\n

h = hours;\nhh = 2-digit hours

\n\n

m = minutes;\nmm = 2-digit minutes

\n\n

s = seconds;\nss = 2-digit seconds

\n\n

S = miliseconds

\n\n

Used similar formating as Class SimpleDateFormat (Java)

\n\n

\r\n
\r\n
var monthNames = [\r\n  \"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\",\r\n  \"August\", \"September\", \"October\", \"November\", \"December\"\r\n];\r\nvar dayOfWeekNames = [\r\n  \"Sunday\", \"Monday\", \"Tuesday\",\r\n  \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"\r\n];\r\nfunction formatDate(date, formatStr){\r\n    if (!formatStr) {\r\n      formatStr = 'dd/mm/yyyy';\r\n    }\r\n    var day = date.getDate(),\r\n        month = date.getMonth(),\r\n        year = date.getFullYear(),\r\n        hour = date.getHours(),\r\n        minute = date.getMinutes(),\r\n        second = date.getSeconds(),\r\n        miliseconds = date.getMilliseconds(),\r\n        hh = twoDigitPad(hour),\r\n        mm = twoDigitPad(minute),\r\n        ss = twoDigitPad(second),\r\n        EEEE = dayOfWeekNames[date.getDay()],\r\n        EEE = EEEE.substr(0, 3),\r\n        dd = twoDigitPad(day),\r\n        M = month + 1,\r\n        MM = twoDigitPad(M),\r\n        MMMM = monthNames[month],\r\n        MMM = MMMM.substr(0, 3),\r\n        yyyy = year + \"\",\r\n        yy = yyyy.substr(2, 2)\r\n    ;\r\n    return formatStr\r\n      .replace('hh', hh).replace('h', hour)\r\n      .replace('mm', mm).replace('m', minute)\r\n      .replace('ss', ss).replace('s', second)\r\n      .replace('S', miliseconds)\r\n      .replace('dd', dd).replace('d', day)\r\n      .replace('MMMM', MMMM).replace('MMM', MMM).replace('MM', MM).replace('M', M)\r\n      .replace('EEEE', EEEE).replace('EEE', EEE)\r\n      .replace('yyyy', yyyy)\r\n      .replace('yy', yy)\r\n    ;\r\n}\r\nfunction twoDigitPad(num) {\r\n    return num < 10 ? \"0\" + num : num;\r\n}\r\nconsole.log(formatDate(new Date()));\r\nconsole.log(formatDate(new Date(), 'EEEE, MMMM d, yyyy hh:mm:ss:S'));\r\nconsole.log(formatDate(new Date(), 'EEE, MMM d, yyyy hh:mm'));\r\nconsole.log(formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss:S'));\r\nconsole.log(formatDate(new Date(), 'yy-MM-dd hh:mm'));
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ec", + "creator": "site", + "createdAt": 1549395575000, + "text": "

To obtain \"10-Aug-2010\", try:

\n\n
var date = new Date('2010-08-10 00:00:00');\ndate = date.toLocaleDateString(undefined, {day:'2-digit'}) + '-' + date.toLocaleDateString(undefined, {month:'short'}) + '-' + date.toLocaleDateString(undefined, {year:'numeric'})\n
\n\n

For browser support, see toLocaleDateString.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907eb", + "creator": "blairzotron", + "createdAt": 1548344622000, + "text": "

A simple function that can return the date, the date + time, or just the time:

\n\n
var myDate = dateFormatter(\"2019-01-24 11:33:24\", \"date-time\");\n// >> RETURNS \"January 24, 2019 11:33:24\"\n\nvar myDate2 = dateFormatter(\"2019-01-24 11:33:24\", \"date\");\n// >> RETURNS \"January 24, 2019\"\n\nvar myDate3 = dateFormatter(\"2019-01-24 11:33:24\", \"time\");\n// >> RETURNS \"11:33:24\"\n\n\nfunction dateFormatter(strDate, format){\n    var theDate = new Date(strDate);\n    if (format==\"time\")\n       return getTimeFromDate(theDate);\n    else{\n       var dateOptions = {year:'numeric', month:'long', day:'numeric'};\n       var formattedDate = theDate.toLocaleDateString(\"en-US\", + dateOptions);\n       if (format==\"date\")\n           return formattedDate;\n       return formattedDate + \" \" + getTimeFromDate(theDate);\n    }\n}\n\nfunction getTimeFromDate(theDate){\n    var sec = theDate.getSeconds();\n    if (sec<10)\n        sec = \"0\" + sec;\n    var min = theDate.getMinutes();\n    if (min<10)\n        min = \"0\" + min;\n    return theDate.getHours() + ':'+ min + ':' + sec;\n}\n\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ee", + "creator": "Melchia", + "createdAt": 1572615824000, + "text": "

You should have a look at DayJs It's a remake of momentJs but modular architecture oriented so lighter.

\n\n

Fast 2kB alternative to Moment.js with the same modern API

\n\n
\n

Day.js is a minimalist JavaScript library that parses, validates, manipulates, and displays dates and times for modern browsers with a largely Moment.js-compatible API. If you use Moment.js, you already know how to use Day.js.

\n
\n\n

\r\n
\r\n
var date = Date.now();\r\nconst formatedDate = dayjs(date).format(\"YYYY-MM-DD\")\r\nconsole.log(formatedDate);
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.8.16/dayjs.min.js\" crossorigin=\"anonymous\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ed", + "creator": "Guilherme Ferreira", + "createdAt": 1555741788000, + "text": "

This module can easily handle mostly every case there is.\nIt is part of a bigger npm package, by Locutus, which includes a variety of functions, but it can be used totally independent of the package itself, just copy paste/ adapt a little if not working with npm (change from a module to just a function).

\n

As a second parameter it accepts a timestamp, which can come from anywhere, such as Date.getTime().

\n

Also, Locutus maintains a bigger datetime module, also inside the locutus package which will give a more object-oriented way to use it.

\n

Here you can see other datetime functions, as modules, that proved to be very useful too.

\n

You can find documentation on parameters and format strings here (note that the documentation site is a PHP site, but the locutus implementation follows exactly the same specifications).

\n

Examples of the date Module

\n
date('H:m:s \\\\m \\\\i\\\\s \\\\m\\\\o\\\\n\\\\t\\\\h', 1062402400)//'07:09:40 m is month'\n\ndate('F j, Y, g:i a', 1062462400)//'September 2, 2003, 12:26 am'\n\ndate('Y W o', 1062462400)//'2003 36 2003'\n\nvar $x = date('Y m d', (new Date()).getTime() / 1000) $x = $x + '' var $result = $x.length // 2009 01 09    10\n\ndate('W', 1104534000)    //'52'\n\ndate('B t', 1104534000)    //'999 31'\n\ndate('W U', 1293750000.82); // 2010-12-31    '52 1293750000'\n\ndate('W', 1293836400); // 2011-01-01    '52'\n\ndate('W Y-m-d', 1293974054); // 2011-01-02    '52 2011-01-02'\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907ef", + "creator": "K Vij", + "createdAt": 1573962729000, + "text": "

As of 2019, it looks like you can get toLocaleDateString to return only certain parts and then you can join them as you wish:

\n\n
var date = new Date();\n\nconsole.log(date.toLocaleDateString(\"en-US\", { day: 'numeric' }) \n            + \"-\"+ date.toLocaleDateString(\"en-US\", { month: 'short' })\n            + \"-\" + date.toLocaleDateString(\"en-US\", { year: 'numeric' }) );\n\n> 16-Nov-2019\n\nconsole.log(date.toLocaleDateString(\"en-US\", { month: 'long' }) \n            + \" \" + date.toLocaleDateString(\"en-US\", { day: 'numeric' }) \n            + \", \" + date.toLocaleDateString(\"en-US\", { year: 'numeric' }) );\n\n> November 16, 2019\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f0", + "creator": "Chris Kobrzak", + "createdAt": 1574439244000, + "text": "

In order to format a date as e.g. 10-Aug-2010, you might want to use .toDateString() and ES6 array destructuring.

\n\n
const formattedDate = new Date().toDateString()\n// The above yields e.g. 'Mon Jan 06 2020'\n\nconst [, month, day, year] = formattedDate.split(' ')\n\nconst ddMmmYyyy = `${day}-${month}-${year}`\n// or\nconst ddMmmYyyy = [day, month, year].join('-')\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f2", + "creator": "Tom", + "createdAt": 1585294497000, + "text": "

It works same in Internet Explorer 11, Firefox, and Chrome (Chrome 80.x shows 12 hours format when en-UK selected).

\n

\r\n
\r\n
const d = new Date('2010/08/05 23:45') // 26.3.2020\nconst dtfUK = new Intl.DateTimeFormat('UK', { year: 'numeric', month: '2-digit', day: '2-digit',\n        hour: '2-digit',minute: '2-digit', second: '2-digit' }); //\nconst dtfUS = new Intl.DateTimeFormat('en', { year: 'numeric', month: '2-digit', day: '2-digit',\n        hour: '2-digit',minute: '2-digit', second: '2-digit' }); //\nconsole.log(dtfUS.format(d)); // 08/05/2010 11:45:00 PM\nconsole.log(dtfUK.format(d)); // 05.08.2010 23:45:00\n/* node.js:\n08/05/2010, 11:45:00 PM\n2010-08-05 23:45:00\n*/
\r\n
\r\n
\r\n

\n

What about something more general?

\n

\r\n
\r\n
var d = new Date('2010-08-10T10:34:56.789Z');\nvar str = d.toDateString() + // Tue Aug 10 2010\n    ' ' + d.toTimeString().split(' ')[0] + // 12:34:56, GMT+0x00 (GMT+0x:00)\n    ' ' + (d.getMonth() + 101) + // 108\n    ' ' + d.getMilliseconds(); // 789\nconsole.log(str); // Tue Aug 10 2010 12:34:56 108 789\nconsole.log(//   $1 Tue  $2 Aug  $3 11     $4 2020 $5 12   $6 34   $7 56    $8 108  $9 789\n    str.replace(/(\\S{3}) (\\S{3}) (\\d{1,2}) (\\d{4}) (\\d{2}):(\\d{2}):(\\d{2}) 1(\\d{2}) (\\d{1,3})/, '$3-$2-$4 $5:$6.$9 ($1)')\n); // 10-Aug-2010 12:34.789 (Tue)\n/*\n$1: Tue  Week Day string\n$2: Aug  Month short text\n$3: 11   Day\n$4: 2010 Year\n$5: 12   Hour\n$6: 34   Minute\n$7: 56   Seconds\n$8: 08   Month\n$9: 789  Milliseconds\n*/
\r\n
\r\n
\r\n

\n

Or for example 1-line IIFE "library" ;-)

\n

\r\n
\r\n
console.log(\n    (function (frm, d) { return [d.toDateString(), d.toTimeString().split(' ')[0], (d.getMonth() + 101), d.getMilliseconds()].join(' ').replace(/(\\S{3}) (\\S{3}) (\\d{1,2}) (\\d{4}) (\\d{2}):(\\d{2}):(\\d{2}) 1(\\d{2}) (\\d{1,3})/, frm); })\n    ('$4/$8/$3 $5:$6 ($1)', new Date())\n);
\r\n
\r\n
\r\n

\n

You can remove useless parts and / or change indexes if you do not need them.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f1", + "creator": "Kamil Kiełczewski", + "createdAt": 1583238576000, + "text": "

Two pure JavaScript one-liners

\n

In this answer I develop JD Smith's idea. I was able to shorten the JD Smith regexp.

\n

\r\n
\r\n
let format= d=> d.toString().replace(/\\w+ (\\w+) (\\d+) (\\d+).*/,'$2-$1-$3');\n\nconsole.log( format(Date()) );
\r\n
\r\n
\r\n

\n

Dave's is also based on JD Smith's idea, but he avoids regexps and give a very nice solution - I short his solution a little (by changing the split parameter) and opaque it in a wrapper.

\n

\r\n
\r\n
let format= (d,a=d.toString().split` `)=> a[2]+\"-\"+a[1]+\"-\"+a[3];\n\nconsole.log( format(Date()) );
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f4", + "creator": "antelove", + "createdAt": 1602598303000, + "text": "

\r\n
\r\n
function convert_month(i = 0, option = \"num\") { // i = index\n\n  var object_months = [\n    { num: 01, short: \"Jan\", long: \"January\" },\n    { num: 02, short: \"Feb\", long: \"Februari\" }, \n    { num: 03, short: \"Mar\", long: \"March\" },          \n    { num: 04, short: \"Apr\", long: \"April\" },\n    { num: 05, short: \"May\", long: \"May\" },\n    { num: 06, short: \"Jun\", long: \"Juni\" },\n    { num: 07, short: \"Jul\", long: \"July\" },\n    { num: 08, short: \"Aug\", long: \"August\" },\n    { num: 09, short: \"Sep\", long: \"September\" },\n    { num: 10, short: \"Oct\", long: \"October\" },\n    { num: 11, short: \"Nov\", long: \"November\" },\n    { num: 12, short: \"Dec\", long: \"December\" }\n  ];\n        \n  return object_months[i][option];\n\n}\n      \nvar d = new Date();\n      \n// https://stackoverflow.com/questions/1408289/how-can-i-do-string-interpolation-in-javascript\nvar num   = `${d.getDate()}-${convert_month(d.getMonth())}-${d.getFullYear()}`;\nvar short = `${d.getDate()}-${convert_month(d.getMonth(), \"short\")}-${d.getFullYear()}`;\nvar long  = `${d.getDate()}-${convert_month(d.getMonth(), \"long\")}-${d.getFullYear()}`;\n\ndocument.querySelector(\"#num\").innerHTML = num;\ndocument.querySelector(\"#short\").innerHTML = short;\ndocument.querySelector(\"#long\").innerHTML = long;
\r\n
<p>Numeric  : <span id=\"num\"></span> (default)</p>\n<p>Short    : <span id=\"short\"></span></p>\n<p>Long     : <span id=\"long\"></span></p>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f3", + "creator": "Prabha", + "createdAt": 1593370419000, + "text": "

Maybe this helps some one who are looking for multiple date formats one after the other by willingly or unexpectedly. Please find the code: I am using the Moment.js format function on a current date as (today is 29-06-2020): var startDate = moment(new Date()).format('MM/DD/YY'); Result: 06/28/20

\n

It retains only the year part: 20 as "06/28/20", after if I run the statement new Date(startDate), the result is "Mon Jun 28 1920 00:00:00 GMT+0530 (India Standard Time)".

\n

Then, when I use another format on "06/28/20": startDate = moment(startDate ).format('MM-DD-YYYY'); Result: 06-28-1920, in Google Chrome and Firefox browsers, it gives the correct date on the second attempt as: 06-28-2020.

\n

But in Internet Explorer it is having issues. From this I understood we can apply one dateformat on the given date. If we want a second date format, it should be apply on the fresh date, not on the first date format result. And also observe that for the first time applying 'MM-DD-YYYY' and next 'MM-DD-YY' is working in Internet Explorer. For a clear understanding, please find my question in the link: Date went wrong when using Moment.js date format in Internet Explorer 11.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f6", + "creator": "perepm", + "createdAt": 1620066493000, + "text": "

The Date constructor (and Date.parse()) only accepts one format as a parameter when constructing a date and that is ISO 8601:

\n
// new Date('YYYY-MM-DDTHH:mm:ss.sssZ')\nconst date = new Date('2017-08-15')\n
\n

But parsing a from a string is strongly discouraged (MDN recommends against creating date with date strings) due to browser differences and inconsistencies.

\n

The recommended alternative would be building your Date instance directly from the numeric data like this:

\n
new Date(2017, 7, 15) // Month is zero-indexed\n
\n

That is parsing. Now, to format your date to the string you desire you have several options that are native of the Date object (although I believe none is compliant to the format you require):

\n
date.toString()       // 'Wed Jan 23 2019 17:23:42 GMT+0800 (Singapore Standard Time)'\ndate.toDateString()   // 'Wed Jan 23 2019'\ndate.toLocaleString() // '23/01/2019, 17:23:42'\ndate.toGMTString()    // 'Wed, 23 Jan 2019 09:23:42 GMT'\ndate.toUTCString()    // 'Wed, 23 Jan 2019 09:23:42 GMT'\ndate.toISOString()    // '2019-01-23T09:23:42.079Z'\n
\n

For other formatting options I'm afraid you'll have to turn to libraries such as Moment.js, day.js and the like.

\n

Credit to Zell Liew from this article for the date formatting tips.

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f5", + "creator": "saran3h", + "createdAt": 1613737840000, + "text": "

This can help:

\n
export const formatDateToString = date => {\n    if (!date) {\n        return date;\n    }\n    try {\n        return format(parse(date, 'yyyy-MM-dd', new Date()), 'dd/MM/yyyy');\n    } catch (error) {\n        return 'invalid date';\n    }\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f7", + "creator": "moshfiqrony", + "createdAt": 1623327971000, + "text": "

Use this procedure

\n

\r\n
\r\n
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec']\n\nconst date = new Date()\n\nconst dateString = `${date.getDate()}-${MONTHS[date.getMonth()]}-${date.getFullYear()}`\n\nconsole.log(dateString)
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f8", + "creator": "Weilory", + "createdAt": 1647005252000, + "text": "

string conversion

\n
\n// date \nconst dateConvert = {\n  dasher: dt => {\n    let m = (dt.getMonth() + 1) === 13 ? 1 : (dt.getMonth() + 1);\n    m = m < 10 ? `0${m}` : m.toString();\n    let d = dt.getDate();\n    d = d < 10 ? `0${d}` : d.toString();\n    return `${dt.getFullYear()}-${m}-${d}`;\n  }, \n  slasher: dt => {\n    return dateConvert.slash(dateConvert.dasher(dt));\n  }, \n  dash: str => {\n    // 03/11/2022 -> 2022-03-11\n    let [d, m, y] = str.split('/');\n    return `${y}-${m}-${d}`;\n  }, \n  slash: str => {\n    // 2022-03-11 -> 03/11/2022\n    let [y, m, d] = str.split('-');\n    return `${d}/${m}/${y}`\n  }\n}\n\n// console.log(dateConvert.dasher(new Date('01/31/2001')));\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907f9", + "creator": "Mohsen Alyafei", + "createdAt": 1647006518000, + "text": "

The Javascript Intl.DateTimeFormat method provides a convenient way to format dates.

\n

Here is how the needed format can be constructed:

\n

\r\n
\r\n
const date = new Date(\"2010-08-10\");\n\nlet d=new Intl.DateTimeFormat('en-GB',{year:\"numeric\", month:\"short\",day:\"2-digit\"}).format(date).split(\" \").join(\"-\");\n\nconsole.log(d);
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907fa", + "creator": "saddam", + "createdAt": 1648308972000, + "text": "

simply you can do this :-

\n
\n let date = new Date().toLocaleDateString('en-us',{day: 'numeric'})\n let month = new Date().toLocaleDateString('en-us',{month: 'long'})\n let year = new Date().toLocaleDateString('en-us',{year: 'numeric'})\n const FormattedDate = `${date}-${month}-${year}`\n console.log(FormattedDate) // 26-March-2022\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c4082fcc3049e907fb", + "creator": "KARTHIKEYAN.A", + "createdAt": 1656661245000, + "text": "

In my case I have formatted date form '01/07/2022' to '2022-07-01'

\n

\r\n
\r\n
const formatDate = date => {\nconst d = new Date(date)\nlet month = (d.getMonth() + 1).toString()\nlet day = d.getDate().toString()\nconst year = d.getFullYear()\nif (month.length < 2) {\n    month = '0' + month\n}\nif (day.length < 2) {\n    day = '0' + day\n}\nreturn [ year, month, day ].join('-')\n}\n\nconsole.log(formatDate('01/07/2022'))
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 3, + "commentItems": [ + { + "_id": "62f321c3082fcc3049e9079d", + "creator": "Christophe Roussy", + "createdAt": 1447937129000, + "text": "As usual: beware THE MONTH is ZERO-INDEXED ! So January is zero not one...", + "upvotes": 456, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9079e", + "creator": "onmyway133", + "createdAt": 1541425800000, + "text": "You can use toLocaleDateString ", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c3082fcc3049e9079f", + "creator": "Sebastian Simon", + "createdAt": 1632736854000, + "text": "If you’re looking how to parse a string to a Date object, see Parsing a string to a date in JavaScript.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 5709257, + "uvac": 5709311 + } + }, + { + "_id": "62f321bb082fcc3049e8fef7", + "title": "Check if a variable is a string in JavaScript", + "title-lowercase": "check if a variable is a string in javascript", + "creator": "Olical", + "createdAt": 1288449394000, + "status": "open", + "text": "

How can I determine whether a variable is a string or something else in JavaScript?

\n", + "upvotes": 2763, + "upvoterUsernames": [], + "downvotes": 294, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2252011, + "answers": 26, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90d7f", + "creator": "Pablo Santa Cruz", + "createdAt": 1288449641000, + "text": "

You can use typeof operator:

\n\n
var booleanValue = true; \nvar numericalValue = 354;\nvar stringValue = \"This is a String\";\nvar stringObject = new String( \"This is a String Object\" );\nalert(typeof booleanValue) // displays \"boolean\"\nalert(typeof numericalValue) // displays \"number\"\nalert(typeof stringValue) // displays \"string\"\nalert(typeof stringObject) // displays \"object\"\n
\n\n

Example from this webpage. (Example was slightly modified though).

\n\n

This won't work as expected in the case of strings created with new String(), but this is seldom used and recommended against[1][2]. See the other answers for how to handle these, if you so desire.

\n\n
\n\n
    \n
  1. The Google JavaScript Style Guide says to never use primitive object wrappers.
  2. \n
  3. Douglas Crockford recommended that primitive object wrappers be deprecated.
  4. \n
\n", + "upvotes": 2859, + "upvoterUsernames": [], + "downvotes": 635, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32889082fcc3049e92821", + "creator": "Vsevolod Golovanov", + "createdAt": 1512581439000, + "text": "@DanielLe, because he proposed a replacement that fixes some issues, not because he's against it in principle.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92823", + "creator": "Marek Marczak", + "createdAt": 1522659255000, + "text": "let value = NaN; typeof value => 'number' JS sucks completely.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92824", + "creator": "Thomas", + "createdAt": 1526137340000, + "text": "@MarkAmery what's the use case for declaring a string that way? Seems like bad practice.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d84", + "creator": "ClearCloud8", + "createdAt": 1389041030000, + "text": "

I recommend using the built-in functions from jQuery or lodash/Underscore. They're simpler to use and easier to read.

\n\n

Either function will handle the case DRAX mentioned... that is, they both check if (A) the variable is a string literal or (B) it's an instance of the String object. In either case, these functions correctly identify the value as being a string.

\n\n

lodash / Underscore.js

\n\n
if(_.isString(myVar))\n   //it's a string\nelse\n   //it's something else\n
\n\n

jQuery

\n\n
if($.type(myVar) === \"string\")\n   //it's a string\nelse\n   //it's something else\n
\n\n

See lodash Documentation for _.isString() for more details.

\n\n

See jQuery Documentation for $.type() for more details.

\n", + "upvotes": 117, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e92827", + "creator": "Emaborsa", + "createdAt": 1507792228000, + "text": "You should never use 3rd party libraries if not really needed!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92829", + "creator": "Matt Fletcher", + "createdAt": 1516614671000, + "text": "Bear in mind that many people will be doing this in a Node or Node-like environment, and very few people will be using jQuery there.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d80", + "creator": "DRAX", + "createdAt": 1330112304000, + "text": "

This is what works for me:

\n\n
if (typeof myVar === 'string' || myVar instanceof String)\n// it's a string\nelse\n// it's something else\n
\n", + "upvotes": 2928, + "upvoterUsernames": [], + "downvotes": 389, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e9282c", + "creator": "svth", + "createdAt": 1335362396000, + "text": "Does "myVar instanceof String" do anything above and beyond "typeof myVar == 'string'" ?", + "upvotes": 206, + "upvoterUsernames": [], + "downvotes": 76, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9282e", + "creator": "Danubian Sailor", + "createdAt": 1373359953000, + "text": "var somevar = new String('somestring') console.log(typeof somevar) // object", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9282f", + "creator": "DRAX", + "createdAt": 1441265504000, + "text": "To sum up, I wouldn't call primitive wrappers "worthless feature", and code provided is not unusual at all. They have their use cases.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92831", + "creator": "Dewi Morgan", + "createdAt": 1441340127000, + "text": "That said, I may like @Ling's answer even more, as it handles MarkAmery's issue, too...", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92833", + "creator": "Automatico", + "createdAt": 1516184961000, + "text": "let a:any = 'some string'; console.log(a instanceof String) results in false on my machine.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92834", + "creator": "darcher", + "createdAt": 1538937487000, + "text": "...also if !!('some string', String) which return true.", + "upvotes": 501, + "upvoterUsernames": [], + "downvotes": 501, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92836", + "creator": "darcher", + "createdAt": 1540455267000, + "text": "@Matthew you're very right, was probably tipsy when I posted this and later found the error in my ways.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92837", + "creator": "DRAX", + "createdAt": 1573037658000, + "text": "@Ry- You are correct. Here is updated code: jsbench.me/tuk2n5xsuz", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92838", + "creator": "SmileyJames", + "createdAt": 1588263181000, + "text": ""somestring" instanceof String resolves to false", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9283a", + "creator": "DRAX", + "createdAt": 1589801610000, + "text": "@SmileyJames that is correct, and new String("somestring") instanceof String resolves to true", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d83", + "creator": "Chris Dolphin", + "createdAt": 1380305340000, + "text": "

I also found that this works fine too, and its a lot shorter than the other examples.

\n\n
if (myVar === myVar + '') {\n   //its string\n} else {\n   //its something else\n}\n
\n\n

By concatenating on empty quotes it turns the value into a string. If myVar is already a string then the if statement is successful.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e9283b", + "creator": "user5672998", + "createdAt": 1510008364000, + "text": "this does not work with the String type; var s = new String('abc'); > s === s + '' > false", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9283d", + "creator": "Anthony Rutledge", + "createdAt": 1526161504000, + "text": "Good thought, but leaves out the edge case of object wrapped strings.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9283e", + "creator": "ToolmakerSteve", + "createdAt": 1625955802000, + "text": "I find this approach distasteful. Writing good code isn't about making it shorter. Its about saying what you mean.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d81", + "creator": "Cody", + "createdAt": 1366895753000, + "text": "

Best way:

\n
var s = 'String';\nvar a = [1,2,3];\nvar o = {key: 'val'};\n\n(s.constructor === String) && console.log('its a string');\n(a.constructor === Array) && console.log('its an array');\n(o.constructor === Object) && console.log('its an object');\n(o.constructor === Number || s.constructor === Boolean) && console.log('this won\\'t run');\n
\n

Each of these has been constructed by its appropriate class function, like "new Object()" etc.

\n

Also, Duck-Typing:\n"If it looks like a duck, walks like a duck, and smells like a duck - it must be an Array"\nMeaning, check its properties.

\n

Hope this helps.

\n

Edit; 12/05/2016

\n

Remember, you can always use combinations of approaches too. Here's an example of using an inline map of actions with typeof:

\n
var type = { 'number': Math.sqrt.bind(Math), ... }[ typeof datum ];\n
\n

Here's a more 'real world' example of using inline-maps:

\n
function is(datum) {\n    var isnt = !{ null: true, undefined: true, '': true, false: false, 0: false }[ datum ];\n    return !isnt;\n}\nconsole.log( is(0), is(false), is(undefined), ... );  // >> true true false\n
\n

This function would use [ custom ] "type-casting" -- rather, "type-/-value-mapping" -- to figure out if a variable actually "exists". Now you can split that nasty hair between null & 0!

\n

Many times you don't even care about its type. Another way to circumvent typing is combining Duck-Type sets:

\n
this.id = "998";  // use a number or a string-equivalent\nfunction get(id) {\n    if (!id || !id.toString) return;\n    if (id.toString() === this.id.toString()) http( id || +this.id );\n    // if (+id === +this.id) ...;\n}\n
\n

Both Number.prototype and String.prototype have a .toString() method. You just made sure that the string-equivalent of the number was the same, and then you made sure that you passed it into the http function as a Number. In other words, we didn't even care what its type was.

\n

Hope that gives you more to work with :)

\n", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e92841", + "creator": "user663031", + "createdAt": 1371446326000, + "text": "You would need some other check for plain old numbers, since trying to take their constructor property will fail:", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92842", + "creator": "Mark Amery", + "createdAt": 1402088690000, + "text": "@torazaburo Worked fine for me just now in the Chrome console. What makes you think it won't work?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d82", + "creator": "Orwellophile", + "createdAt": 1374407940000, + "text": "

Since 580+ people have voted for an incorrect answer, and 800+ have voted for a working but shotgun-style answer, I thought it might be worth redoing my answer in a simpler form that everybody can understand.

\n
function isString(x) {\n  return Object.prototype.toString.call(x) === "[object String]"\n}\n
\n

Or, inline (I have an UltiSnip setup for this):

\n
Object.prototype.toString.call(myVar) === "[object String]"\n
\n

FYI, Pablo Santa Cruz's answer is wrong, because typeof new String("string") is object

\n

DRAX's answer is accurate and functional and should be the correct answer (since Pablo Santa Cruz is most definitely incorrect, and I won't argue against the popular vote.)

\n

However, this answer is also definitely correct, and actually the best answer (except, perhaps, for the suggestion of using lodash/underscore). disclaimer: I contributed to the lodash 4 codebase.

\n

My original answer (which obviously flew right over a lot of heads) follows:

\n

I transcoded this from underscore.js:

\n
['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'].forEach( \n    function(name) { \n        window['is' + name] = function(obj) {\n              return toString.call(obj) == '[object ' + name + ']';\n    }; \n});\n
\n

That will define isString, isNumber, etc.

\n
\n

In Node.js, this can be implemented as a module:

\n
module.exports = [\n  'Arguments',\n  'Function', \n  'String', \n  'Number', \n  'Date', \n  'RegExp'\n].reduce( (obj, name) => {\n  obj[ 'is' + name ] = x => toString.call(x) == '[object ' + name + ']';\n  return obj;\n}, {});\n
\n

[edit]: Object.prototype.toString.call(x) works to delineate between functions and async functions as well:

\n

\r\n
\r\n
const fn1 = () => new Promise((resolve, reject) => setTimeout(() => resolve({}), 1000))\nconst fn2 = async () => ({})\n\nconsole.log('fn1', Object.prototype.toString.call(fn1))\nconsole.log('fn2', Object.prototype.toString.call(fn2))
\r\n
\r\n
\r\n

\n", + "upvotes": 314, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e92843", + "creator": "Utopik", + "createdAt": 1381504429000, + "text": "DRAX's answer may be wrong only in prototypal inheritance context, depending on what you're trying to achieve. In others cases, it is correct", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92845", + "creator": "Volkan", + "createdAt": 1435393448000, + "text": "Cool solution for people with systemwise architecture concerns.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92847", + "creator": "Pacerier", + "createdAt": 1497988394000, + "text": "@Orwellophile, How is this better than DRAX's answer?", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92848", + "creator": "HoldOffHunger", + "createdAt": 1533238047000, + "text": "This answer looks complete, but I am curious about performance of this versus typeof.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d85", + "creator": "ling", + "createdAt": 1406228864000, + "text": "
function isString (obj) {\n  return (Object.prototype.toString.call(obj) === '[object String]');\n}\n
\n\n

I saw that here:

\n\n

http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32889082fcc3049e9284a", + "creator": "ewh", + "createdAt": 1432795982000, + "text": "I think this solution is the most robust since it handles cross-frame/cross-window reference scenarios as mentioned in the URL provided in the answer.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9284c", + "creator": "Daan", + "createdAt": 1467379537000, + "text": "Great answer, it looks like Underscore.js also uses this method!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e9284e", + "creator": "StubbornShowaGuy", + "createdAt": 1481002660000, + "text": "@ling Just curious, why do you put parenthesis around Object.prototype.toString.call(obj) === '[object String]'?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92850", + "creator": "StubbornShowaGuy", + "createdAt": 1507616151000, + "text": "@Earlee You mean (x === y) has better readability than x === y?", + "upvotes": 96, + "upvoterUsernames": [], + "downvotes": 96, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92851", + "creator": "Aquarelle", + "createdAt": 1516767051000, + "text": "@StubbornShowaGuy In my opinion, yes. It's also about consistency. I personally always use parentheses when returning a value.", + "upvotes": 1373, + "upvoterUsernames": [], + "downvotes": 1373, + "downvoterUsernames": [] + }, + { + "_id": "62f32889082fcc3049e92853", + "creator": "Jonathan H", + "createdAt": 1525722353000, + "text": "How is that different from @Orwellophile's answer?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d86", + "creator": "ahmd0", + "createdAt": 1409779110000, + "text": "

Just to expand on @DRAX's answer, I'd do this:

\n\n
function isWhitespaceEmptyString(str)\n{\n    //RETURN:\n    //      = 'true' if 'str' is empty string, null, undefined, or consists of white-spaces only\n    return str ? !(/\\S/.test(str)) : (str === \"\" || str === null || str === undefined);\n}\n
\n\n

It will account also for nulls and undefined types, and it will take care of non-string types, such as 0.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d87", + "creator": "Noris", + "createdAt": 1424876408000, + "text": "

A simple solution would be:

\n\n
var x = \"hello\"\n\nif(x === x.toString()){\n// it's a string \n}else{\n// it isn't\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288a082fcc3049e92856", + "creator": "Muhammad Umer", + "createdAt": 1432685603000, + "text": "this doesn't checks if it's a string. It makes into a string, lots of things have toString() function", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e92857", + "creator": "sarimarton", + "createdAt": 1548414123000, + "text": "The idea is still usable. x === String(x) is safe and works.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d88", + "creator": "Benj Sicam", + "createdAt": 1441515983000, + "text": "

Taken from lodash:

\n\n
function isString(val) {\n   return typeof val === 'string' || ((!!val && typeof val === 'object') && Object.prototype.toString.call(val) === '[object String]');\n}\n\nconsole.log(isString('hello world!')); // true\nconsole.log(isString(new String('hello world'))); // true\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d89", + "creator": "Yin", + "createdAt": 1450760384000, + "text": "
var a = new String('')\nvar b = ''\nvar c = []\n\nfunction isString(x) {\n  return x !== null && x !== undefined && x.constructor === String\n}\n\nconsole.log(isString(a))\nconsole.log(isString(b))\nconsole.log(isString(c))\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288a082fcc3049e9285b", + "creator": "Jules Manson", + "createdAt": 1517278818000, + "text": "Why do you need to check for null or undefined if x.constructor === String would also return false for null or undefined?", + "upvotes": 114, + "upvoterUsernames": [], + "downvotes": 114, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e9285d", + "creator": "Ry-", + "createdAt": 1537596793000, + "text": "@JulesManson: It would throw an error, not produce false.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d8b", + "creator": "Aryeh Beitz", + "createdAt": 1494825740000, + "text": "

I'm not sure if you mean knowing if it's a type string regardless of its contents, or whether it's contents is a number or string, regardless of its type.

\nSo to know if its type is a string, that's already been answered.\n
\nBut to know based on its contents if its a string or a number, I would use this:

\n\n
function isNumber(item) {\n    return (parseInt(item) + '') === item;\n}\n
\n\n

And for some examples:

\n\n
isNumber(123);   //true\nisNumber('123'); //true\nisNumber('123a');//false\nisNumber('');    //false\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d8a", + "creator": "David", + "createdAt": 1479001241000, + "text": "

Edit: The current way to do it is typeof value === 'string'. For example:

\n
const str = 'hello';\nif (typeof str === 'string') { ... }\n
\n

Below has been deprecated since node v4.

\n

If you work on the node.js environment, you can simply use the built-in function isString in utils.

\n
const util = require('util');\nif (util.isString(myVar)) {}\n
\n", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288a082fcc3049e92860", + "creator": "Anthony Kong", + "createdAt": 1510268972000, + "text": "Is there any replacement?", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e92862", + "creator": "Mr Rogers", + "createdAt": 1540939800000, + "text": "Documents say "Use typeof value === 'string' instead."", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d8d", + "creator": "Rob Brander", + "createdAt": 1527522446000, + "text": "

This is a great example of why performance matters:

\n\n

Doing something as simple as a test for a string can be expensive if not done correctly.

\n\n

For example, if I wanted to write a function to test if something is a string, I could do it in one of two ways:

\n\n

1) const isString = str => (Object.prototype.toString.call(str) === '[object String]');

\n\n

2) const isString = str => ((typeof str === 'string') || (str instanceof String));

\n\n

Both of these are pretty straight forward, so what could possibly impact performance? Generally speaking, function calls can be expensive, especially if you don't know what's happening inside. In the first example, there is a function call to Object's toString method. In the second example, there are no function calls, as typeof and instanceof are operators. Operators are significantly faster than function calls.

\n\n

When the performance is tested, example 1 is 79% slower than example 2!

\n\n

See the tests: https://jsperf.com/isstringtype

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d8c", + "creator": "ScottyG", + "createdAt": 1510003261000, + "text": "

I like to use this simple solution:

\n\n
var myString = \"test\";\nif(myString.constructor === String)\n{\n     //It's a string\n}\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288a082fcc3049e92865", + "creator": "Jonathan H", + "createdAt": 1525721765000, + "text": "How is that different from Cody's answer, 4 years later?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e92866", + "creator": "ScottyG", + "createdAt": 1525817008000, + "text": "@Sheljohn Cody's answer is great. My answer (complete text) is shorter and straight to the point. You asked... :)", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e92868", + "creator": "ToolmakerSteve", + "createdAt": 1625955179000, + "text": "@MikeBeaton - do empty strings return a different answer for .constructor? That would be quite surprising.", + "upvotes": 1615, + "upvoterUsernames": [], + "downvotes": 1615, + "downvoterUsernames": [] + }, + { + "_id": "62f3288a082fcc3049e92869", + "creator": "MikeBeaton", + "createdAt": 1625989736000, + "text": "So we need something like myString === '' || (!!myString && myString.constructor === String)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d8f", + "creator": "Tomozma", + "createdAt": 1540354473000, + "text": "

This is good enough for me.

\n\n

WARNING: This is not a perfect solution.\nSee the bottom of my post.

\n\n
Object.prototype.isString = function() { return false; };\nString.prototype.isString = function() { return true; };\n\nvar isString = function(a) {\n  return (a !== null) && (a !== undefined) && a.isString();\n};\n
\n\n

And you can use this like below.

\n\n
//return false\nisString(null);\nisString(void 0);\nisString(-123);\nisString(0);\nisString(true);\nisString(false);\nisString([]);\nisString({});\nisString(function() {});\nisString(0/0);\n\n//return true\nisString(\"\");\nisString(new String(\"ABC\"));\n
\n\n

WARNING: This works incorrectly in the case:

\n\n
//this is not a string\nvar obj = {\n    //but returns true lol\n    isString: function(){ return true; }\n}\n\nisString(obj) //should be false, but true\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d8e", + "creator": "Grant Miller", + "createdAt": 1531974792000, + "text": "

The following method will check if any variable is a string (including variables that do not exist).

\n\n
const is_string = value => {\n  try {\n    return typeof value() === 'string';\n  } catch (error) {\n    return false;\n  }\n};\n\nlet example = 'Hello, world!';\n\nconsole.log(is_string(() => example)); // true\nconsole.log(is_string(() => variable_doesnt_exist)); // false\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d91", + "creator": "Pato", + "createdAt": 1555343313000, + "text": "

You can use this function to determine the type of anything:

\n
var type = function(obj) {\n    return Object.prototype.toString.apply(obj).replace(/\\[object (.+)\\]/i, '$1').toLowerCase();\n};\n
\n

To check if a variable is a string:

\n
type('my string') === 'string' //true\ntype(new String('my string')) === 'string' //true\ntype(`my string`) === 'string' //true\ntype(12345) === 'string' //false\ntype({}) === 'string' // false\n
\n

https://codepen.io/patodiblasi/pen/NQXPwY?editors=0012

\n

To check for other types:

\n
type(null) //null\ntype(undefined) //undefined\ntype([]) //array\ntype({}) //object\ntype(function() {}) //function\ntype(123) //number\ntype(new Number(123)) //number\ntype(/some_regex/) //regexp\ntype(Symbol("foo")) //symbol\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d90", + "creator": "customcommander", + "createdAt": 1547853778000, + "text": "

I can't honestly see why one would not simply use typeof in this case:

\n\n
if (typeof str === 'string') {\n  return 42;\n}\n
\n\n

Yes it will fail against object-wrapped strings (e.g. new String('foo')) but these are widely regarded as a bad practice and most modern development tools are likely to discourage their use. (If you see one, just fix it!)

\n\n

The Object.prototype.toString trick is something that all front-end developers have been found guilty of doing one day in their careers but don't let it fool you by its polish of clever: it will break as soon as something monkey-patch the Object prototype:

\n\n

\r\n
\r\n
const isString = thing => Object.prototype.toString.call(thing) === '[object String]';\r\n\r\nconsole.log(isString('foo'));\r\n\r\nObject.prototype.toString = () => 42;\r\n\r\nconsole.log(isString('foo'));
\r\n
\r\n
\r\n

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d93", + "creator": "wheelmaker", + "createdAt": 1573919393000, + "text": "
if (s && typeof s.valueOf() === \"string\") {\n  // s is a string\n}\n
\n\n

Works for both string literals let s = 'blah' and for Object Strings let s = new String('blah')

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288b082fcc3049e9286f", + "creator": "Philipp Sumi", + "createdAt": 1583586927000, + "text": "Attention! This will fail on empty strings, since those are falsey.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d92", + "creator": "yaya", + "createdAt": 1569933199000, + "text": "

A Typechecker helper:

\n\n
function isFromType(variable, type){\n  if (typeof type == 'string') res = (typeof variable == type.toLowerCase())\n  else res = (variable.constructor == type)\n  return res\n}\n
\n\n

usage:

\n\n
isFromType('cs', 'string') //true\nisFromType('cs', String) //true\nisFromType(['cs'], Array) //true\nisFromType(['cs'], 'object') //false\n
\n\n

Also if you want it to be recursive(like Array that is an Object), you can use instanceof.

\n\n

(['cs'] instanceof Object //true)

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d95", + "creator": "Filip Seman", + "createdAt": 1631355529000, + "text": "

Implementation from lodash library v4.0.0

\n
// getTag.js\n\nconst toString = Object.prototype.toString;\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction getTag(value) {\n    if (value == null) {\n        return value === undefined \n            ? "[object Undefined]" \n            : "[object Null]";\n    }\n    return toString.call(value);\n}\n
\n
// isString.js\n\nimport getTag from "./getTag.js";\n\n/**\n * Checks if `value` is classified as a `String` primitive or object.\n *\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a string, else `false`.\n * @example\n *\n * isString('abc')\n * // => true\n *\n * isString(1)\n * // => false\n */\nfunction isString(value) {\n    const type = typeof value;\n    return (\n        type === "string" || (type === "object" &&\n                              value != null &&\n                              !Array.isArray(value) &&\n                              getTag(value) == "[object String]")\n    );\n}\n\nexport default isString;\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d94", + "creator": "Marcel Kohls", + "createdAt": 1626770275000, + "text": "

A simple and fast way to test can be using the constructor name attribute.

\n
let x = "abc";\nconsole.log(x.constructor.name === "String"); // true\n\nlet y = new String('abc');\nconsole.log(y.constructor.name === "String"); // true\n
\n

Performance

\n

\"enter

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288b082fcc3049e92873", + "creator": "alex", + "createdAt": 1649928146000, + "text": "your solution is the best here, and also works for other objects, as in the solution offered by @Orwellophile.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90d96", + "creator": "Mohamad", + "createdAt": 1633606240000, + "text": "

A code to have only string without any numbers

\n
isNaN("A") = true;\nparseInt("A") = NaN;\nisNaN(NaN) = true;\n
\n

Than we can use isNaN(parseInt()) to have only the string

\n

\r\n
\r\n
let ignoreNumbers = \"ad123a4m\";\n\nlet ign = ignoreNumbers.split(\"\").map((ele) => isNaN(parseInt(ele)) ? ele : \"\").join(\"\");\n\nconsole.log(ign);
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d98", + "creator": "Salsabeel", + "createdAt": 1651072187000, + "text": "

also we can use isFinite() rather than typeof or isNAN()\ncheck this:

\n
var name="somename",trickyName="123", invalidName="123abc";\n
\n
typeof name == typeof trickyName == typeof invalidName == "string" 🤷‍♀️\n\nisNAN(name)==true\nisNAN(trickyName)==false\nisNAN(invalidName)==true 👀\n
\n

where:

\n
isFinite(name) == false\nisFinite(trickyName)== true\nisFinite(invalidName)== true\n
\n

so we can do:

\n
if(!isFinite(/*any string*/))\n  console.log("it is string type for sure")\n
\n

notice that:

\n
    isFinite("asd123")==false\n    isNAN("asd123")==true\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d97", + "creator": "ZJR", + "createdAt": 1650928629000, + "text": "

I have a technique that's stupid. But straightforward.

\n
if(maybeAString.toUpperCase)\n  weHaveAString(maybeAString)\n
\n

Yeah, it's far from perfect. But it is straightforward.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288b082fcc3049e92877", + "creator": "Mike", + "createdAt": 1656345899000, + "text": "@andreyrk Did you even try it before you commented? Paste this into your JS console: let x = 123; console.log(x.toUpperCase());", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3288b082fcc3049e92878", + "creator": "andreyrk", + "createdAt": 1656429488000, + "text": "@Mike Reread the answer and check if your code matches. Hint: toUpperCase is not the same as toUpperCase()", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3288b082fcc3049e9287a", + "creator": "Mike", + "createdAt": 1656446950000, + "text": "@andreyrk Ah, you're right. I misread it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2254774, + "uvac": 2254800 + } + }, + { + "_id": "62f321bb082fcc3049e8fee7", + "title": "How do I pass command line arguments to a Node.js program?", + "title-lowercase": "how do i pass command line arguments to a node.js program?", + "creator": "milkplus", + "createdAt": 1291427806000, + "status": "open", + "text": "

I have a web server written in Node.js and I would like to launch with a specific folder. I'm not sure how to access arguments in JavaScript. I'm running node like this:

\n\n
$ node server.js folder\n
\n\n

here server.js is my server code. Node.js help says this is possible:

\n\n
$ node -h\nUsage: node [options] script.js [arguments]\n
\n\n

How would I access those arguments in JavaScript? Somehow I was not able to find this information on the web.

\n", + "upvotes": 2833, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1526136, + "answers": 35, + "answerItems": [ + { + "_id": "62f321c7082fcc3049e90a8f", + "creator": "MooGoo", + "createdAt": 1291428332000, + "text": "

Standard Method (no library)

\n\n

The arguments are stored in process.argv

\n\n

Here are the node docs on handling command line args:

\n\n
\n

process.argv is an array containing the command line arguments. The first element will be 'node', the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments.

\n
\n\n
// print process.argv\nprocess.argv.forEach(function (val, index, array) {\n  console.log(index + ': ' + val);\n});\n
\n\n

This will generate:

\n\n
$ node process-2.js one two=three four\n0: node\n1: /Users/mjr/work/node/process-2.js\n2: one\n3: two=three\n4: four\n
\n", + "upvotes": 3655, + "upvoterUsernames": [], + "downvotes": 213, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a91", + "creator": "gor", + "createdAt": 1309752175000, + "text": "

Optimist (node-optimist)

\n\n

Check out optimist library, it is much better than parsing command line options by hand.

\n\n

Update

\n\n

Optimist is deprecated. Try yargs which is an active fork of optimist.

\n", + "upvotes": 204, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a92", + "creator": "balupton", + "createdAt": 1316514005000, + "text": "

Commander.js

\n\n

Works great for defining your options, actions, and arguments. It also generates the help pages for you.

\n\n

Promptly

\n\n

Works great for getting input from the user, if you like the callback approach.

\n\n

Co-Prompt

\n\n

Works great for getting input from the user, if you like the generator approach.

\n", + "upvotes": 147, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a90", + "creator": "Mauvis Ledford", + "createdAt": 1303600390000, + "text": "

To normalize the arguments like a regular javascript function would receive, I do this in my node.js shell scripts:

\n\n
var args = process.argv.slice(2);\n
\n\n

Note that the first arg is usually the path to nodejs, and the second arg is the location of the script you're executing.

\n", + "upvotes": 1690, + "upvoterUsernames": [], + "downvotes": 839, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32718082fcc3049e923a0", + "creator": "Nuno", + "createdAt": 1632927741000, + "text": "It's been over 6 years. Any update?", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f32718082fcc3049e923a2", + "creator": "Mauvis Ledford", + "createdAt": 1643290987000, + "text": "@Nuno I just tested my original answer on Node v16.13.2 (2022-01-11) and yep, it still works perfectly fine 11 years after posting. :)", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f32718082fcc3049e923a4", + "creator": "Timo", + "createdAt": 1644856487000, + "text": "Let's go for the next five years to see the process.argv working;)", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a93", + "creator": "Sean H. Worthington", + "createdAt": 1371427457000, + "text": "

If your script is called myScript.js and you want to pass the first and last name, 'Sean Worthington', as arguments like below:

\n\n
node myScript.js Sean Worthington\n
\n\n

Then within your script you write:

\n\n
var firstName = process.argv[2]; // Will be set to 'Sean'\nvar lastName = process.argv[3]; // Will be set to 'Worthington'\n
\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a94", + "creator": "sgmonda", + "createdAt": 1376389807000, + "text": "

Stdio Library

\n\n

The easiest way to parse command-line arguments in NodeJS is using the stdio module. Inspired by UNIX getopt utility, it is as trivial as follows:

\n\n
var stdio = require('stdio');\nvar ops = stdio.getopt({\n    'check': {key: 'c', args: 2, description: 'What this option means'},\n    'map': {key: 'm', description: 'Another description'},\n    'kaka': {args: 1, required: true},\n    'ooo': {key: 'o'}\n});\n
\n\n

If you run the previous code with this command:

\n\n
node <your_script.js> -c 23 45 --map -k 23 file1 file2\n
\n\n

Then ops object will be as follows:

\n\n
{ check: [ '23', '45' ],\n  args: [ 'file1', 'file2' ],\n  map: true,\n  kaka: '23' }\n
\n\n

So you can use it as you want. For instance:

\n\n
if (ops.kaka && ops.check) {\n    console.log(ops.kaka + ops.check[0]);\n}\n
\n\n

Grouped options are also supported, so you can write -om instead of -o -m.

\n\n

Furthermore, stdio can generate a help/usage output automatically. If you call ops.printHelp() you'll get the following:

\n\n
USAGE: node something.js [--check <ARG1> <ARG2>] [--kaka] [--ooo] [--map]\n  -c, --check <ARG1> <ARG2>   What this option means (mandatory)\n  -k, --kaka                  (mandatory)\n  --map                       Another description\n  -o, --ooo\n
\n\n

The previous message is shown also if a mandatory option is not given (preceded by the error message) or if it is mispecified (for instance, if you specify a single arg for an option and it needs 2).

\n\n

You can install stdio module using NPM:

\n\n
npm install stdio\n
\n", + "upvotes": 135, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a96", + "creator": "Paul van Jaarsveld", + "createdAt": 1386825594000, + "text": "

Several great answers here, but it all seems very complex. This is very similar to how bash scripts access argument values and it's already provided standard with node.js as MooGoo pointed out.\n(Just to make it understandable to somebody that's new to node.js)

\n\n

Example:

\n\n
$ node yourscript.js banana monkey\n\nvar program_name = process.argv[0]; //value will be \"node\"\nvar script_path = process.argv[1]; //value will be \"yourscript.js\"\nvar first_value = process.argv[2]; //value will be \"banana\"\nvar second_value = process.argv[3]; //value will be \"monkey\"\n
\n", + "upvotes": 135, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a95", + "creator": "Amadu Bah", + "createdAt": 1386686496000, + "text": "

You can parse all arguments and check if they exist.

\n\n

file: parse-cli-arguments.js:

\n\n
module.exports = function(requiredArguments){\n    var arguments = {};\n\n    for (var index = 0; index < process.argv.length; index++) {\n        var re = new RegExp('--([A-Za-z0-9_]+)=([A/-Za-z0-9_]+)'),\n            matches = re.exec(process.argv[index]);\n\n        if(matches !== null) {\n            arguments[matches[1]] = matches[2];\n        }\n    }\n\n    for (var index = 0; index < requiredArguments.length; index++) {\n        if (arguments[requiredArguments[index]] === undefined) {\n            throw(requiredArguments[index] + ' not defined. Please add the argument with --' + requiredArguments[index]);\n        }\n    }\n\n    return arguments;\n}\n
\n\n

Than just do:

\n\n
var arguments = require('./parse-cli-arguments')(['foo', 'bar', 'xpto']);\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a98", + "creator": "Zlatko", + "createdAt": 1426293910000, + "text": "

There's an app for that. Well, module. Well, more than one, probably hundreds.

\n\n

Yargs is one of the fun ones, its docs are cool to read.

\n\n

Here's an example from the github/npm page:

\n\n
#!/usr/bin/env node\nvar argv = require('yargs').argv;\nconsole.log('(%d,%d)', argv.x, argv.y);\nconsole.log(argv._);\n
\n\n

Output is here (it reads options with dashes etc, short and long, numeric etc).

\n\n
$ ./nonopt.js -x 6.82 -y 3.35 rum\n(6.82,3.35)\n[ 'rum' ] \n$ ./nonopt.js \"me hearties\" -x 0.54 yo -y 1.12 ho\n(0.54,1.12)\n[ 'me hearties', 'yo', 'ho' ]\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a97", + "creator": "real_ate", + "createdAt": 1404840411000, + "text": "

The up-to-date right answer for this it to use the minimist library. We used to use node-optimist but it has since been deprecated.

\n\n

Here is an example of how to use it taken straight from the minimist documentation:

\n\n
var argv = require('minimist')(process.argv.slice(2));\nconsole.dir(argv);\n
\n\n

-

\n\n
$ node example/parse.js -a beep -b boop\n{ _: [], a: 'beep', b: 'boop' }\n
\n\n

-

\n\n
$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz\n{ _: [ 'foo', 'bar', 'baz' ],\n  x: 3,\n  y: 4,\n  n: 5,\n  a: true,\n  b: true,\n  c: true,\n  beep: 'boop' }\n
\n", + "upvotes": 562, + "upvoterUsernames": [], + "downvotes": 101, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32719082fcc3049e923aa", + "creator": "JK ABC", + "createdAt": 1423192542000, + "text": "Actually, this solution is more helpful for developing command line tool with more flags and arguments, and should be upvoted more IMHO.", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a99", + "creator": "Piyush Sagar", + "createdAt": 1452524078000, + "text": "

Passing,parsing arguments is an easy process. Node provides you with the process.argv property, which is an array of strings, which are the arguments that were used when Node was invoked. \nThe first entry of the array is the Node executable, and the second entry is the name of your script.

\n\n

If you run script with below atguments

\n\n
$ node args.js arg1 arg2\n
\n\n

File : args.js

\n\n
console.log(process.argv)\n
\n\n

You will get array like

\n\n
 ['node','args.js','arg1','arg2']\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a9a", + "creator": "dthree", + "createdAt": 1452749194000, + "text": "

2018 answer based on current trends in the wild:

\n\n
\n\n

Vanilla javascript argument parsing:

\n\n
const args = process.argv;\nconsole.log(args);\n
\n\n

This returns:

\n\n
$ node server.js one two=three four\n['node', '/home/server.js', 'one', 'two=three', 'four']\n
\n\n

Official docs

\n\n
\n\n

Most used NPM packages for argument parsing:

\n\n

Minimist: For minimal argument parsing.

\n\n

Commander.js: Most adopted module for argument parsing.

\n\n

Meow: Lighter alternative to Commander.js

\n\n

Yargs: More sophisticated argument parsing (heavy).

\n\n

Vorpal.js: Mature / interactive command-line applications with argument parsing.

\n", + "upvotes": 679, + "upvoterUsernames": [], + "downvotes": 301, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32719082fcc3049e923ae", + "creator": "Subramanya Rao", + "createdAt": 1637682013000, + "text": "simple way to select custom arg: const list_arg = process.argv.filter((arg) => (['-list', '-l'].includes(arg))).toString();", + "upvotes": 3387, + "upvoterUsernames": [], + "downvotes": 3387, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a9b", + "creator": "Abdennour TOUMI", + "createdAt": 1468550881000, + "text": "
npm install ps-grab\n
\n\n

If you want to run something like this :

\n\n
node greeting.js --user Abdennour --website http://abdennoor.com \n
\n\n

--

\n\n
var grab=require('ps-grab');\ngrab('--username') // return 'Abdennour'\ngrab('--action') // return 'http://abdennoor.com'\n
\n\n
\n\n

Or something like :

\n\n
node vbox.js -OS redhat -VM template-12332 ;\n
\n\n

--

\n\n
var grab=require('ps-grab');\ngrab('-OS') // return 'redhat'\ngrab('-VM') // return 'template-12332'\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a9c", + "creator": "Evren Kutar", + "createdAt": 1470836310000, + "text": "

You can reach command line arguments using system.args. And i use the solution below to parse arguments into an object, so i can get which one i want by name.

\n\n
var system = require('system');\n\nvar args = {};\nsystem.args.map(function(x){return x.split(\"=\")})\n    .map(function(y){args[y[0]]=y[1]});\n
\n\n

now you don't need to know the index of the argument. use it like args.whatever

\n\n
\n

Note: you should use named arguments like file.js x=1 y=2 to use\n this solution.

\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a9d", + "creator": "MatCas", + "createdAt": 1496872893000, + "text": "

A simple snippet if any need it:

\n\n
var fs = require('fs'), objMod = {};\n\nprocess.argv.slice(2).map(function(y, i) {\n  y = y.split('=');\n  if (y[0] && y[1]) objMod[y[0]] = y[1];\n  else console.log('Error in argument number ' + (i+1));\n});\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271a082fcc3049e923b2", + "creator": "barwnikk", + "createdAt": 1521750082000, + "text": "Why do you use require('fs')?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a9e", + "creator": "Cassidy", + "createdAt": 1507061134000, + "text": "

Without libraries

\n\n

If you want to do this in vanilla JS/ES6 you can use the following solution

\n\n

worked only in NodeJS > 6

\n\n
const args = process.argv\n  .slice(2)\n  .map((val, i)=>{\n    let object = {};\n    let [regexForProp, regexForVal] = (() => [new RegExp('^(.+?)='), new RegExp('\\=(.*)')] )();\n    let [prop, value] = (() => [regexForProp.exec(val), regexForVal.exec(val)] )();\n    if(!prop){\n      object[val] = true;\n      return object;\n    } else {\n      object[prop[1]] = value[1] ;\n      return object\n    }\n  })\n  .reduce((obj, item) => {\n    let prop = Object.keys(item)[0];\n    obj[prop] = item[prop];\n    return obj;\n  }, {});\n
\n\n

And this command

\n\n
node index.js host=http://google.com port=8080 production\n
\n\n

will produce the following result

\n\n
console.log(args);//{ host:'http://google.com',port:'8080',production:true }\nconsole.log(args.host);//http://google.com\nconsole.log(args.port);//8080\nconsole.log(args.production);//true\n
\n\n

p.s. Please correct the code in map and reduce function\nif you find more elegant solution, thanks ;)

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a9f", + "creator": "Joseph Merdrignac", + "createdAt": 1507898180000, + "text": "

whithout librairies: using Array.prototype.reduce()

\n\n
const args = process.argv.slice(2).reduce((acc, arg) => {\n\n    let [k, v = true] = arg.split('=')\n    acc[k] = v\n    return acc\n\n}, {})\n
\n\n

for this command node index.js count=2 print debug=false msg=hi

\n\n
console.log(args) // { count: '2', print: true, debug: 'false', msg: 'hi' }\n
\n\n

also,

\n\n

we can change

\n\n
    let [k, v = true] = arg.split('=')\n    acc[k] = v\n
\n\n

by (much longer)

\n\n
    let [k, v] = arg.split('=')\n    acc[k] = v === undefined ? true : /true|false/.test(v) ? v === 'true' : /[\\d|\\.]+/.test(v) ? Number(v) : v\n
\n\n

to auto parse Boolean & Number

\n\n
console.log(args) // { count: 2, print: true, debug: false, msg: 'hi' }\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa0", + "creator": "Adeojo Emmanuel IMM", + "createdAt": 1524528840000, + "text": "

as stated in the node docs \nThe process.argv property returns an array containing the command line arguments passed when the Node.js process was launched.

\n\n

For example, assuming the following script for process-args.js:

\n\n
// print process.argv\nprocess.argv.forEach((val, index) => {\n   console.log(`${index}: ${val}`);\n});\n
\n\n

Launching the Node.js process as:

\n\n
 $ node process-args.js one two=three four\n
\n\n

Would generate the output:

\n\n
0: /usr/local/bin/node\n1: /Users/mjr/work/node/process-args.js\n2: one\n3: two=three\n4: four\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa2", + "creator": "Rubin bhandari", + "createdAt": 1526989004000, + "text": "

The simplest way of retrieving arguments in Node.js is via the process.argv array. This is a global object that you can use without importing any additional libraries to use it. You simply need to pass arguments to a Node.js application, just like we showed earlier, and these arguments can be accessed within the application via the process.argv array.

\n\n

The first element of the process.argv array will always be a file system path pointing to the node executable. The second element is the name of the JavaScript file that is being executed. And the third element is the first argument that was actually passed by the user.

\n\n
'use strict';\n\nfor (let j = 0; j < process.argv.length; j++) {  \n    console.log(j + ' -> ' + (process.argv[j]));\n}\n
\n\n

All this script does is loop through the process.argv array and prints the indexes, along with the elements stored in those indexes. It's very useful for debugging if you ever question what arguments you're receiving, and in what order.

\n\n

You can also use libraries like yargs for working with commnadline arguments.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa1", + "creator": "S.Mishra", + "createdAt": 1524675391000, + "text": "

Most of the people have given good answers. I would also like to contribute something here. I am providing the answer using lodash library to iterate through all command line arguments we pass while starting the app:

\n\n
// Lodash library\nconst _ = require('lodash');\n\n// Function that goes through each CommandLine Arguments and prints it to the console.\nconst runApp = () => {\n    _.map(process.argv, (arg) => {\n        console.log(arg);\n    });\n};\n\n// Calling the function.\nrunApp();\n
\n\n

To run above code just run following commands:

\n\n
npm install\nnode index.js xyz abc 123 456\n
\n\n

The result will be:

\n\n
xyz \nabc \n123\n456\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa5", + "creator": "grebenyuksv", + "createdAt": 1532953476000, + "text": "

Here's my 0-dep solution for named arguments:

\n\n
const args = process.argv\n    .slice(2)\n    .map(arg => arg.split('='))\n    .reduce((args, [value, key]) => {\n        args[value] = key;\n        return args;\n    }, {});\n\nconsole.log(args.foo)\nconsole.log(args.fizz)\n
\n\n

Example:

\n\n
$ node test.js foo=bar fizz=buzz\nbar\nbuzz\n
\n\n

Note: Naturally this will fail when the argument contains a =. This is only for very simple usage.

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa4", + "creator": "Nouman Dilshad", + "createdAt": 1530538807000, + "text": "

proj.js

\n
for(var i=0;i<process.argv.length;i++){\n  console.log(process.argv[i]);\n}\n
\n

Terminal:

\n
nodemon app.js "arg1" "arg2" "arg3"\n
\n

Result:

\n
0 'C:\\\\Program Files\\\\nodejs\\\\node.exe'\n1 'C:\\\\Users\\\\Nouman\\\\Desktop\\\\Node\\\\camer nodejs\\\\proj.js'\n2 'arg1' your first argument you passed.\n3 'arg2' your second argument you passed.\n4 'arg3' your third argument you passed.\n
\n

Explaination:

\n
    \n
  1. The directory of node.exe in your machine (C:\\Program Files\\nodejs\\node.exe)
  2. \n
  3. The directory of your project file\n(proj.js)
  4. \n
  5. Your first argument to node (arg1)
  6. \n
  7. Your second argument to node (arg2)
  8. \n
  9. Your third argument to node (arg3)
  10. \n
\n
\n

your actual arguments start form second index of argv array, that is process.argv[2].

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa3", + "creator": "bhwp", + "createdAt": 1528453763000, + "text": "

process.argv is your friend, capturing command line args is natively supported in Node JS. See example below::

\n\n
process.argv.forEach((val, index) => {\n  console.log(`${index}: ${val}`);\n})\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa6", + "creator": "Akshay Rajput", + "createdAt": 1544602854000, + "text": "

Although Above answers are perfect, and someone has already suggested yargs, using the package is really easy.\nThis is a nice package which makes passing arguments to command line really easy.

\n\n
npm i yargs\nconst yargs = require(\"yargs\");\nconst argv = yargs.argv;\nconsole.log(argv);\n
\n\n

Please visit https://yargs.js.org/ for more info.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271a082fcc3049e923bc", + "creator": "user3285954", + "createdAt": 1572824690000, + "text": "Yargs doesn't affect how arguments are passed on command line, it only helps in reading them in code.", + "upvotes": 416, + "upvoterUsernames": [], + "downvotes": 416, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90aa8", + "creator": "isacvale", + "createdAt": 1551112461000, + "text": "

Passing arguments is easy, and receiving them is just a matter of reading the process.argv array Node makes accessible from everywhere, basically. But you're sure to want to read them as key/value pairs, so you'll need a piece to script to interpret it.

\n\n

Joseph Merdrignac posted a beautiful one using reduce, but it relied on a key=value syntax instead of -k value and --key value. I rewrote it much uglier and longer to use that second standard, and I'll post it as an answer because it wouldn't fit as a commentary. But it does get the job done.

\n\n
   const args = process.argv.slice(2).reduce((acc,arg,cur,arr)=>{\n     if(arg.match(/^--/)){\n       acc[arg.substring(2)] = true\n       acc['_lastkey'] = arg.substring(2)\n     } else\n     if(arg.match(/^-[^-]/)){\n       for(key of arg.substring(1).split('')){\n         acc[key] = true\n         acc['_lastkey'] = key\n       }\n     } else\n       if(acc['_lastkey']){\n         acc[acc['_lastkey']] = arg\n         delete acc['_lastkey']\n       } else\n         acc[arg] = true\n     if(cur==arr.length-1)\n       delete acc['_lastkey']\n     return acc\n   },{})\n
\n\n

With this code a command node script.js alpha beta -charlie delta --echo foxtrot would give you the following object

\n\n
\nargs = {\n \"alpha\":true,\n \"beta\":true,\n \"c\":true,\n \"h\":true,\n \"a\":true,\n \"r\":true\n \"l\":true,\n \"i\":true,\n \"e\":\"delta\",\n \"echo\":\"foxtrot\"\n}\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aa7", + "creator": "Michael Warner", + "createdAt": 1546976593000, + "text": "

No Libs with Flags Formatted into a Simple Object

\n
function getArgs () {\n    const args = {};\n    process.argv\n        .slice(2, process.argv.length)\n        .forEach( arg => {\n        // long arg\n        if (arg.slice(0,2) === '--') {\n            const longArg = arg.split('=');\n            const longArgFlag = longArg[0].slice(2,longArg[0].length);\n            const longArgValue = longArg.length > 1 ? longArg[1] : true;\n            args[longArgFlag] = longArgValue;\n        }\n        // flags\n        else if (arg[0] === '-') {\n            const flags = arg.slice(1,arg.length).split('');\n            flags.forEach(flag => {\n            args[flag] = true;\n            });\n        }\n    });\n    return args;\n}\nconst args = getArgs();\nconsole.log(args);\n
\n

Examples

\n

Simple

\n

input

\n
node test.js -D --name=Hello\n
\n

output

\n
{ D: true, name: 'Hello' }\n
\n

Real World

\n

input

\n
node config/build.js -lHRs --ip=$HOST --port=$PORT --env=dev\n
\n

output

\n
{ \n  l: true,\n  H: true,\n  R: true,\n  s: true,\n  ip: '127.0.0.1',\n  port: '8080',\n  env: 'dev'\n}\n
\n", + "upvotes": 164, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271b082fcc3049e923c0", + "creator": "hastrb", + "createdAt": 1636700068000, + "text": ".slice(2, process.argv.length) isn't the second arg redundant? .slice() goes to the end of string by default.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90aa9", + "creator": "tibalt", + "createdAt": 1568379824000, + "text": "

ES6-style no-dependencies solution:

\n
const longArgs = arg => {\n    const [ key, value ] = arg.split('=');\n    return { [key.slice(2)]: value || true }\n};\n\nconst flags = arg => [...arg.slice(1)].reduce((flagObj, f) => ({ ...flagObj, [f]: true }), {});\n\n\nconst args = () =>\n    process.argv\n        .slice(2)\n        .reduce((args, arg) => ({\n            ...args,\n            ...((arg.startsWith('--') && longArgs(arg)) || (arg[0] === '-' && flags(arg)))\n        }), {});\n\nconsole.log(args());\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271b082fcc3049e923c3", + "creator": "Endless", + "createdAt": 1614397272000, + "text": "writing too much es6 like this can make the code feel unreadable at first glance", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90aaa", + "creator": "Manvel", + "createdAt": 1569943524000, + "text": "

Parsing argument based on standard input ( --key=value )

\n
const argv = (() => {\n    const arguments = {};\n    process.argv.slice(2).map( (element) => {\n        const matches = element.match( '--([a-zA-Z0-9]+)=(.*)');\n        if ( matches ){\n            arguments[matches[1]] = matches[2]\n                .replace(/^['"]/, '').replace(/['"]$/, '');\n        }\n    });\n    return arguments;\n})();\n
\n

Command example

\n
node app.js --name=stackoverflow --id=10 another-argument --text="Hello World"\n
\n

Result of argv: console.log(argv)

\n
{\n    name: "stackoverflow",\n    id: "10",\n    text: "Hello World"\n}\n
\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aab", + "creator": "rmolinamir", + "createdAt": 1583261575000, + "text": "

TypeScript solution with no libraries:

\n\n
interface IParams {\n  [key: string]: string\n}\n\nfunction parseCliParams(): IParams {\n  const args: IParams = {};\n  const rawArgs = process.argv.slice(2, process.argv.length);\n  rawArgs.forEach((arg: string, index) => {\n    // Long arguments with '--' flags:\n    if (arg.slice(0, 2).includes('--')) {\n      const longArgKey = arg.slice(2, arg.length);\n      const longArgValue = rawArgs[index + 1]; // Next value, e.g.: --connection connection_name\n      args[longArgKey] = longArgValue;\n    }\n    // Shot arguments with '-' flags:\n    else if (arg.slice(0, 1).includes('-')) {\n      const longArgKey = arg.slice(1, arg.length);\n      const longArgValue = rawArgs[index + 1]; // Next value, e.g.: -c connection_name\n      args[longArgKey] = longArgValue;\n    }\n  });\n  return args;\n}\n\nconst params = parseCliParams();\nconsole.log('params: ', params);\n
\n\n

Input: ts-node index.js -p param --parameter parameter

\n\n

Output: { p: 'param ', parameter: 'parameter' }

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aac", + "creator": "Giorgio Robino", + "createdAt": 1600932427000, + "text": "

I extended the getArgs function just to get also commands, as well as flags (-f, --anotherflag) and named args (--data=blablabla):

\n
    \n
  1. The module
  2. \n
\n
/**\n * @module getArgs.js\n * get command line arguments (commands, named arguments, flags)\n *\n * @see https://stackoverflow.com/a/54098693/1786393\n *\n * @return {Object}\n *\n */\nfunction getArgs () {\n  const commands = []\n  const args = {}\n  process.argv\n    .slice(2, process.argv.length)\n    .forEach( arg => {\n      // long arg\n      if (arg.slice(0,2) === '--') {\n        const longArg = arg.split('=')\n        const longArgFlag = longArg[0].slice(2,longArg[0].length)\n        const longArgValue = longArg.length > 1 ? longArg[1] : true\n        args[longArgFlag] = longArgValue\n     }\n     // flags\n      else if (arg[0] === '-') {\n        const flags = arg.slice(1,arg.length).split('')\n        flags.forEach(flag => {\n          args[flag] = true\n        })\n      }\n     else {\n      // commands\n      commands.push(arg)\n     } \n    })\n  return { args, commands }\n}\n\n\n// test\nif (require.main === module) {\n  // node getArgs test --dir=examples/getUserName --start=getUserName.askName\n  console.log( getArgs() )\n}\n\nmodule.exports = { getArgs }\n\n
\n
    \n
  1. Usage example:
  2. \n
\n
$ node lib/getArgs test --dir=examples/getUserName --start=getUserName.askName\n{\n  args: { dir: 'examples/getUserName', start: 'getUserName.askName' },\n  commands: [ 'test' ]\n}\n\n$ node lib/getArgs --dir=examples/getUserName --start=getUserName.askName test tutorial\n{\n  args: { dir: 'examples/getUserName', start: 'getUserName.askName' },\n  commands: [ 'test', 'tutorial' ]\n}\n\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aad", + "creator": "Carson", + "createdAt": 1627655154000, + "text": "

You can get command-line information from process.argv()

\n

And I don't want to limit the problem to node.js. Instead, I want to turn it into how to parse the string as the argument.

\n
console.log(ArgumentParser(`--debug --msg="Hello World" --title="Test" --desc=demo -open --level=5 --MyFloat=3.14`))\n
\n

output

\n
{\n  "debug": true,\n  "msg": "Hello World",\n  "title": "Test",\n  "desc": "demo",\n  "open": true,\n  "level": 5,\n  "MyFloat": 3.14\n}\n
\n

code

\n

Pure javascript, no dependencies needed

\n

\r\n
\r\n
// 👇 Below is Test\n(() => {\n  window.onload = () => {\n    const testArray = [\n      `--debug --msg=\"Hello World\" --title=\"Test\" --desc=demo -open --level=5 --MyFloat=3.14`,\n    ]\n    for (const testData of testArray) {\n      try {\n        const obj = ArgumentParser(testData)\n        console.log(obj)\n      } catch (e) {\n        console.error(e.message)\n      }\n    }\n  }\n})()\n\n// 👇 Script\nclass ParserError extends Error {\n\n}\n\nfunction Cursor(str, pos) {\n  this.str = str\n  this.pos = pos\n  this.MoveRight = (step = 1) => {\n    this.pos += step\n  }\n\n  this.MoveToNextPara = () => {\n    const curStr = this.str.substring(this.pos)\n    const match = /^(?<all> *--?(?<name>[a-zA-Z_][a-zA-Z0-9_]*)(=(?<value>[^-]*))?)/g.exec(curStr) // https://regex101.com/r/k004Gv/2\n    if (match) {\n      let {groups: {all, name, value}} = match\n\n      if (value !== undefined) {\n        value = value.trim()\n        if (value.slice(0, 1) === '\"') { // string\n          if (value.slice(-1) !== '\"') {\n            throw new ParserError(`Parsing error: '\"' expected`)\n          }\n          value = value.slice(1, -1)\n        } else { // number or string (without '\"')\n          value = isNaN(Number(value)) ? String(value) : Number(value)\n        }\n      }\n\n      this.MoveRight(all.length)\n      return [name, value ?? true] // If the value is undefined, then set it as ture.\n    }\n    throw new ParserError(`illegal format detected. ${curStr}`)\n  }\n}\n\nfunction ArgumentParser(str) {\n  const obj = {}\n  const cursor = new Cursor(str, 0)\n  while (1) {\n    const [name, value] = cursor.MoveToNextPara()\n    obj[name] = value\n    if (cursor.pos === str.length) {\n      return obj\n    }\n  }\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aae", + "creator": "Andrew Odri", + "createdAt": 1633107164000, + "text": "

Simple + ES6 + no-dependency + supports boolean flags

\n
const process = require( 'process' );\n\nconst argv = key => {\n  // Return true if the key exists and a value is defined\n  if ( process.argv.includes( `--${ key }` ) ) return true;\n\n  const value = process.argv.find( element => element.startsWith( `--${ key }=` ) );\n\n  // Return null if the key does not exist and a value is not defined\n  if ( !value ) return null;\n  \n  return value.replace( `--${ key }=` , '' );\n}\n
\n

Output:

\n\n", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271b082fcc3049e923ca", + "creator": "temporary_user_name", + "createdAt": 1650398768000, + "text": "Oh that's so obvious now that you say it. I totally knew that and wasn't thinking. Thank you for the reminder.", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90ab0", + "creator": "Idan_Krupnik", + "createdAt": 1648900632000, + "text": "

NodeJS exposes a global variable called process.

\n

we can use:

\n
process.argv\n
\n

to get the command line arguments passes to our script.

\n

The output of process.argv will be a list in the following order:

\n
[\nfull-path-to-node-executable,\nfull-path-to-the-script-file\n...additonal-arguments-we-provide\n]\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90aaf", + "creator": "Preston L. Bannister", + "createdAt": 1639557796000, + "text": "

The original question was asking to pass command line arguments, not about more complex parsing of arguments. Yet with all the complex answers, they all missed one simple, useful variation.

\n

Did you know that the Unix shell supports named arguments? This dates back to the original Bourne shell in the 1980s. Usage is simple:

\n
$ FOO=one BAR=two nodejs myscript.js\n
\n

To fetch the parameters in Javascript:

\n
var foo = process.env.FOO;\nvar bar = process.env.BAR;\n
\n

Named parameters are much easier to read, once you get past two or three parameters. Optional parameters are simple, and order is not fixed.

\n

(This might even work on Windows, with the recent support for Unix shells.)

\n

Also, shockingly few Unix programmers know of this usage. :)

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3271b082fcc3049e923ce", + "creator": "CherryDT", + "createdAt": 1645635408000, + "text": "These aren't "named parameters" as such, they are environment variables.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f3271b082fcc3049e923cf", + "creator": "Preston L. Bannister", + "createdAt": 1645675960000, + "text": "They are parameters, and they are named. That they are environment variables is incidental. Or perhaps you have some other definition? :)", + "upvotes": 2276, + "upvoterUsernames": [], + "downvotes": 2276, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90ab1", + "creator": "Hender", + "createdAt": 1650227722000, + "text": "

In the node code require the built in process lib.

\n
const {argv} = require('process')\n
\n

Run the program with their arguments.

\n
$ node process-args.js one two=three four\n
\n

argv is the array that follows:

\n
argv[0] = /usr/bin/node\nargv[1] = /home/user/process-args.js\nargv[2] = one\nargv[3] = two=three\nargv[4] = four\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 1528969, + "uvac": 1529004 + } + }, + { + "_id": "62f321bb082fcc3049e8fed2", + "title": "How can I check if an object is an array?", + "title-lowercase": "how can i check if an object is an array?", + "creator": "mpen", + "createdAt": 1295808784000, + "status": "open", + "text": "

I'm trying to write a function that either accepts a list of strings, or a single string. If it's a string, then I want to convert it to an array with just the one item so I can loop over it without fear of an error.

\n

So how do I check if the variable is an array?

\n", + "upvotes": 5196, + "upvoterUsernames": [], + "downvotes": 1959, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1925271, + "answers": 50, + "answerItems": [ + { + "_id": "62f321c3082fcc3049e906d6", + "creator": "Tim Down", + "createdAt": 1295812272000, + "text": "

If the only two kinds of values that could be passed to this function are a string or an array of strings, keep it simple and use a typeof check for the string possibility:

\n\n
function someFunc(arg) {\n    var arr = (typeof arg == \"string\") ? [arg] : arg;\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32554082fcc3049e91cb7", + "creator": "mpen", + "createdAt": 1295812883000, + "text": "Yeah... that'd work for this scenario, but not in general. Ended up using varargs anyway. :)", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906d7", + "creator": "John Wundes", + "createdAt": 1332522431000, + "text": "

The best solution I've seen is a cross-browser replacement for typeof. Check Angus Croll's solution.

\n

The TL;DR version is below, but the article is a great discussion of the issue so you should read it if you have time.

\n
Object.toType = function(obj) {\n    return ({}).toString.call(obj).match(/\\s([a-z|A-Z]+)/)[1].toLowerCase();\n}\n// ... and usage:\nObject.toType([1,2,3]); //"array" (all browsers)\n\n// or to test...\nvar shouldBeAnArray = [1,2,3];\nif(Object.toType(shouldBeAnArray) === 'array'){/* do stuff */};\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906d8", + "creator": "janr", + "createdAt": 1333368939000, + "text": "

jQuery also offers an $.isArray() method:

\n\n

\r\n
\r\n
var a = [\"A\", \"AA\", \"AAA\"];\r\n\r\nif($.isArray(a)) {\r\n  alert(\"a is an array!\");\r\n} else {\r\n  alert(\"a is not an array!\");\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 347, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906da", + "creator": "Billy Moon", + "createdAt": 1344432841000, + "text": "

I would make a function to test the type of object you are dealing with...

\n\n

\r\n
\r\n
function whatAmI(me){ return Object.prototype.toString.call(me).split(/\\W/)[2]; }\r\n\r\n// tests\r\nconsole.log(\r\n  whatAmI([\"aiming\",\"@\"]),\r\n  whatAmI({living:4,breathing:4}),\r\n  whatAmI(function(ing){ return ing+\" to the global window\" }),\r\n  whatAmI(\"going to do with you?\")\r\n);\r\n\r\n// output: Array Object Function String
\r\n
\r\n
\r\n

\n\n

then you can write a simple if statement...

\n\n
if(whatAmI(myVar) === \"Array\"){\n    // do array stuff\n} else { // could also check `if(whatAmI(myVar) === \"String\")` here to be sure\n    // do string stuff\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906d9", + "creator": "Eugene", + "createdAt": 1333500366000, + "text": "

I know, that people are looking for some kind of raw JavaScript approach. But if you want think less about it, take a look at Underscore.js' isArray:

\n
_.isArray(object)\n
\n

It returns true if object is an Array.

\n
(function(){ return _.isArray(arguments); })();\n=> false\n_.isArray([1,2,3]);\n=> true\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32591082fcc3049e91cbc", + "creator": "Michał Perłakowski", + "createdAt": 1452376435000, + "text": ""Unless another tag for a framework/library is also included, a pure JavaScript answer is expected."", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906db", + "creator": "MidnightTortoise", + "createdAt": 1346781370000, + "text": "

A simple function to check this:

\n
function isArray(object)\n{\n    return object.constructor === Array;\n}\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32591082fcc3049e91cbe", + "creator": "mpen", + "createdAt": 1346783968000, + "text": "I'd reduce that down to one line return object.constructor === Array -- but are you sure that this will only return true for arrays?", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cbf", + "creator": "John Henckel", + "createdAt": 1426867089000, + "text": "This fails badly if object is undefined or null.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906dc", + "creator": "ajax333221", + "createdAt": 1347656535000, + "text": "

As MDN says in here:

\n\n
\n

use Array.isArray or Object.prototype.toString.call to differentiate\n regular objects from arrays

\n
\n\n

Like this:

\n\n\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906dd", + "creator": "CruorVult", + "createdAt": 1355158917000, + "text": "

Array.isArray works fast, but it isn't supported by all versions of browsers.

\n

So you could make an exception for others and use a universal method:

\n
    Utils = {};\n    Utils.isArray = ('isArray' in Array) ?\n        Array.isArray :\n        function (value) {\n            return Object.prototype.toString.call(value) === '[object Array]';\n        }\n
\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906de", + "creator": "namuol", + "createdAt": 1357894619000, + "text": "

Here's my lazy approach:

\n\n
if (Array.prototype.array_ === undefined) {\n  Array.prototype.array_ = true;\n}\n\n// ...\n\nvar test = [],\n    wat = {};\n\nconsole.log(test.array_ === true); // true\nconsole.log(wat.array_ === true);  // false\n
\n\n

I know it's sacrilege to \"mess with\" the prototype, but it appears to perform significantly better than the recommended toString method.

\n\n

Note: A pitfall of this approach is that it wont work across iframe boundaries, but for my use case this is not an issue.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32591082fcc3049e91cc4", + "creator": "test30", + "createdAt": 1405935636000, + "text": "its not better in terms of performance anymore, at least on FF30 on Ubuntu 64-bit", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cc6", + "creator": "Bergi", + "createdAt": 1438931929000, + "text": "fooled by wat = {array_: true} objects.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cc7", + "creator": "namuol", + "createdAt": 1438991777000, + "text": "@Bergi: Yes, that should be obvious. If you're setting obj.array_ = true, then you're only fooling yourself.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cc9", + "creator": "namuol", + "createdAt": 1440105886000, + "text": "Agreed! Naming is important. This answer was just a quick hack meant to be used "in a vacuum" -- not really meant for use in libraries.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cca", + "creator": "Roy Tinker", + "createdAt": 1482180922000, + "text": "Potentially fooled by JSON.parse(someDataFromElsewhere).items.array_ returning true.", + "upvotes": 1243, + "upvoterUsernames": [], + "downvotes": 1243, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906df", + "creator": "Ahmet DAL", + "createdAt": 1358240287000, + "text": "

You can check the type of your variable whether it is an array with;

\n\n
var myArray=[];\n\nif(myArray instanceof Array)\n{\n....\n}\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32591082fcc3049e91ccc", + "creator": "mpen", + "createdAt": 1358267421000, + "text": "A few people have already mentioned instanceof.. I think it fails under a few weird scenarios.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906e0", + "creator": "Marcus", + "createdAt": 1360666986000, + "text": "

Since I don't like any Object.prototype-calls, I searched for another solution. Especially because the solutions of ChaosPandion won't always work, and the solution of MidnightTortoise with isArray() doesn't work with arrays coming from the DOM (like getElementsByTagName). And finally I found an easy and cross-browser solution, which probably also would have worked with Netscape 4. ;)

\n

It's just these four lines (checking any object h):

\n
function isArray(h){\n    if((h.length!=undefined&&h[0]!=undefined)||(h.length===0&&h[0]===undefined)){\n        return true;\n    }\n    else{ return false; }\n}\n
\n

I already tested these arrays (all return true):

\n
1) array=d.getElementsByName('some_element'); //'some_element' can be a real or unreal element\n2) array=[];\n3) array=[10];\n4) array=new Array();\n5) array=new Array();\n   array.push("whatever");\n
\n

Does this work for all cases? Or is there a case where my solution doesn't work?

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32591082fcc3049e91cce", + "creator": "the system", + "createdAt": 1360951608000, + "text": "...and a NodeList isn't an Array anyway.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cd0", + "creator": "Marcus", + "createdAt": 1360961546000, + "text": "Thanks for sharing your test results. This is getting my a lot more insight how Javascript works internally.", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + }, + { + "_id": "62f32591082fcc3049e91cd2", + "creator": "Marcus", + "createdAt": 1360962851000, + "text": "The usage of charAt just vanished everywhere out of my code. ;)", + "upvotes": 331, + "upvoterUsernames": [], + "downvotes": 331, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906e1", + "creator": "Safareli", + "createdAt": 1362472000000, + "text": "

You can use Array.isArray(). Here is a polyfill:

\n
if (Array.isArray == null) {\n  Array.isArray = (arr) => Object.prototype.toString.call(arr) === "[object Array]"\n}\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906e2", + "creator": "exebook", + "createdAt": 1363057063000, + "text": "
A = [1,2,3]\nconsole.log(A.map == [].map)\n
\n

In search for the shortest version, here is what I got so far.

\n

Note, there is no perfect function that will always detect all possible combinations. It is better to know all abilities and limitations of your tools than expect a magic tool.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32592082fcc3049e91cd4", + "creator": "dmi3y", + "createdAt": 1364698022000, + "text": "slight derivation of mine A.map !== undefined but yeah, that could be slippy road in the world of monkey patchers ;)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906e3", + "creator": "RoboTamer", + "createdAt": 1383081964000, + "text": "
function isArray(value) {\n    if (value) {\n        if (typeof value === 'object') {\n            return (Object.prototype.toString.call(value) == '[object Array]')\n        }\n    }\n    return false;\n}\n\nvar ar = [\"ff\",\"tt\"]\nalert(isArray(ar))\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906e6", + "creator": "Brad Parks", + "createdAt": 1391300994000, + "text": "

A simple function for testing if an input value is an array is the following:

\n\n
function isArray(value)\n{\n  return Object.prototype.toString.call(value) === '[object Array]';\n}\n
\n\n

This works cross browser, and with older browsers. This is pulled from T.J. Crowders' blog post

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906e5", + "creator": "Fela", + "createdAt": 1389031915000, + "text": "

In modern browsers you can do:

\n
Array.isArray(obj)\n
\n

(Supported by Chrome 5, Firefox 4.0, Internet Explorer 9, Opera 10.5 and Safari 5)

\n

For backward compatibility you can add the following:

\n
// Only implement if no native implementation is available\nif (typeof Array.isArray === 'undefined') {\n  Array.isArray = function(obj) {\n    return Object.prototype.toString.call(obj) === '[object Array]';\n  }\n};\n
\n

If you use jQuery you can use jQuery.isArray(obj) or $.isArray(obj). If you use Underscore.js you can use _.isArray(obj).

\n

If you don't need to detect arrays created in different frames you can also just use instanceof:

\n
obj instanceof Array\n
\n", + "upvotes": 1837, + "upvoterUsernames": [], + "downvotes": 335, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906e8", + "creator": "rsbkk", + "createdAt": 1428130700000, + "text": "

I do this in a very simple way. It works for me.

\n
Array.prototype.isArray = true;\n\na=[]; b={};\na.isArray  // true\nb.isArray  // (undefined -> false)\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32592082fcc3049e91cda", + "creator": "Bergi", + "createdAt": 1438931837000, + "text": "fooled by {isArray:true}", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32592082fcc3049e91cdb", + "creator": "Roy Tinker", + "createdAt": 1482180744000, + "text": "JSON.parse(someDataFromElsewhere).items.isArray could return true (depending on the data) and break your code.", + "upvotes": 94, + "upvoterUsernames": [], + "downvotes": 94, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906e7", + "creator": "Dexygen", + "createdAt": 1427920554000, + "text": "

This is my attempt to improve on this answer taking into account the comments:

\n\n
var isArray = myArray && myArray.constructor === Array;\n
\n\n

It gets rid of the if/else, and accounts for the possibility of the array being null or undefined

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32592082fcc3049e91cdd", + "creator": "TechTurtle", + "createdAt": 1487698670000, + "text": "constructor is not available in ES5", + "upvotes": 186, + "upvoterUsernames": [], + "downvotes": 186, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906e9", + "creator": "Sensei_Shoh", + "createdAt": 1428295064000, + "text": "

Here is a solution that I came up with and have been using for my projects...

\n
function isArray (o) {\n    return typeof o === "object" && o.length !== undefined;\n}\n\nisArray({}); // false\nisArray(1); // false\nisArray("str"); // false\nisArray(function(){}); // false\n\nisArray([]); // true\n
\n

The only pitfall is that it will give a false positive if your object happens to have a length property:

\n
isArray({length:0}); // true\n
\n

If you are okay with that drawback and know your pure objects won't have that property, it's a clean solution and should be faster than the Object.prototype.toString.call method.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32592082fcc3049e91ce0", + "creator": "László Kardinál", + "createdAt": 1456327151000, + "text": "isArray( new String() ); returns true", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906ea", + "creator": "user3367643", + "createdAt": 1428929597000, + "text": "

Use:

\n
var is_array = function (value) {\n   return value &&\n     typeof value === 'object' &&\n     typeof value.length === 'number' &&\n     typeof value.splice === 'function' &&\n    !(value.propertyIsEnumerable('length'));\n};\n
\n

This function has been taken from "JavaScript: The Good Parts" book, and it works perfect for me.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906e4", + "creator": "Salvador Dali", + "createdAt": 1387091479000, + "text": "

There is a nice example in Stoyan Stefanov's book JavaScript Patterns which is supposed to handle all possible problems as well as use the ECMAScript 5 method Array.isArray().

\n

So here it is:

\n
if (typeof Array.isArray === "undefined") {\n    Array.isArray = function (arg) {\n        return Object.prototype.toString.call(arg) === "[object Array]";\n    };\n}\n
\n

By the way, if you are using jQuery, you can use its method $.isArray().

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32592082fcc3049e91ce4", + "creator": "Marco Demaio", + "createdAt": 1392734984000, + "text": "+1: why not just a simple if(!Array.isArray) {... ?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906eb", + "creator": "Gaurav", + "createdAt": 1436357923000, + "text": "
 var length = 16;                               // Number\n var lastName = \"Johnson\";                      // String\n var cars = [\"Saab\", \"Volvo\", \"BMW\"];           // Array\n var x = {firstName:\"John\", lastName:\"Doe\"};\n\n Object.prototype.myCheck= function(){\n if (this.constructor === Array){\n          alert('array');\n        }else if (this.constructor === Object)\n       {\n         alert('object');\n        }else if (this.constructor === Number)\n        {\n          alert('number');\n        }else if (this.constructor === String)\n        {\n          alert('string');\n        }\n\n }\n cars.myCheck();\n lastName.myCheck();\n length.myCheck();\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32593082fcc3049e91ce5", + "creator": "mpen", + "createdAt": 1436373425000, + "text": "Why did you make your method a prototype of Object if you aren't going to call it like cars.myCheck()?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32593082fcc3049e91ce6", + "creator": "Gaurav", + "createdAt": 1436462749000, + "text": "yes mark you are correct it should be cars.myCheck().. updated the answer", + "upvotes": 339, + "upvoterUsernames": [], + "downvotes": 339, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906ec", + "creator": "le_top", + "createdAt": 1438427428000, + "text": "

I have updated the jsperf fiddle with two alternative methods as well as error checking.

\n\n

It turns out that the method defining a constant value in the 'Object' and 'Array' prototypes is faster than any of the other methods. It is a somewhat surprising result.

\n\n

\r\n
\r\n
/* Initialisation */\r\nObject.prototype.isArray = function() {\r\n  return false;\r\n};\r\nArray.prototype.isArray = function() {\r\n  return true;\r\n};\r\nObject.prototype._isArray = false;\r\nArray.prototype._isArray = true;\r\n\r\nvar arr = [\"1\", \"2\"];\r\nvar noarr = \"1\";\r\n\r\n/* Method 1 (function) */\r\nif (arr.isArray()) document.write(\"arr is an array according to function<br/>\");\r\nif (!noarr.isArray()) document.write(\"noarr is not an array according to function<br/>\");\r\n/* Method 2 (value) - **** FASTEST ***** */\r\nif (arr._isArray) document.write(\"arr is an array according to member value<br/>\");\r\nif (!noarr._isArray) document.write(\"noarr is not an array according to member value<br/>\");
\r\n
\r\n
\r\n

\n\n

These two methods do not work if the variable takes the undefined value, but they do work if you are certain that they have a value. With regards to checking with performance in mind if a value is an array or a single value, the second method looks like a valid fast method. It is slightly faster than 'instanceof' on Chrome, twice as fast as the second best method in Internet Explorer, Opera and Safari (on my machine).

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906ee", + "creator": "VIJAY P", + "createdAt": 1456379548000, + "text": "

You can try this:

\n\n
var arr = []; (or) arr = new Array();\nvar obj = {}; (or) arr = new Object();\n\narr.constructor.prototype.hasOwnProperty('push') //true\n\nobj.constructor.prototype.hasOwnProperty('push') // false\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906ed", + "creator": "shinobi", + "createdAt": 1449396759000, + "text": "

This is the fastest among all methods (all browsers supported):

\n\n
function isArray(obj){\n    return !!obj && obj.constructor === Array;\n}\n
\n", + "upvotes": 126, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f0", + "creator": "yesil", + "createdAt": 1472025763000, + "text": "

The following could be used if you know that your object doesn't have a concat method.

\n\n

\r\n
\r\n
var arr = [];\r\nif (typeof arr.concat === 'function') {\r\n    console.log(\"It's an array\");\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32593082fcc3049e91cea", + "creator": "Alireza", + "createdAt": 1491744230000, + "text": "This is a good trick, but could be overridden... but most of the times should get the result", + "upvotes": 757, + "upvoterUsernames": [], + "downvotes": 757, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906ef", + "creator": "Kamuran Sönecek", + "createdAt": 1456391423000, + "text": "

You can use this function to get the data type.

\n
var myAr = [1,2];\n\ncheckType(myAr);\n\nfunction checkType(data) {\n  if(typeof data ==='object') {\n    if(Object.prototype.toString.call(data).indexOf('Array') !== (-1)) {\n      return 'array';\n    } else {\n      return 'object';\n    }\n  } else {\n    return typeof data;\n  }\n}\n\nif(checkType(myAr) === 'array') {\n  console.log('yes, it is an array')\n};\n
\n", + "upvotes": 111, + "upvoterUsernames": [], + "downvotes": 111, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32593082fcc3049e91cec", + "creator": "Kermit_ice_tea", + "createdAt": 1469649859000, + "text": "All the op asked for was a simple efficient check.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906f1", + "creator": "mpen", + "createdAt": 1477324081000, + "text": "

This function will turn almost anything into an array:

\n\n
function arr(x) {\n    if(x === null || x === undefined) {\n        return [];\n    }\n    if(Array.isArray(x)) {\n        return x;\n    }\n    if(isString(x) || isNumber(x)) {\n        return [x];\n    }\n    if(x[Symbol.iterator] !== undefined || x.length !== undefined) {\n        return Array.from(x);\n    }\n    return [x];\n}\n\nfunction isString(x) {\n    return Object.prototype.toString.call(x) === \"[object String]\"\n}\n\nfunction isNumber(x) {\n    return Object.prototype.toString.call(x) === \"[object Number]\"\n}\n
\n\n

It uses some newer browser features so you may want to polyfill this for maximum support.

\n\n

Examples:

\n\n
> arr(null);\n[]\n> arr(undefined)\n[]\n> arr(3.14)\n[ 3.14 ]\n> arr(1/0)\n[ Infinity ]\n> gen = function*() { yield 1; yield 2; yield 3; }\n[Function: gen]\n> arr(gen())\n[ 1, 2, 3 ]\n> arr([4,5,6])\n[ 4, 5, 6 ]\n> arr(\"foo\")\n[ 'foo' ]\n
\n\n

N.B. strings will be converted into an array with a single element instead of an array of chars. Delete the isString check if you would prefer it the other way around.

\n\n

I've used Array.isArray here because it's the most robust and also simplest.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f2", + "creator": "Alireza", + "createdAt": 1482844630000, + "text": "

Imagine you have this array below:

\n
var arr = [1,2,3,4,5];\n
\n

JavaScript (new and older browsers):

\n
function isArray(arr) {\n  return arr.constructor.toString().indexOf("Array") > -1;\n}\n
\n

or

\n
function isArray(arr) {\n  return arr instanceof Array;\n}\n
\n

or

\n
function isArray(arr) {\n  return Object.prototype.toString.call(arr) === '[object Array]';\n}\n
\n

Then call it like this:

\n
isArray(arr);\n
\n

JavaScript (Internet Explorer 9+, Chrome 5+, Firefox 4+, Safari 5+, and Opera 10.5+)

\n
Array.isArray(arr);\n
\n

jQuery:

\n
$.isArray(arr);\n
\n

Angular:

\n
angular.isArray(arr);\n
\n

Underscore.js and Lodash:

\n
_.isArray(arr);\n
\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f3", + "creator": "Sheelpriy", + "createdAt": 1488360355000, + "text": "

The easiest and fastest way to check if an Object is an Array or not.

\n
var arr = [];\narr.constructor.name === 'Array'  // Returns true;\n
\n

or

\n
arr.constructor === Array // Returns true;\n
\n

Or you can make a utility function:

\n
const isArray = (obj) => !!obj && obj.constructor === Array;\n
\n

Usage:

\n
isArray(arr); // Returns true\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f4", + "creator": "lalithkumar", + "createdAt": 1496663575000, + "text": "

You can find with push like below:

\n

\r\n
\r\n
function isArray(obj){\n   return (typeof obj.push === 'function') ? true : false;\n}\n\nvar array = new Array();\n\nor\n\nvar array = ['a', 'b', 'c'];\nconsole.log(isArray(array));
\r\n
\r\n
\r\n

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f6", + "creator": "kolyaseg", + "createdAt": 1505468467000, + "text": "

In your case you may use concat method of Array which can accept single objects as well as array (and even combined):

\n\n
function myFunc(stringOrArray)\n{\n  var arr = [].concat(stringOrArray);\n\n  console.log(arr);\n\n  arr.forEach(function(item, i)\n  {\n    console.log(i, \"=\", item);\n  })\n}\n\nmyFunc(\"one string\");\n\nmyFunc([\"one string\", \"second\", \"third\"]);\n
\n\n

concat seems to be one of the oldest methods of Array (even IE 5.5 knows it well).

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f5", + "creator": "Vikash Kumar", + "createdAt": 1496926095000, + "text": "

There's just one line solution for this question

\n\n
x instanceof Array\n
\n\n

where x is the variable it will return true if x is an array and false if it is not.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f8", + "creator": "Ric Flair", + "createdAt": 1525547254000, + "text": "

Here's what I use:

\n\n
function isArray(input) {\n  if (input instanceof Array || Object.prototype.toString.call(input) === '[object Array]') {\n        return true;\n  } else return false;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906f7", + "creator": "STEEL", + "createdAt": 1519838466000, + "text": "

You could use the isArray method, but I would prefer to check with:

\n

Object.getPrototypeOf(yourvariable) === Array.prototype

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32594082fcc3049e91cf2", + "creator": "mpen", + "createdAt": 1519839384000, + "text": "Why would you prefer this?", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 123, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cf3", + "creator": "STEEL", + "createdAt": 1519900351000, + "text": "@mpen Object.getPrototypeOf(yourvariable) it returns prototype of an Array object. And The code is fastest and safe to rely on.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906f9", + "creator": "Partha Sarathi Nanda", + "createdAt": 1538027140000, + "text": "

You can also check with array's length property. When you will try to access the length property of an array, it will return a number (0 for an empty array) while if you try to access the length property of object it will return undefined.

\n
if(Object.prototype.toString.call(arrayList) === '[object Array]') {\n  console.log('Array!');\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32594082fcc3049e91cf5", + "creator": "mpen", + "createdAt": 1538029247000, + "text": "Your paragraph and code example don't align. Also, objects can have a .length property.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cf6", + "creator": "Partha Sarathi Nanda", + "createdAt": 1538124887000, + "text": "You can't check object length. Can u give me a example of object's length property", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cf8", + "creator": "mpen", + "createdAt": 1538153872000, + "text": "{length:5} boom. an object with a length property.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906fa", + "creator": "hygull", + "createdAt": 1539325905000, + "text": "

Other methods also exist to check, but I prefer the following method as my best way to check (as you can easily check types of other objects).

\n
> a = [1, 2]\n[ 1, 2 ]\n>\n> Object.prototype.toString.call(a).slice(8,).replace(/\\]$/, '')\n'Array'\n>\n> Object.prototype.toString.call([]).slice(8,-1) // best approach\n'Array'\n
\n

Explanation (with simple examples on Node REPL)»

\n
> o = {'ok': 1}\n{ ok: 1 }\n> a = [1, 2]\n[ 1, 2 ]\n> typeof o\n'object'\n> typeof a\n'object'\n>\n> Object.prototype.toString.call(o)\n'[object Object]'\n> Object.prototype.toString.call(a)\n'[object Array]'\n>\n
\n

Object or Array »

\n
> Object.prototype.toString.call(o).slice(8,).replace(/\\]$/, '')\n'Object'\n>\n> Object.prototype.toString.call(a).slice(8,).replace(/\\]$/, '')\n'Array'\n>\n
\n

Null or Undefined »

\n
> Object.prototype.toString.call(undefined).slice(8,).replace(/\\]$/, '')\n'Undefined'\n> Object.prototype.toString.call(null).slice(8,).replace(/\\]$/, '')\n'Null'\n>\n
\n

String »

\n
> Object.prototype.toString.call('ok').slice(8,).replace(/\\]$/, '')\n'String'\n
\n

Number »

\n
> Object.prototype.toString.call(19).slice(8,).replace(/\\]$/, '')\n'Number'\n> Object.prototype.toString.call(19.0).slice(8,).replace(/\\]$/, '')\n'Number'\n> Object.prototype.toString.call(19.7).slice(8,).replace(/\\]$/, '')\n'Number'\n>\n
\n

I appreciate @mpen's suggestion to use -1 in place of regular expression as follows.

\n
> Object.prototype.toString.call(12).slice(8,-1)\n'Number'\n>\n> Object.prototype.toString.call(12.0).slice(8,-1)\n'Number'\n>\n> Object.prototype.toString.call([]).slice(8,-1)\n'Array'\n> Object.prototype.toString.call({}).slice(8,-1)\n'Object'\n>\n> Object.prototype.toString.call('').slice(8,-1)\n'String'\n>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32594082fcc3049e91cf9", + "creator": "mpen", + "createdAt": 1539361489000, + "text": "You might as well use -1 as the 2nd arg to slice and save the regex for a rainy day.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cfa", + "creator": "hygull", + "createdAt": 1539495784000, + "text": "Thanks @mpen. I have added your suggestions.", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906fb", + "creator": "bytefish", + "createdAt": 1544685406000, + "text": "

There is a difference between checking out its prototype and Array.isArray:

\n
function isArray(obj){\n    return Object.getPrototypeOf(obj) === Array.prototype\n}\n
\n

This function will directly check if an obj is an array.

\n

But for this Proxy object:

\n
var arr = [1,2,3]\n\nvar proxy = new Proxy(arr,{})\n\nconsole.log(Array.isArray(proxy)) // true\n
\n

Array.isArray will take it as Array.

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906fc", + "creator": "underthecode", + "createdAt": 1549308559000, + "text": "

Here's a code snippet that'll explain an important fact of arrays that should be known early on while learning JavaScript (unlike me).

\n

\r\n
\r\n
// this functions puts a string inside an array\nvar stringInsideArray = function(input) {\n  if (typeof input === 'string') {\n    return [input];\n  }\n  else if (Array.isArray(input)) {\n    return input;\n  }\n  else {\n    throw new Error(\"Input is not a string!\");\n  }\n}\n\nvar output = stringInsideArray('hello');\nconsole.log('step one output: ', output); // [\"hello\"]\n\n// use typeof method to verify output is an object\nconsole.log('step two output: ', typeof output); // object\n\n// use Array.isArray() method to verify output is an array\nconsole.log('step three output: ', Array.isArray(output)); // true
\r\n
\r\n
\r\n

\n

Arrays, are in fact, objects.

\n

Using the typeof operator, the output of stringInsideArray('hello') proves that ["hello"] is really an object. This baffled me for the longest time because I assumed that arrays would be a JavaScript data type...

\n

There are only seven JavaScript data types and arrays are not one of them.

\n

To answer your question, using the Array.isArray() method determines that the output is an array.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32594082fcc3049e91cfb", + "creator": "mpen", + "createdAt": 1549425859000, + "text": "Just FYI, [].concat(string) is kind of a weird way of writing [string].", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cfc", + "creator": "underthecode", + "createdAt": 1549435117000, + "text": "@mpen thanks for letting me know. out of curiosity, how would you write this?", + "upvotes": 279, + "upvoterUsernames": [], + "downvotes": 279, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cfe", + "creator": "underthecode", + "createdAt": 1549457372000, + "text": "@mpen your solution makes a lot more sense. updating my answer accordingly. thanks!", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91cff", + "creator": "underthecode", + "createdAt": 1549519284000, + "text": "when you say Error, something like this for the else statement? else { throw "Input is not a string!"; }", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [] + }, + { + "_id": "62f32594082fcc3049e91d00", + "creator": "mpen", + "createdAt": 1549525764000, + "text": "Yes, but you shouldn't throw bare strings. Try throw new Error("Input is not a string!") instead.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906fe", + "creator": "Luis felipe De jesus Munoz", + "createdAt": 1556024480000, + "text": "

I found the shortest answer now:

\n

\r\n
\r\n
var x = [1,2,3]\nconsole.log(x.map?1:0)
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32594082fcc3049e91d01", + "creator": "Sapphire_Brick", + "createdAt": 1581609367000, + "text": "why ? 1 : 0 if you're going for the shortest answer, why not !!x.map or even just x.map?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906fd", + "creator": "Atishay Jain", + "createdAt": 1549743134000, + "text": "

The best practice is to compare it using constructor, something like this

\n
if(some_variable.constructor === Array){\n  // do something\n}\n
\n

You can use other methods too, like typeOf, converting it to a string and then comparing, but comparing it with dataType is always a better approach.

\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90702", + "creator": "Alauddin Afif Cassandra", + "createdAt": 1577694777000, + "text": "
var a = [], b = {};\n\nconsole.log(a.constructor.name == \"Array\");\nconsole.log(b.constructor.name == \"Object\");\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32595082fcc3049e91d04", + "creator": "giraffesyo", + "createdAt": 1577725936000, + "text": "Hi, thanks for your answer. Code only answers tend to be overlooked, would you mind adding some explanation to your code?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90701", + "creator": "Robby_rob", + "createdAt": 1570121249000, + "text": "

Although there's some solid answers I would prefer a functional approach using a functor. A functor is just a fancy way to say that we will be passing a function to a value. (The suggestions that I've seen are passing values to a function.)

\n

Create a TypeOf helper

\n
const TypeOf = obj => Object.prototype.toString.call(obj).slice(8,-1);\n
\n

This is similar to typeof, but it now returns Array for [] and Object for {}. I like to think of it as a strict typeof. If you're working on the Gmail application and performance is a concern then you can do something like this.

\n
const TypeOf = obj => (\n  Array.isArray(obj)\n   ? "array"\n    : obj === null // catch null edge case.  typeof null is an object :)\n   ? null\n    : typeof obj\n)\n
\n

You could stop here and call it a day. However, you could make it a bit more powerful using composition. You get a lot of benefits if you created a TypeBox Functor, again fancy word for passing a function to a value instead of passing a value to a function.

\n

Create TypeBox

\n
const TypeBox = (predicate, defaultValue) => {\n  const TypePredicate = value => ({\n     value,\n     map: cb => predicate(value)\n                ? TypePredicate(cb(value))\n                : TypePredicate(defaultValue)\n  });\n  return TypePredicate;\n}\n
\n

There's a lot going on here, but it's very powerful. The TypeBox function uses a closure and returns our Functor. Closures give you access to Lexical_Scope. Think of it as a backpack that holds the things you want access to later.

\n

Create ArrayBox

\n
const ArrayBox = TypeOf(obj => TypeOf(obj) === 'Array' ? obj : [obj]);\n
\n

ArrayBox is passing our predicate and defaultValue to TypeOf and will be available when we invoke/execute ArrayBox(name it whatever makes sense for your use case).

\n

Now the fun part

\n

If the input is an Array, return it.

\n
ArrayBox(["foo", "bar"]).value; // ['foo', 'bar']\n
\n

If the input is not an array, return it in one

\n
ArrayBox("foo").value // ["foo"]\n
\n

What's great about this approach is that it scales, is easy to test, and it uses composition. You can compose the functions in any manner to get the desired result.

\n

There's many other ways we could approach this using Either or monads.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90704", + "creator": "Dan Bray", + "createdAt": 1603589300000, + "text": "

Array.isArray(obj) does not give very helpful results. I've created a prototype method of Object that seems to correctly determine whether and object is an array or not.

\n

The only edge case that I know of where it fails is when item in the array is set to undefined.

\n
Object.prototype.isArrayLike = function()\n{\n    var length = this.length || Object.keys(this).length;\n    if (length === 0 || this.constructor.name === "String")\n        return false;\n    for (i = 0; i < length; i++)\n    {\n        if (typeof this[i] === "undefined")\n            return false;\n    }\n    return true;\n};\n\nvar arr = ['aaa', 'bbb', 'ccc', 'ddd'];\nvar arr1 = {"0":'aaa', "1":'bbb', 2:'ccc', 3:'ddd'};\nvar arr2 = {"0":'aaa', "a":'bbb', 2:'ccc', 3:'ddd'};\nvar arr3 = "qwerty";\nvar arr4 = [];\nvar arr5 = {0:'aaa', 1:'bbb', 2:'ccc', 3:'ddd'};\n\nconsole.log("arrayLike:" + arr.isArrayLike());\nconsole.log("Array.isArray(arr):" + Array.isArray(arr));\n// arrayLike: true\n// Array.isArray(arr): true\nconsole.log("arrayLike1:" + arr1.isArrayLike());\nconsole.log("Array.isArray(arr1):" + Array.isArray(arr1));\n// arrayLike1: true\n// Array.isArray(arr1): false\nconsole.log("arrayLike2:" + arr2.isArrayLike());\nconsole.log("Array.isArray(arr2):" + Array.isArray(arr2));\n// arrayLike2: false\n// Array.isArray(arr2): false\nconsole.log("arrayLike3:" + arr3.isArrayLike());\nconsole.log("Array.isArray(arr3):" + Array.isArray(arr3));\n// arrayLike3: false\n// Array.isArray(arr3): false\nconsole.log("arrayLike4:" + arr4.isArrayLike());\nconsole.log("Array.isArray(arr4):" + Array.isArray(arr4));\n// arrayLike4: false\n// Array.isArray(arr4): true\nconsole.log("arrayLike5:" + arr5.isArrayLike());\nconsole.log("Array.isArray(arr5):" + Array.isArray(arr5));\n// arrayLike5: false\n// Array.isArray(arr5): true\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90703", + "creator": "Kamil Kiełczewski", + "createdAt": 1591799377000, + "text": "

Exotic one

\n

You want to check if the parameter is a string or not - so try

\n
x===x+''\n
\n

\r\n
\r\n
let isStr = x=> x===x+'';\n\nconsole.log( isStr([]) );\nconsole.log( isStr([\"aa\",\"bb\"]) );\nconsole.log( isStr(\"\") );\nconsole.log( isStr(\"abc\") );
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32595082fcc3049e91d08", + "creator": "3xCh1_23", + "createdAt": 1608913192000, + "text": "Cool, so if not isStr does not mean is Array... nice play with a syntax however.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906ff", + "creator": "bijayshrestha", + "createdAt": 1563215939000, + "text": "

Array.isArray is the way to go about this. For example:

\n\n
var arr = ['tuna', 'chicken', 'pb&j'];\nvar obj = {sandwich: 'tuna', chips: 'cape cod'};\n\n// Returns true\nArray.isArray(arr);\n\n// Return false\nArray.isArray(obj);\n\n
\n", + "upvotes": 439, + "upvoterUsernames": [], + "downvotes": 439, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e90700", + "creator": "Souvik Dey", + "createdAt": 1564319173000, + "text": "

First you can check console.log(typeof Object).

\n

If the output is object then\nvar {data}=object, i.e., just destructure the object according to the object keys.

\n

And the function can be like this:

\n
const abc = (str1, str2=null) => {\n    var result = [];\n    result.push(str1);\n    result.push(str2);\n    return result.join("");\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d42", + "creator": "Souvik Dey", + "createdAt": 1564557541000, + "text": "Yeah you are right.i meant to say that console.log(typeof variableName) to get the type of the variable.", + "upvotes": 71, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e90705", + "creator": "Force Bolt", + "createdAt": 1620049417000, + "text": "

// In simple ways

\n
const arr = [1, 2, 3];\nconst obj = { message: 'nice' };\nconst str = 'nice';\nconst empty = null;\n\nconsole.log(Array.isArray(arr));\nconsole.log(Array.isArray(obj));\nconsole.log(Array.isArray(str));\nconsole.log(Array.isArray(empty));\n
\n", + "upvotes": 688, + "upvoterUsernames": [], + "downvotes": 688, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32596082fcc3049e91d44", + "creator": "Peter Mortensen", + "createdAt": 1628802769000, + "text": "Isn't Array.isArray covered by previous answers?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c3082fcc3049e906d5", + "creator": "ChaosPandion", + "createdAt": 1295808946000, + "text": "

I would first check if your implementation supports isArray:

\n\n
if (Array.isArray)\n    return Array.isArray(v);\n
\n\n

You could also try using the instanceof operator

\n\n
v instanceof Array\n
\n", + "upvotes": 2112, + "upvoterUsernames": [], + "downvotes": 797, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c3082fcc3049e906d4", + "creator": "user113716", + "createdAt": 1295808898000, + "text": "

The method given in the ECMAScript standard to find the class of Object is to use the toString method from Object.prototype.

\n
if(Object.prototype.toString.call(someVar) === '[object Array]') {\n    alert('Array!');\n}\n
\n

Or you could use typeof to test if it is a string:

\n
if(typeof someVar === 'string') {\n    someVar = [someVar];\n}\n
\n

Or if you're not concerned about performance, you could just do a concat to a new empty Array.

\n
someVar = [].concat(someVar);\n
\n

There's also the constructor which you can query directly:

\n
if (somevar.constructor.name == "Array") {\n    // do something\n}\n
\n
\n

Check out a thorough treatment from T.J. Crowder's blog, as posted in his comment below.

\n

Check out this benchmark to get an idea which method performs better: http://jsben.ch/#/QgYAV

\n

From @Bharath, convert a string to an array using ES6 for the question asked:

\n
const convertStringToArray = (object) => {\n   return (typeof object === 'string') ? Array(object) : object\n}\n
\n

Suppose:

\n
let m = 'bla'\nlet n = ['bla','Meow']\nlet y = convertStringToArray(m)\nlet z = convertStringToArray(n)\nconsole.log('check y: '+JSON.stringify(y)) . // check y: ['bla']\nconsole.log('check y: '+JSON.stringify(z)) . // check y: ['bla','Meow']\n
\n", + "upvotes": 3018, + "upvoterUsernames": [], + "downvotes": 991, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c0082fcc3049e92e4a", + "creator": "mcfedr", + "createdAt": 1447372423000, + "text": "Live in the modern world - Array.isArray(obj)", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 8, + "commentItems": [ + { + "_id": "62f321c2082fcc3049e906a1", + "creator": "Neta", + "createdAt": 1448318262000, + "text": "TL;DR - arr.constructor === Array is fastest.", + "upvotes": 208, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a2", + "creator": "mpen", + "createdAt": 1450809521000, + "text": "@vsync There's a hyperlink. You guys can run it as much as you like. The screenshot is there just for people that are too lazy to click.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a3", + "creator": "EscapeNetscape", + "createdAt": 1477330499000, + "text": "jsben.ch/#/QgYAV - a benchmark for the most common ways", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a4", + "creator": "Déjà vu", + "createdAt": 1479282145000, + "text": "@Neta and what about arr instanceof Array?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a5", + "creator": "mpen", + "createdAt": 1479318563000, + "text": "@EscapeNetscape Your prototype test doesn't actually test anything.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a6", + "creator": "Rodrigo Leite", + "createdAt": 1503928216000, + "text": "@sheelpriy > [].constructor === Array // true", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a7", + "creator": "FiniteLooper", + "createdAt": 1516373417000, + "text": "how about just typeof x === "string" to see if it's a string, and if not you can assume it's an array for your use case", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c2082fcc3049e906a8", + "creator": "Alwin Kesler", + "createdAt": 1574802730000, + "text": "Watch out @Neta arr.constructor approach. Some considerations in this post", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1930475, + "uvac": 1930525 + } + }, + { + "_id": "62f321bb082fcc3049e8feda", + "title": "pretty-print JSON using JavaScript", + "title-lowercase": "pretty-print json using javascript", + "creator": "Mark", + "createdAt": 1296081233000, + "status": "open", + "text": "

How can I display JSON in an easy-to-read (for human readers) format? I'm looking primarily for indentation and whitespace, with perhaps even colors / font-styles / etc.

\n", + "upvotes": 5369, + "upvoterUsernames": [], + "downvotes": 2257, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1517073, + "answers": 27, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e908b8", + "creator": "mythz", + "createdAt": 1296081451000, + "text": "

I use the JSONView Chrome extension (it is as pretty as it gets :):

\n\n

Edit: added jsonreport.js

\n\n

I've also released an online stand-alone JSON pretty print viewer, jsonreport.js, that provides a human readable HTML5 report you can use to view any JSON data.

\n\n

You can read more about the format in New JavaScript HTML5 Report Format.

\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908b9", + "creator": "zcopley", + "createdAt": 1298092673000, + "text": "

Douglas Crockford's JSON in JavaScript library will pretty print JSON via the stringify method.

\n\n

You may also find the answers to this older question useful: How can I pretty-print JSON in (unix) shell script?

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908bb", + "creator": "gavenkoa", + "createdAt": 1357827086000, + "text": "

For debugging purpose I use:

\n\n
\nconsole.debug(\"%o\", data);\n
\n\n\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908bc", + "creator": "Rick Hanlon II", + "createdAt": 1371846914000, + "text": "

User Pumbaa80's answer is great if you have an object you want pretty printed. If you're starting from a valid JSON string that you want to pretty printed, you need to convert it to an object first:

\n\n
var jsonString = '{\"some\":\"json\"}';\nvar jsonPretty = JSON.stringify(JSON.parse(jsonString),null,2);  \n
\n\n

This builds a JSON object from the string, and then converts it back to a string using JSON stringify's pretty print.

\n", + "upvotes": 556, + "upvoterUsernames": [], + "downvotes": 178, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32637082fcc3049e9206d", + "creator": "Undistraction", + "createdAt": 1486742722000, + "text": "Note that when displaying the string you need to wrap it in <pre></pre> tags.", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32637082fcc3049e9206f", + "creator": "Cybernetic", + "createdAt": 1636251101000, + "text": "It seems to only work when using textarea, otherwise the newlines don't come in", + "upvotes": 532, + "upvoterUsernames": [], + "downvotes": 532, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908ba", + "creator": "user123444555621", + "createdAt": 1314529011000, + "text": "

Pretty-printing is implemented natively in JSON.stringify(). The third argument enables pretty printing and sets the spacing to use:

\n\n
var str = JSON.stringify(obj, null, 2); // spacing level = 2\n
\n\n

If you need syntax highlighting, you might use some regex magic like so:

\n\n
function syntaxHighlight(json) {\n    if (typeof json != 'string') {\n         json = JSON.stringify(json, undefined, 2);\n    }\n    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n    return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n        var cls = 'number';\n        if (/^\"/.test(match)) {\n            if (/:$/.test(match)) {\n                cls = 'key';\n            } else {\n                cls = 'string';\n            }\n        } else if (/true|false/.test(match)) {\n            cls = 'boolean';\n        } else if (/null/.test(match)) {\n            cls = 'null';\n        }\n        return '<span class=\"' + cls + '\">' + match + '</span>';\n    });\n}\n
\n\n

See in action here: jsfiddle

\n\n

Or a full snippet provided below:

\n\n

\r\n
\r\n
function output(inp) {\r\n    document.body.appendChild(document.createElement('pre')).innerHTML = inp;\r\n}\r\n\r\nfunction syntaxHighlight(json) {\r\n    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\r\n    return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\r\n        var cls = 'number';\r\n        if (/^\"/.test(match)) {\r\n            if (/:$/.test(match)) {\r\n                cls = 'key';\r\n            } else {\r\n                cls = 'string';\r\n            }\r\n        } else if (/true|false/.test(match)) {\r\n            cls = 'boolean';\r\n        } else if (/null/.test(match)) {\r\n            cls = 'null';\r\n        }\r\n        return '<span class=\"' + cls + '\">' + match + '</span>';\r\n    });\r\n}\r\n\r\nvar obj = {a:1, 'b':'foo', c:[false,'false',null, 'null', {d:{e:1.3e5,f:'1.3e5'}}]};\r\nvar str = JSON.stringify(obj, undefined, 4);\r\n\r\noutput(str);\r\noutput(syntaxHighlight(str));
\r\n
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }\r\n.string { color: green; }\r\n.number { color: darkorange; }\r\n.boolean { color: blue; }\r\n.null { color: magenta; }\r\n.key { color: red; }
\r\n
\r\n
\r\n

\n", + "upvotes": 12459, + "upvoterUsernames": [], + "downvotes": 6167, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32637082fcc3049e92071", + "creator": "NoBugs", + "createdAt": 1370324236000, + "text": "Nice. Don't forget it needs css and a <pre>, though.", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32637082fcc3049e92073", + "creator": "minigeek", + "createdAt": 1625309227000, + "text": "i built a tool on this sardapv.github.io/json-prettier :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908bd", + "creator": "Milen Boev", + "createdAt": 1391001379000, + "text": "

Based on Pumbaa80's answer I have modified the code to use the console.log colours (working on Chrome for sure) and not HTML. Output can be seen inside console. You can edit the _variables inside the function adding some more styling.

\n\n
function JSONstringify(json) {\n    if (typeof json != 'string') {\n        json = JSON.stringify(json, undefined, '\\t');\n    }\n\n    var \n        arr = [],\n        _string = 'color:green',\n        _number = 'color:darkorange',\n        _boolean = 'color:blue',\n        _null = 'color:magenta',\n        _key = 'color:red';\n\n    json = json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n        var style = _number;\n        if (/^\"/.test(match)) {\n            if (/:$/.test(match)) {\n                style = _key;\n            } else {\n                style = _string;\n            }\n        } else if (/true|false/.test(match)) {\n            style = _boolean;\n        } else if (/null/.test(match)) {\n            style = _null;\n        }\n        arr.push(style);\n        arr.push('');\n        return '%c' + match + '%c';\n    });\n\n    arr.unshift(json);\n\n    console.log.apply(console, arr);\n}\n
\n\n

Here is a bookmarklet you can use:

\n\n
javascript:function JSONstringify(json) {if (typeof json != 'string') {json = JSON.stringify(json, undefined, '\\t');}var arr = [],_string = 'color:green',_number = 'color:darkorange',_boolean = 'color:blue',_null = 'color:magenta',_key = 'color:red';json = json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {var style = _number;if (/^\"/.test(match)) {if (/:$/.test(match)) {style = _key;} else {style = _string;}} else if (/true|false/.test(match)) {style = _boolean;} else if (/null/.test(match)) {style = _null;}arr.push(style);arr.push('');return '%c' + match + '%c';});arr.unshift(json);console.log.apply(console, arr);};void(0);\n
\n\n

Usage:

\n\n
var obj = {a:1, 'b':'foo', c:[false,null, {d:{e:1.3e5}}]};\nJSONstringify(obj);\n
\n\n

Edit: I just tried to escape the % symbol with this line, after the variables declaration:

\n\n
json = json.replace(/%/g, '%%');\n
\n\n

But I find out that Chrome is not supporting % escaping in the console. Strange... Maybe this will work in the future.

\n\n

Cheers!

\n\n

\"enter

\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c0", + "creator": "wires", + "createdAt": 1482444864000, + "text": "

This is nice:

\n\n

https://github.com/mafintosh/json-markup from mafintosh

\n\n
const jsonMarkup = require('json-markup')\nconst html = jsonMarkup({hello:'world'})\ndocument.querySelector('#myElem').innerHTML = html\n
\n\n

HTML

\n\n
<link ref=\"stylesheet\" href=\"style.css\">\n<div id=\"myElem></div>\n
\n\n

Example stylesheet can be found here

\n\n
https://raw.githubusercontent.com/mafintosh/json-markup/master/style.css\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908be", + "creator": "Just Jake", + "createdAt": 1400195245000, + "text": "

I ran into an issue today with @Pumbaa80's code. I'm trying to apply JSON syntax highlighting to data that I'm rendering in a Mithril view, so I need to create DOM nodes for everything in the JSON.stringify output.

\n\n

I split the really long regex into its component parts as well.

\n\n
render_json = (data) ->\n  # wraps JSON data in span elements so that syntax highlighting may be\n  # applied. Should be placed in a `whitespace: pre` context\n  if typeof(data) isnt 'string'\n    data = JSON.stringify(data, undefined, 2)\n  unicode =     /\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?/\n  keyword =     /\\b(true|false|null)\\b/\n  whitespace =  /\\s+/\n  punctuation = /[,.}{\\[\\]]/\n  number =      /-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/\n\n  syntax = '(' + [unicode, keyword, whitespace,\n            punctuation, number].map((r) -> r.source).join('|') + ')'\n  parser = new RegExp(syntax, 'g')\n\n  nodes = data.match(parser) ? []\n  select_class = (node) ->\n    if punctuation.test(node)\n      return 'punctuation'\n    if /^\\s+$/.test(node)\n      return 'whitespace'\n    if /^\\\"/.test(node)\n      if /:$/.test(node)\n        return 'key'\n      return 'string'\n\n    if /true|false/.test(node)\n      return 'boolean'\n\n     if /null/.test(node)\n       return 'null'\n     return 'number'\n  return nodes.map (node) ->\n    cls = select_class(node)\n    return Mithril('span', {class: cls}, node)\n
\n\n

Code in context on Github here

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908bf", + "creator": "adius", + "createdAt": 1447494394000, + "text": "

You can use console.dir(), which is a shortcut for console.log(util.inspect()).\n(The only difference is that it bypasses any custom inspect() function defined on an object.)

\n\n

It uses syntax-highlighting, smart indentation, removes quotes from keys and just makes the output as pretty as it gets.

\n\n
const object = JSON.parse(jsonString)\n\nconsole.dir(object, {depth: null, colors: true})\n
\n\n

and for the command line:

\n\n

cat package.json | node -e \"process.stdin.pipe(new stream.Writable({write: chunk => console.dir(JSON.parse(chunk), {depth: null, colors: true})}))\"

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c3", + "creator": "Kellen Stuart", + "createdAt": 1491351206000, + "text": "

If you need this to work in a textarea the accepted solution will not work.

\n\n

<textarea id='textarea'></textarea>

\n\n

$(\"#textarea\").append(formatJSON(JSON.stringify(jsonobject),true));

\n\n
function formatJSON(json,textarea) {\n    var nl;\n    if(textarea) {\n        nl = \"&#13;&#10;\";\n    } else {\n        nl = \"<br>\";\n    }\n    var tab = \"&#160;&#160;&#160;&#160;\";\n    var ret = \"\";\n    var numquotes = 0;\n    var betweenquotes = false;\n    var firstquote = false;\n    for (var i = 0; i < json.length; i++) {\n        var c = json[i];\n        if(c == '\"') {\n            numquotes ++;\n            if((numquotes + 2) % 2 == 1) {\n                betweenquotes = true;\n            } else {\n                betweenquotes = false;\n            }\n            if((numquotes + 3) % 4 == 0) {\n                firstquote = true;\n            } else {\n                firstquote = false;\n            }\n        }\n\n        if(c == '[' && !betweenquotes) {\n            ret += c;\n            ret += nl;\n            continue;\n        }\n        if(c == '{' && !betweenquotes) {\n            ret += tab;\n            ret += c;\n            ret += nl;\n            continue;\n        }\n        if(c == '\"' && firstquote) {\n            ret += tab + tab;\n            ret += c;\n            continue;\n        } else if (c == '\"' && !firstquote) {\n            ret += c;\n            continue;\n        }\n        if(c == ',' && !betweenquotes) {\n            ret += c;\n            ret += nl;\n            continue;\n        }\n        if(c == '}' && !betweenquotes) {\n            ret += nl;\n            ret += tab;\n            ret += c;\n            continue;\n        }\n        if(c == ']' && !betweenquotes) {\n            ret += nl;\n            ret += c;\n            continue;\n        }\n        ret += c;\n    } // i loop\n    return ret;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c2", + "creator": "user5044606", + "createdAt": 1489575838000, + "text": "

It works well:

\n\n
console.table()\n
\n\n

Read more here: https://developer.mozilla.org/pt-BR/docs/Web/API/Console/table

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c1", + "creator": "Adel MANI", + "createdAt": 1488382941000, + "text": "
var jsonObj = {\"streetLabel\": \"Avenue Anatole France\", \"city\": \"Paris 07\",  \"postalCode\": \"75007\", \"countryCode\": \"FRA\",  \"countryLabel\": \"France\" };\n\ndocument.getElementById(\"result-before\").innerHTML = JSON.stringify(jsonObj);\n
\n\n

In case of displaying in HTML, you should to add a balise <pre></pre>

\n\n
document.getElementById(\"result-after\").innerHTML = \"<pre>\"+JSON.stringify(jsonObj,undefined, 2) +\"</pre>\"\n
\n\n

Example:

\n\n

\r\n
\r\n
var jsonObj = {\"streetLabel\": \"Avenue Anatole France\", \"city\": \"Paris 07\",  \"postalCode\": \"75007\", \"countryCode\": \"FRA\",  \"countryLabel\": \"France\" };\r\n\r\ndocument.getElementById(\"result-before\").innerHTML = JSON.stringify(jsonObj);\r\n\r\ndocument.getElementById(\"result-after\").innerHTML = \"<pre>\"+JSON.stringify(jsonObj,undefined, 2) +\"</pre>\"
\r\n
div { float:left; clear:both; margin: 1em 0; }
\r\n
<div id=\"result-before\"></div>\r\n<div id=\"result-after\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c5", + "creator": "chim", + "createdAt": 1511108646000, + "text": "

If you're looking for a nice library to prettify json on a web page...

\n\n

Prism.js is pretty good.

\n\n

http://prismjs.com/

\n\n

I found using JSON.stringify(obj, undefined, 2) to get the indentation, and then using prism to add a theme was a good approach.

\n\n

If you're loading in JSON via an ajax call, then you can run one of Prism's utility methods to prettify

\n\n

For example:

\n\n
Prism.highlightAll()\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c4", + "creator": "Charmie", + "createdAt": 1508581199000, + "text": "

Better way.

\n\n

Prettify JSON Array in Javascript

\n\n
JSON.stringify(jsonobj,null,'\\t')\n
\n", + "upvotes": 159, + "upvoterUsernames": [], + "downvotes": 65, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c6", + "creator": "everlasto", + "createdAt": 1511554290000, + "text": "

Here is how you can print without using native function.

\n\n
function pretty(ob, lvl = 0) {\n\n  let temp = [];\n\n  if(typeof ob === \"object\"){\n    for(let x in ob) {\n      if(ob.hasOwnProperty(x)) {\n        temp.push( getTabs(lvl+1) + x + \":\" + pretty(ob[x], lvl+1) );\n      }\n    }\n    return \"{\\n\"+ temp.join(\",\\n\") +\"\\n\" + getTabs(lvl) + \"}\";\n  }\n  else {\n    return ob;\n  }\n\n}\n\nfunction getTabs(n) {\n  let c = 0, res = \"\";\n  while(c++ < n)\n    res+=\"\\t\";\n  return res;\n}\n\nlet obj = {a: {b: 2}, x: {y: 3}};\nconsole.log(pretty(obj));\n\n/*\n  {\n    a: {\n      b: 2\n    },\n    x: {\n      y: 3\n    }\n  }\n*/\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c8", + "creator": "Brent Bradburn", + "createdAt": 1530676082000, + "text": "

The simplest way to display an object for debugging purposes:

\n\n
console.log(\"data\",data) // lets you unfold the object manually\n
\n\n

If you want to display the object in the DOM, you should consider that it could contain strings that would be interpreted as HTML. Therefore, you need to do some escaping...

\n\n
var s = JSON.stringify(data,null,2) // format\nvar e = new Option(s).innerHTML // escape\ndocument.body.insertAdjacentHTML('beforeend','<pre>'+e+'</pre>') // display\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c7", + "creator": "benshope", + "createdAt": 1526197013000, + "text": "

Here is a simple JSON format/color component written in React:

\n\n
const HighlightedJSON = ({ json }: Object) => {\n  const highlightedJSON = jsonObj =>\n    Object.keys(jsonObj).map(key => {\n      const value = jsonObj[key];\n      let valueType = typeof value;\n      const isSimpleValue =\n        [\"string\", \"number\", \"boolean\"].includes(valueType) || !value;\n      if (isSimpleValue && valueType === \"object\") {\n        valueType = \"null\";\n      }\n      return (\n        <div key={key} className=\"line\">\n          <span className=\"key\">{key}:</span>\n          {isSimpleValue ? (\n            <span className={valueType}>{`${value}`}</span>\n          ) : (\n            highlightedJSON(value)\n          )}\n        </div>\n      );\n    });\n  return <div className=\"json\">{highlightedJSON(json)}</div>;\n};\n
\n\n

See it working in this CodePen:\nhttps://codepen.io/benshope/pen/BxVpjo

\n\n

Hope that helps!

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ca", + "creator": "James Heazlewood", + "createdAt": 1531467334000, + "text": "

Here's user123444555621's awesome HTML one adapted for terminals. Handy for debugging Node scripts:

\n\n
function prettyJ(json) {\n  if (typeof json !== 'string') {\n    json = JSON.stringify(json, undefined, 2);\n  }\n  return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, \n    function (match) {\n      let cls = \"\\x1b[36m\";\n      if (/^\"/.test(match)) {\n        if (/:$/.test(match)) {\n          cls = \"\\x1b[34m\";\n        } else {\n          cls = \"\\x1b[32m\";\n        }\n      } else if (/true|false/.test(match)) {\n        cls = \"\\x1b[35m\"; \n      } else if (/null/.test(match)) {\n        cls = \"\\x1b[31m\";\n      }\n      return cls + match + \"\\x1b[0m\";\n    }\n  );\n}\n
\n\n

Usage:

\n\n
// thing = any json OR string of json\nprettyJ(thing);\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908c9", + "creator": "jenil christo", + "createdAt": 1531367934000, + "text": "

You can use JSON.stringify(your object, null, 2)\nThe second parameter can be used as a replacer function which takes key and Val as parameters.This can be used in case you want to modify something within your JSON object.

\n\n

more reference : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32639082fcc3049e92080", + "creator": "Doomd", + "createdAt": 1607365784000, + "text": "This answer is a duplicate of the top answer with over 5400 votes.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908cb", + "creator": "snovelli", + "createdAt": 1546970715000, + "text": "

Couldn't find any solution that had good syntax highlighting for the console, so here's my 2p

\n\n

Install & Add cli-highlight dependency

\n\n
npm install cli-highlight --save\n
\n\n

Define logjson globally

\n\n
const highlight = require('cli-highlight').highlight\nconsole.logjson = (obj) => console.log(\n                               highlight( JSON.stringify(obj, null, 4), \n                                          { language: 'json', ignoreIllegals: true } ));\n
\n\n

Use

\n\n
console.logjson({foo: \"bar\", someArray: [\"string1\", \"string2\"]});\n
\n\n

\"output\"

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908cc", + "creator": "Louie", + "createdAt": 1564604742000, + "text": "
<!-- here is a complete example pretty print with more space between lines-->\n<!-- be sure to pass a json string not a json object -->\n<!-- use line-height to increase or decrease spacing between json lines -->\n\n<style  type=\"text/css\">\n.preJsonTxt{\n  font-size: 18px;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  line-height: 200%;\n}\n.boxedIn{\n  border: 1px solid black;\n  margin: 20px;\n  padding: 20px;\n}\n</style>\n\n<div class=\"boxedIn\">\n    <h3>Configuration Parameters</h3>\n    <pre id=\"jsonCfgParams\" class=\"preJsonTxt\">{{ cfgParams }}</pre>\n</div>\n\n<script language=\"JavaScript\">\n$( document ).ready(function()\n{\n     $(formatJson);\n\n     <!-- this will do a pretty print on the json cfg params      -->\n     function formatJson() {\n         var element = $(\"#jsonCfgParams\");\n         var obj = JSON.parse(element.text());\n        element.html(JSON.stringify(obj, undefined, 2));\n     }\n});\n</script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ce", + "creator": "Teiem", + "createdAt": 1622626347000, + "text": "

based on @user123444555621, just slightly more modern.

\n
const clsMap = [\n    [/^".*:$/, "key"],\n    [/^"/, "string"],\n    [/true|false/, "boolean"],\n    [/null/, "key"],\n    [/.*/, "number"],\n]\n\nconst syntaxHighlight = obj => JSON.stringify(obj, null, 4)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, match => `<span class="${clsMap.find(([regex]) => regex.test(match))[1]}">${match}</span>`);\n
\n

you can also specify the colors inside js (no CSS needed)

\n
const clsMap = [\n    [/^".*:$/, "red"],\n    [/^"/, "green"],\n    [/true|false/, "blue"],\n    [/null/, "magenta"],\n    [/.*/, "darkorange"],\n]\n\nconst syntaxHighlight = obj => JSON.stringify(obj, null, 4)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, match => `<span style="color:${clsMap.find(([regex]) => regex.test(match))[1]}">${match}</span>`);\n
\n

and a version with less regex

\n
const clsMap = [\n    [match => match.startsWith('"') && match.endsWith(':'), "red"],\n    [match => match.startsWith('"'), "green"],\n    [match => match === "true" || match === "false" , "blue"],\n    [match => match === "null", "magenta"],\n    [() => true, "darkorange"],\n];\n    \nconst syntaxHighlight = obj => JSON.stringify(obj, null, 4)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, match => `<span style="color:${clsMap.find(([fn]) => fn(match))[1]}">${match}</span>`);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908cd", + "creator": "Mahdyfo", + "createdAt": 1573460148000, + "text": "

To highlight and beautify it in HTML using Bootstrap:

\n\n
function prettifyJson(json, prettify) {\n    if (typeof json !== 'string') {\n        if (prettify) {\n            json = JSON.stringify(json, undefined, 4);\n        } else {\n            json = JSON.stringify(json);\n        }\n    }\n    return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g,\n        function(match) {\n            let cls = \"<span>\";\n            if (/^\"/.test(match)) {\n                if (/:$/.test(match)) {\n                    cls = \"<span class='text-danger'>\";\n                } else {\n                    cls = \"<span>\";\n                }\n            } else if (/true|false/.test(match)) {\n                cls = \"<span class='text-primary'>\";\n            } else if (/null/.test(match)) {\n                cls = \"<span class='text-info'>\";\n            }\n            return cls + match + \"</span>\";\n        }\n    );\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d0", + "creator": "fauzimh", + "createdAt": 1643788926000, + "text": "

If you are using ES5, simply call JSON.stringify with:

\n\n
JSON.stringify(anObject, null, '\\t');\n
\n

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908cf", + "creator": "Mustak_Talukder", + "createdAt": 1632229065000, + "text": "

it's for Laravel, Codeigniter\nHtml:\n<pre class="jsonPre"> </pre>

\n

Controller: Return the JSON value from the controller as like as

\n

return json_encode($data, JSON_PRETTY_PRINT);

\n

In script:\n<script> $('.jsonPre').html(result); </script>

\n

result will be

\n

\"result

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908d1", + "creator": "xmoonlight", + "createdAt": 1649016728000, + "text": "

Quick pretty human-readable JSON output in 1 line code (without colors):

\n
document.documentElement.innerHTML='<pre>'+JSON.stringify(obj, null, 2)+'</pre>';\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3263a082fcc3049e92088", + "creator": "avalanche1", + "createdAt": 1655672869000, + "text": "Best out of the box solution :+1:", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908d2", + "creator": "John Slegers", + "createdAt": 1649724290000, + "text": "

I think you're looking for something like this :

\n
JSON.stringify(obj, null, '\\t');\n
\n

This "pretty-prints" your JSON string, using a tab for indentation.

\n

If you prefer to use spaces instead of tabs, you could also use a number for the number of spaces you'd like :

\n
JSON.stringify(obj, null, 2);\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c4082fcc3049e90886", + "creator": "Ryan Walker", + "createdAt": 1522384564000, + "text": "If you're just outputting to html, you can wrap it in a <pre> tag.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1522443, + "uvac": 1522470 + } + }, + { + "_id": "62f321bb082fcc3049e8feec", + "title": "Open a URL in a new tab (and not a new window)", + "title-lowercase": "open a url in a new tab (and not a new window)", + "creator": "Mark", + "createdAt": 1296921133000, + "status": "open", + "text": "

I'm trying to open a URL in a new tab, as opposed to a popup window.

\n\n

I've seen related questions where the responses would look something like:

\n\n
window.open(url,'_blank');\nwindow.open(url);\n
\n\n

But none of them worked for me, the browser still tried to open a popup window.

\n", + "upvotes": 3165, + "upvoterUsernames": [], + "downvotes": 521, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3383252, + "answers": 30, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90b8d", + "creator": "Quentin", + "createdAt": 1296921221000, + "text": "

Nothing an author can do can choose to open in a new tab instead of a new window; it is a user preference. (Note that the default user preference in most browsers is for new tabs, so a trivial test on a browser where that preference hasn't been changed will not demonstrate this.)

\n

CSS3 proposed target-new, but the specification was abandoned.

\n

The reverse is not true; by specifying certain window features for the window in the third argument of window.open(), you can trigger a new window when the preference is for tabs.

\n", + "upvotes": 2061, + "upvoterUsernames": [], + "downvotes": 955, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327a8082fcc3049e924ec", + "creator": "toon81", + "createdAt": 1373528258000, + "text": "@PaulBrown Not if the default browser settings make links open in a new window instead of a new tab.", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 136, + "downvoterUsernames": [] + }, + { + "_id": "62f327a8082fcc3049e924ee", + "creator": "Trevor", + "createdAt": 1396902991000, + "text": "@Luke: If it's a question of how to get this functionality only in your own browser, it's probably a question for the SuperUsers site.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327a8082fcc3049e924ef", + "creator": "Quentin", + "createdAt": 1397485923000, + "text": "@Bondye — The question is asking about new tabs instead of new windows. The fact you get new windows is rather the point.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b8e", + "creator": "Fran Verona", + "createdAt": 1296921310000, + "text": "

I think that you can't control this. If the user had setup their browser to open links in a new window, you can't force this to open links in a new tab.

\n\n

JavaScript open in a new window, not tab

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b8f", + "creator": "arikfr", + "createdAt": 1302015737000, + "text": "

To elaborate Steven Spielberg's answer, I did this in such a case:

\n\n
$('a').click(function() {\n  $(this).attr('target', '_blank');\n});\n
\n\n

This way, just before the browser will follow the link I'm setting the target attribute, so it will make the link open in a new tab or window (depends on user's settings).

\n\n

One line example in jQuery:

\n\n
$('a').attr('target', '_blank').get(0).click();\n// The `.get(0)` must be there to return the actual DOM element.\n// Doing `.click()` on the jQuery object for it did not work.\n
\n\n

This can also be accomplished just using native browser DOM APIs as well:

\n\n
document.querySelector('a').setAttribute('target', '_blank');\ndocument.querySelector('a').click();\n
\n", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b90", + "creator": "Rinto George", + "createdAt": 1341758996000, + "text": "

This is a trick,

\n
function openInNewTab(url) {\n window.open(url, '_blank').focus();\n}\n\n//or just\nwindow.open(url, '_blank').focus();\n
\n

In most cases, this should happen directly in the onclick handler for the link to prevent pop-up blockers, and the default "new window" behavior. You could do it this way, or by adding an event listener to your DOM object.

\n
<div onclick="openInNewTab('www.test.com');">Something To Click On</div>\n
\n

http://www.tutsplanet.com/open-url-new-tab-using-javascript/

\n", + "upvotes": 3524, + "upvoterUsernames": [], + "downvotes": 1210, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a8082fcc3049e924f2", + "creator": "wieczorek1990", + "createdAt": 1611241690000, + "text": "if you want to use it with a tag surrounding anchor tag use onclick="event.preventDefault();" in the anchor tag.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f327a8082fcc3049e924f4", + "creator": "mukuljainx", + "createdAt": 1621780103000, + "text": "If anything needs a change in browser settings, avoid it!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b92", + "creator": "loshMiS", + "createdAt": 1376308734000, + "text": "

The browser will always open the link in a new tab if the link is on the same domain (on the same website). If the link is on some other domain it will open it in a new tab/window, depending on browser settings.

\n\n

So, according to this, we can use:

\n\n
<a class=\"my-link\" href=\"http://www.mywebsite.com\" rel=\"http://www.otherwebsite.com\">new tab</a>\n
\n\n

And add some jQuery code:

\n\n
jQuery(document).ready(function () {\n    jQuery(\".my-link\").on(\"click\",function(){\n        var w = window.open('http://www.mywebsite.com','_blank');\n        w.focus();\n        w.location.href = jQuery(this).attr('rel');\n        return false;\n    });\n});\n
\n\n

So, first open new window on same website with _blank target (it will open it in new tab), and then open your desired website inside that new window.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b91", + "creator": "Venkat Kotra", + "createdAt": 1351689353000, + "text": "

window.open() will not open in a new tab if it is not happening on the actual click event. In the example given the URL is being opened on the actual click event. This will work provided the user has appropriate settings in the browser.

\n\n
<a class=\"link\">Link</a>\n<script  type=\"text/javascript\">\n     $(\"a.link\").on(\"click\",function(){\n         window.open('www.yourdomain.com','_blank');\n     });\n</script>\n
\n\n

Similarly, if you are trying to do an Ajax call within the click function and want to open a window on success, ensure you are doing the Ajax call with the async : false option set.

\n", + "upvotes": 541, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a8082fcc3049e924f7", + "creator": "Tristanisginger", + "createdAt": 1653900081000, + "text": "does one not need an e.preventDefault() ?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b94", + "creator": "karaxuna", + "createdAt": 1382099727000, + "text": "

An interesting fact is that the new tab can not be opened if the action is not invoked by the user (clicking a button or something) or if it is asynchronous, for example, this will NOT open in new tab:

\n\n
$.ajax({\n    url: \"url\",\n    type: \"POST\",\n    success: function() {\n        window.open('url', '_blank');              \n    }\n});\n
\n\n

But this may open in a new tab, depending on browser settings:

\n\n
$.ajax({\n    url: \"url\",\n    type: \"POST\",\n    async: false,\n    success: function() {\n        window.open('url', '_blank');              \n    }\n});\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a9082fcc3049e924fa", + "creator": "Steve Meisner", + "createdAt": 1395927550000, + "text": "This is getting blocked as a popup in Firefox 28, Chrome 34b, and Safari 7.0.2, all stock settings. :(", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b93", + "creator": "Michael", + "createdAt": 1377291655000, + "text": "

If you only want to open the external links (links that go to other sites) then this bit of JavaScript/jQuery works well:

\n\n
$(function(){\n    var hostname = window.location.hostname.replace('www.', '');\n    $('a').each(function(){\n        var link_host = $(this).attr('hostname').replace('www.', '');\n        if (link_host !== hostname) {\n            $(this).attr('target', '_blank');\n        }\n    });\n});\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327a9082fcc3049e924fd", + "creator": "Nuri Akman", + "createdAt": 1417084739000, + "text": "This response is not suitable for this question.BUT, this is very useful think! Thank you for your idea!", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b95", + "creator": "spirinvladimir", + "createdAt": 1382174826000, + "text": "
(function(a){\ndocument.body.appendChild(a);\na.setAttribute('href', location.href);\na.dispatchEvent((function(e){\n    e.initMouseEvent(\"click\", true, true, window, 0, 0, 0, 0, 0, true, false, false, false, 0, null);\n    return e\n}(document.createEvent('MouseEvents'))))}(document.createElement('a')))\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b96", + "creator": "Luke Alderton", + "createdAt": 1383758544000, + "text": "

Or you could just create a link element and click it...

\n\n
var evLink = document.createElement('a');\nevLink.href = 'http://' + strUrl;\nevLink.target = '_blank';\ndocument.body.appendChild(evLink);\nevLink.click();\n// Now delete it\nevLink.parentNode.removeChild(evLink);\n
\n\n

This shouldn't be blocked by any popup blockers... Hopefully.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b98", + "creator": "Victor", + "createdAt": 1397244455000, + "text": "

How about creating an <a> with _blank as target attribute value and the url as href, with style display:hidden with a a children element? Then add to the DOM and then trigger the click event on a children element.

\n\n

UPDATE

\n\n

That doesn't work. The browser prevents the default behaviour. It could be triggered programmatically, but it doesn't follow the default behaviour.

\n\n

Check and see for yourself: http://jsfiddle.net/4S4ET/

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b97", + "creator": "chipairon", + "createdAt": 1385120767000, + "text": "

This has nothing to do with browser settings if you are trying to open a new tab from a custom function.

\n\n

In this page, open a JavaScript console and type:

\n\n
document.getElementById(\"nav-questions\").setAttribute(\"target\", \"_blank\");\ndocument.getElementById(\"nav-questions\").click();\n
\n\n

And it will try to open a popup regardless of your settings, because the 'click' comes from a custom action.

\n\n

In order to behave like an actual 'mouse click' on a link, you need to follow @spirinvladimir's advice and really create it:

\n\n
document.getElementById(\"nav-questions\").setAttribute(\"target\", \"_blank\");\ndocument.getElementById(\"nav-questions\").dispatchEvent((function(e){\n  e.initMouseEvent(\"click\", true, true, window, 0, 0, 0, 0, 0,\n                    false, false, false, false, 0, null);\n  return e\n}(document.createEvent('MouseEvents'))));\n
\n\n

Here is a complete example (do not try it on jsFiddle or similar online editors, as it will not let you redirect to external pages from there):

\n\n
<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    #firing_div {\n      margin-top: 15px;\n      width: 250px;\n      border: 1px solid blue;\n      text-align: center;\n    }\n  </style>\n</head>\n<body>\n  <a id=\"my_link\" href=\"http://www.google.com\"> Go to Google </a>\n  <div id=\"firing_div\"> Click me to trigger custom click </div>\n</body>\n<script>\n  function fire_custom_click() {\n    alert(\"firing click!\");\n    document.getElementById(\"my_link\").dispatchEvent((function(e){\n      e.initMouseEvent(\"click\", true, true, window, /* type, canBubble, cancelable, view */\n            0, 0, 0, 0, 0,              /* detail, screenX, screenY, clientX, clientY */\n            false, false, false, false, /* ctrlKey, altKey, shiftKey, metaKey */\n            0, null);                   /* button, relatedTarget */\n      return e\n    }(document.createEvent('MouseEvents'))));\n  }\n  document.getElementById(\"firing_div\").onclick = fire_custom_click;\n</script>\n</html>\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b9a", + "creator": "Kunal", + "createdAt": 1411176456000, + "text": "

This might be a hack, but in Firefox if you specify a third parameter, 'fullscreen=yes', it opens a fresh new window.

\n\n

For example,

\n\n
<a href=\"#\" onclick=\"window.open('MyPDF.pdf', '_blank', 'fullscreen=yes'); return false;\">MyPDF</a>\n
\n\n

It seems to actually override the browser settings.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b99", + "creator": "vernonner3voltazim", + "createdAt": 1403451234000, + "text": "

I'm going to agree somewhat with the person who wrote (paraphrased here): \"For a link in an existing web page, the browser will always open the link in a new tab if the new page is part of the same web site as the existing web page.\" For me, at least, this \"general rule\" works in Chrome, Firefox, Opera, IE, Safari, SeaMonkey, and Konqueror.

\n\n

Anyway, there is a less complicated way to take advantage of what the other person presented. Assuming we are talking about your own web site (\"thissite.com\" below), where you want to control what the browser does, then, below, you want \"specialpage.htm\" to be EMPTY, no HTML at all in it (saves time sending data from the server!).

\n\n
 var wnd, URL;  //global variables\n\n //specifying \"_blank\" in window.open() is SUPPOSED to keep the new page from replacing the existing page\n wnd = window.open(\"http://www.thissite.com/specialpage.htm\", \"_blank\"); //get reference to just-opened page\n //if the \"general rule\" above is true, a new tab should have been opened.\n URL = \"http://www.someothersite.com/desiredpage.htm\";  //ultimate destination\n setTimeout(gotoURL(),200);  //wait 1/5 of a second; give browser time to create tab/window for empty page\n\n\n function gotoURL()\n { wnd.open(URL, \"_self\");  //replace the blank page, in the tab, with the desired page\n   wnd.focus();             //when browser not set to automatically show newly-opened page, this MAY work\n }\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b9c", + "creator": "MannyC", + "createdAt": 1416302073000, + "text": "

Just omitting the strWindowFeatures parameters will open a new tab, UNLESS the browser setting overrides it (browser settings trumps JavaScript).

\n

New window

\n
var myWin = window.open(strUrl, strWindowName, [strWindowFeatures]);\n
\n

New tab

\n
var myWin = window.open(strUrl, strWindowName);\n
\n

-- or --

\n
var myWin = window.open(strUrl);\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b9b", + "creator": "Smile4ever", + "createdAt": 1411489944000, + "text": "

Opening a new tab from within a Firefox (Mozilla) extension goes like this:

\n\n
gBrowser.selectedTab = gBrowser.addTab(\"http://example.com\");\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b9d", + "creator": "Adam Pietrasiak", + "createdAt": 1423254932000, + "text": "

This creates a virtual a element, gives it target="_blank" so it opens in a new tab, gives it the proper url href and then clicks it.

\n
function openInNewTab(href) {\n  Object.assign(document.createElement('a'), {\n    target: '_blank',\n    rel: 'noopener noreferrer',\n    href: href,\n  }).click();\n}\n
\n

and then you can use it like:

\n
openInNewTab("https://google.com"); \n
\n

Important note:

\n

This must be called during the so-called 'trusted event' callback - eg. during click event (not necessary in callback function directly, but during click action). Otherwise opening a new page will be blocked by the browser

\n

If you call it manually at some random moment (e.g., inside an interval or after server response) - it might be blocked by the browser (which makes sense as it'd be a security risk and might lead to poor user experience)

\n", + "upvotes": 561, + "upvoterUsernames": [], + "downvotes": 247, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327aa082fcc3049e92506", + "creator": "Love Putin", + "createdAt": 1589789591000, + "text": "@shaedrich- How to include a text in the code provided by you which will have the link to the page (specified bu the url)?", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f327aa082fcc3049e92508", + "creator": "Avnish alok", + "createdAt": 1596804211000, + "text": "Auto click worked for me using this solution on Chrome. Thanks.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90b9e", + "creator": "CG_DEV", + "createdAt": 1432742978000, + "text": "

This way is similar to the above solution but implemented differently

\n\n

.social_icon -> some class with CSS

\n\n
 <div class=\"social_icon\" id=\"SOME_ID\" data-url=\"SOME_URL\"></div>\n\n\n $('.social_icon').click(function(){\n\n        var url = $(this).attr('data-url');\n        var win = window.open(url, '_blank');  ///similar to above solution\n        win.focus();\n   });\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90b9f", + "creator": "Mohammed Safeer", + "createdAt": 1432831418000, + "text": "

If you use window.open(url, '_blank'), it will be blocked (popup blocker) on Chrome.

\n\n

Try this:

\n\n
//With JQuery\n\n$('#myButton').click(function () {\n    var redirectWindow = window.open('http://google.com', '_blank');\n    redirectWindow.location;\n});\n
\n\n

With pure JavaScript,

\n\n
document.querySelector('#myButton').onclick = function() {\n    var redirectWindow = window.open('http://google.com', '_blank');\n    redirectWindow.location;\n};\n
\n", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba0", + "creator": "CodeNinja", + "createdAt": 1453302146000, + "text": "

You can use a trick with form:

\n\n
$(function () {\n    $('#btn').click(function () {\n        openNewTab(\"http://stackoverflow.com\")\n        return false;\n    });\n});\n\nfunction openNewTab(link) {\n    var frm = $('<form   method=\"get\" action=\"' + link + '\" target=\"_blank\"></form>')\n    $(\"body\").append(frm);\n    frm.submit().remove();\n}\n
\n\n

jsFiddle demo

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba1", + "creator": "Ezequiel García", + "createdAt": 1457697087000, + "text": "

I use the following and it works very well!

\n\n
window.open(url, '_blank').focus();\n
\n", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba2", + "creator": "Carlos Salazar", + "createdAt": 1509589385000, + "text": "

this work for me, just prevent the event, add the url to an <a> tag then trigger the click event on that tag.

\n\n
Js\n$('.myBtn').on('click', function(event) {\n        event.preventDefault();\n        $(this).attr('href',\"http://someurl.com\");\n        $(this).trigger('click');\n});\nHTML\n<a href=\"#\" class=\"myBtn\" target=\"_blank\">Go</a>\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba3", + "creator": "ALZlper", + "createdAt": 1537558673000, + "text": "

There is an answer to this question and it is not no.

\n

I found an easy work around:

\n

Step 1: Create an invisible link:

\n

<a id="yourId" href="yourlink.html" target="_blank" style="display: none;"></a>

\n

Step 2: Click on that link programmatically:

\n

document.getElementById("yourId").click();

\n

Here you go! Works a charm for me.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba5", + "creator": "Bryan", + "createdAt": 1575571429000, + "text": "

JQuery

\n\n
$('<a />',{'href': url, 'target': '_blank'}).get(0).click();\n
\n\n

JS

\n\n
Object.assign(document.createElement('a'), { target: '_blank', href: 'URL_HERE'}).click();\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ba4", + "creator": "iamandrewluca", + "createdAt": 1564133093000, + "text": "
function openTab(url) {\n  const link = document.createElement('a');\n  link.href = url;\n  link.target = '_blank';\n  document.body.appendChild(link);\n  link.click();\n  link.remove();\n}\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327aa082fcc3049e9250f", + "creator": "Kar.ma", + "createdAt": 1606986768000, + "text": "I think this is the cleanest solution", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ba6", + "creator": "Vaha", + "createdAt": 1577523796000, + "text": "

To open a new tab and stay on the same location, you can open the current page in the new tab, and redirect the old tab to the new url.

\n
let newUrl = 'http://example.com';\nlet currentUrl = window.location.href;    \nwindow.open(currentUrl , '_blank'); // open window with url of current page    \nlocation.href = newUrl; // redirect the previous window to the new url\n
\n

You will be automatically moved by the browser to a new opened tab. It will look like your page is reloaded and you will stay on same page but on a new window

\n

\"enter

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327aa082fcc3049e92511", + "creator": "Saqib Omer", + "createdAt": 1610969070000, + "text": "This will not work in case ad blocker is installed. Current tab will be closed by ad blocker.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ba7", + "creator": "Kamil Kiełczewski", + "createdAt": 1577975582000, + "text": "

The window.open(url) will open url in new browser Tab. Belowe JS alternative to it

\n\n
let a= document.createElement('a');\na.target= '_blank';\na.href= 'https://support.wwf.org.uk/';\na.click(); // we don't need to remove 'a' from DOM because we not add it\n
\n\n

here is working example (stackoverflow snippets not allow to opening new tab)

\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ab082fcc3049e92514", + "creator": "Quentin", + "createdAt": 1580913551000, + "text": "Both the approaches you suggest have been mentioned (and criticized) many times in the previous 56 undeleted answers on this question.", + "upvotes": 224, + "upvoterUsernames": [], + "downvotes": 224, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ba8", + "creator": "attacomsian", + "createdAt": 1582578908000, + "text": "

Whether to open the URL in a new tab or a new window, is actually controlled by the user's browser preferences. There is no way to override it in JavaScript.

\n

window.open() behaves differently depending on how it is being used. If it is called as a direct result of a user action, let us say a button click, it should work fine and open a new tab (or window):

\n
const button = document.querySelector('#openTab');\n\n// add click event listener\nbutton.addEventListener('click', () => {\n    // open a new tab\n    const tab = window.open('https://attacomsian.com', '_blank');\n});\n
\n

However, if you try to open a new tab from an AJAX request callback, the browser will block it as it was not a direct user action.

\n

To bypass the popup blocker and open a new tab from a callback, here is a little hack:

\n
const button = document.querySelector('#openTab');\n\n// add click event listener\nbutton.addEventListener('click', () => {\n\n    // open an empty window\n    const tab = window.open('about:blank');\n\n    // make an API call\n    fetch('/api/validate')\n        .then(res => res.json())\n        .then(json => {\n\n            // TODO: do something with JSON response\n\n            // update the actual URL\n            tab.location = 'https://attacomsian.com';\n            tab.focus();\n        })\n        .catch(err => {\n            // close the empty window\n            tab.close();\n        });\n});\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ab082fcc3049e92515", + "creator": "stevex", + "createdAt": 1603453114000, + "text": "Not sure why this is voted down - this worked for me and seems like a clever way to open a tab in a callback.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ba9", + "creator": "Predrag Davidovic", + "createdAt": 1595313906000, + "text": "

Do not use target="_blank"

\n

Always use specific name for that window in my case meaningfulName, in this case you save processor resource:

\n
button.addEventListener('click', () => {\n    window.open('https://google.com', 'meaningfulName')\n})\n
\n

On this way when you click for example 10 times on button, browser will always re-render it in one new tab, instead of opening it in 10 different tabs which will consume much more resources.

\n

You can read more about this on MDN.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90baa", + "creator": "c z", + "createdAt": 1597320592000, + "text": "

There are lots of answer copies suggesting using "_blank" as the target, however I found this didn't work. As Prakash notes, it is up to the browser. However, you can make certain suggestions to the browser, such as to whether the window should have a location bar.

\n

If you suggest enough "tab-like things" you might get a tab, as per Nico's answer to this more specific question for chrome:

\n
window.open('http://www.stackoverflow.com', '_blank', 'toolbar=yes, location=yes, status=yes, menubar=yes, scrollbars=yes');\n
\n

Disclaimer: This is not a panacea. It is still up to the user and browser. Now at least you've specified one more preference for what you'd like your window to look like.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 5, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90af0", + "creator": "Andrey", + "createdAt": 1296921511000, + "text": "Javascript knows nothing about your browser and tabs vs windows, so it is really up to the browser to decide how to open a new window.", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90af1", + "creator": "Mark", + "createdAt": 1296921519000, + "text": "How can I configure Chrome to display it in a new tab, as opposed to a popup?", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90af2", + "creator": "Mark", + "createdAt": 1296921833000, + "text": "Is there a way I can tell the browser to open a new blank tab, and then control its location ?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90af3", + "creator": "Qtax", + "createdAt": 1375388880000, + "text": "@Sergio, that's the default browser behavior for any link. (For Chrome and Firefox at least.) Has nothing to do with Gmail.", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90af4", + "creator": "Raza Ahmed", + "createdAt": 1377843480000, + "text": "may be due to browser settings, but for me window.open(url); opened it up in new tab of chrome and firefox", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 3386422, + "uvac": 3386452 + } + }, + { + "_id": "62f321bb082fcc3049e8ff03", + "title": "Generate random number between two numbers in JavaScript", + "title-lowercase": "generate random number between two numbers in javascript", + "creator": "Mirgorod", + "createdAt": 1297356082000, + "status": "open", + "text": "

Is there a way to generate a random number in a specified range with JavaScript ?

\n

For example: a specified range from 1 to 6 were the random number could be either 1, 2, 3, 4, 5, or 6.

\n", + "upvotes": 2941, + "upvoterUsernames": [], + "downvotes": 622, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2066996, + "answers": 30, + "answerItems": [ + { + "_id": "62f321cd082fcc3049e90ed7", + "creator": "ryebr3ad", + "createdAt": 1297356410000, + "text": "
var x = 6; // can be any number\nvar rand = Math.floor(Math.random()*x) + 1;\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eda", + "creator": "Petter Thowsen", + "createdAt": 1363814592000, + "text": "

Math is not my strong point, but I've been working on a project where I needed to generate a lot of random numbers between both positive and negative.

\n\n
function randomBetween(min, max) {\n    if (min < 0) {\n        return min + Math.random() * (Math.abs(min)+max);\n    }else {\n        return min + Math.random() * max;\n    }\n}\n
\n\n

E.g

\n\n
randomBetween(-10,15)//or..\nrandomBetween(10,20)//or...\nrandomBetween(-200,-100)\n
\n\n

Of course, you can also add some validation to make sure you don't do this with anything other than numbers. Also make sure that min is always less than or equal to max.

\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ed9", + "creator": "Vishal", + "createdAt": 1320833336000, + "text": "

Other solutions:

\n\n\n\n

Try online

\n", + "upvotes": 173, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32921082fcc3049e92a8c", + "creator": "DiegoDD", + "createdAt": 1370040581000, + "text": "would you mind explaining (or giving references to) the ~~ sintaxis? I haven't seen it before! Elegant solution but hard to understand.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a8e", + "creator": "kbtz", + "createdAt": 1443293358000, + "text": "@edi9999 faster to write, but faster to execute as well?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a90", + "creator": "edi9999", + "createdAt": 1443306190000, + "text": "According to the question I linked to, it seems that is it also faster.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a92", + "creator": "The Witness", + "createdAt": 1489156488000, + "text": "For ~~(Math.random() * 9) + 2 it printed 10.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ed8", + "creator": "Francisc", + "createdAt": 1314609957000, + "text": "

\r\n
\r\n
function randomIntFromInterval(min, max) { // min and max included \n  return Math.floor(Math.random() * (max - min + 1) + min)\n}\n\nconst rndInt = randomIntFromInterval(1, 6)\nconsole.log(rndInt)
\r\n
\r\n
\r\n

\n

What it does "extra" is it allows random intervals that do not start with 1.\nSo you can get a random number from 10 to 15 for example. Flexibility.

\n", + "upvotes": 4917, + "upvoterUsernames": [], + "downvotes": 1941, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32921082fcc3049e92a95", + "creator": "Jason", + "createdAt": 1360115639000, + "text": "this is also great because if someone doesn't include the to arg, the from arg doubles as the max", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a96", + "creator": "Francisc", + "createdAt": 1371822091000, + "text": "Read the above comment. Random is inside [0,1), not [0,1].", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a98", + "creator": "Robin Zimmermann", + "createdAt": 1382906863000, + "text": "Works great if the lower number is 0.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9a", + "creator": "Francisc", + "createdAt": 1382922219000, + "text": "The lower number can be any number, not just 0.", + "upvotes": 578, + "upvoterUsernames": [], + "downvotes": 578, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9b", + "creator": "CMCDragonkai", + "createdAt": 1384906431000, + "text": "When you pass strings into it, it doesn't work. Made the edit.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9c", + "creator": "Francisc", + "createdAt": 1384949657000, + "text": "Yeah, you can just parseInt() from and to. It returns NaN for non numeric values so it's cool.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9d", + "creator": "Francisc", + "createdAt": 1404732835000, + "text": "It's only for integers. It's easy to make it work with floats though.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9e", + "creator": "Francisc", + "createdAt": 1433328465000, + "text": "That's not true, you must have made a mistake while testing this. I just did and it works as it should for 100,000 iterations.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92a9f", + "creator": "Francisc", + "createdAt": 1465402694000, + "text": "You can imagine a min and max or use the language's numeric limits.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aa0", + "creator": "William Jones", + "createdAt": 1536480745000, + "text": "If I use this, however: return Math.floor(Math.random()*(max-min+1))+min, it works.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ed6", + "creator": "khr055", + "createdAt": 1297356325000, + "text": "

Important

\nThe following code works only if the minimum value is `1`. It does not work for minimum values other than `1`.\n

If you wanted to get a random integer between 1 (and only 1) and 6, you would calculate:

\n

\r\n
\r\n
    const rndInt = Math.floor(Math.random() * 6) + 1\n    console.log(rndInt)
\r\n
\r\n
\r\n

\n

Where:

\n\n", + "upvotes": 4493, + "upvoterUsernames": [], + "downvotes": 2034, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32921082fcc3049e92aa2", + "creator": "RaymondMachira", + "createdAt": 1375713489000, + "text": "While this would work, @Mike, it would be best to point out the more generic version as Francisc has it below :-).", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aa4", + "creator": "Rob", + "createdAt": 1385395922000, + "text": "Doesn't work if you want a number between two larger numbers eg. Math.floor(Math.random() * 900) + 700", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aa5", + "creator": "Lion King", + "createdAt": 1387723167000, + "text": "This code not good because, does not work with any number. @Francisc code is the correct.", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aa7", + "creator": "Exlord", + "createdAt": 1400308265000, + "text": "the correct way is Math.floor(Math.random() * (max-min)) + min so in this case it would be Math.floor(Math.random() * 5) + 1", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aa8", + "creator": "user4463826", + "createdAt": 1422739824000, + "text": "Technically, this would create a random number between 7 and one. like @Exlord said, max-min.", + "upvotes": 204, + "upvoterUsernames": [], + "downvotes": 204, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aaa", + "creator": "Islam Attrash", + "createdAt": 1506602252000, + "text": "That's not correct. My bad that I can't cancel my up-vote. Maybe the answer author can update it to something correct?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aac", + "creator": "kupendra", + "createdAt": 1533034003000, + "text": "This doesn't work in this case : Math.floor(Math.random() * 70) + 50, I need number between 50-70.", + "upvotes": 1094, + "upvoterUsernames": [], + "downvotes": 1094, + "downvoterUsernames": [] + }, + { + "_id": "62f32921082fcc3049e92aae", + "creator": "M22", + "createdAt": 1646825935000, + "text": "Math.floor(Math.random() * (max - min + 1)) + min;", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90edd", + "creator": "vladiim", + "createdAt": 1412301125000, + "text": "

Or, in Underscore

\n\n
_.random(min, max)\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90edc", + "creator": "Erdi İzgi", + "createdAt": 1410099294000, + "text": "

I was searching random number generator written in TypeScript and I have written this after reading all of the answers, hope It would work for TypeScript coders.

\n\n
    Rand(min: number, max: number): number {\n        return (Math.random() * (max - min + 1) | 0) + min;\n    }   \n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ede", + "creator": "ElChupacabra", + "createdAt": 1431288848000, + "text": "

I wrote more flexible function which can give you random number but not only integer.

\n\n
function rand(min,max,interval)\n{\n    if (typeof(interval)==='undefined') interval = 1;\n    var r = Math.floor(Math.random()*(max-min+interval)/interval);\n    return r*interval+min;\n}\n\nvar a = rand(0,10); //can be 0, 1, 2 (...) 9, 10\nvar b = rand(4,6,0.1); //can be 4.0, 4.1, 4.2 (...) 5.9, 6.0\n
\n\n

Fixed version.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32922082fcc3049e92ab3", + "creator": "Sebastien", + "createdAt": 1441964753000, + "text": "This is not a good solution as it won't work with zero as min value. See @Lior's answer.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32922082fcc3049e92ab5", + "creator": "Sebastien", + "createdAt": 1442225481000, + "text": "I ran multiple times this function with zero as min value and never obtained zero in the output. Or I'm not lucky enough...", + "upvotes": 920, + "upvoterUsernames": [], + "downvotes": 920, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90edb", + "creator": "Lior Elrom", + "createdAt": 1402446231000, + "text": "

Math.random()

\n

Returns an integer random number between min (included) and max (included):

\n
function randomInteger(min, max) {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n
\n

Or any random number between min (included) and max (not included):

\n
function randomNumber(min, max) {\n  return Math.random() * (max - min) + min;\n}\n
\n

Useful examples (integers):

\n
// 0 -> 10\nMath.floor(Math.random() * 11);\n\n// 1 -> 10\nMath.floor(Math.random() * 10) + 1;\n\n// 5 -> 20\nMath.floor(Math.random() * 16) + 5;\n\n// -10 -> (-2)\nMath.floor(Math.random() * 9) - 10;\n
\n

** And always nice to be reminded (Mozilla):

\n
\n

Math.random() does not provide cryptographically secure random\nnumbers. Do not use them for anything related to security. Use the Web\nCrypto API instead, and more precisely the\nwindow.crypto.getRandomValues() method.

\n
\n", + "upvotes": 673, + "upvoterUsernames": [], + "downvotes": 225, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90edf", + "creator": "Sebastián Lara", + "createdAt": 1462039521000, + "text": "

Example

\n\n

Return a random number between 1 and 10:

\n\n
Math.floor((Math.random() * 10) + 1);\n
\n\n

The result could be:\n 3

\n\n

Try yourself: here

\n\n

--

\n\n

or using lodash / undescore:

\n\n

_.random(min, max)

\n\n

Docs:\n- lodash\n- undescore

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32922082fcc3049e92ab7", + "creator": "Sebastián Lara", + "createdAt": 1533219640000, + "text": "so you need 9 or 10 right? If yes: const randomNumber = Math.floor((Math.random() * 10) + 1) const nineOrTen = randomNumber % 2 === 0 ? 9 : 10", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32922082fcc3049e92ab9", + "creator": "Arun Joseph", + "createdAt": 1646675526000, + "text": "This wont work, try with a example like 100 to 400", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90ee0", + "creator": "Arun Sharma", + "createdAt": 1484459087000, + "text": "

Inspite of many answers and almost same result. I would like to add my answer and explain its working. Because it is important to understand its working rather than copy pasting one line code. Generating random numbers is nothing but simple maths.

\n\n

CODE:

\n\n
function getR(lower, upper) {\n\n  var percent = (Math.random() * 100);\n  // this will return number between 0-99 because Math.random returns decimal number from 0-0.9929292 something like that\n  //now you have a percentage, use it find out the number between your INTERVAL :upper-lower \n  var num = ((percent * (upper - lower) / 100));\n  //num will now have a number that falls in your INTERVAL simple maths\n  num += lower;\n  //add lower to make it fall in your INTERVAL\n  //but num is still in decimal\n  //use Math.floor>downward to its nearest integer you won't get upper value ever\n  //use Math.ceil>upward to its nearest integer upper value is possible\n  //Math.round>to its nearest integer 2.4>2 2.5>3   both lower and upper value possible\n  console.log(Math.floor(num), Math.ceil(num), Math.round(num));\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee2", + "creator": "Faiz Mohamed Haneef", + "createdAt": 1491219932000, + "text": "

TL;DR

\n
function generateRandomInteger(min, max) {\n  return Math.floor(min + Math.random()*(max - min + 1))\n}\n
\n

To get the random number\ngenerateRandomInteger(-20, 20);

\n

EXPLANATION BELOW

\n

integer - A number which is not a fraction; a whole number

\n

We need to get a random number , say X between min and max.\nX, min and max are all integers

\n

i.e\nmin <= X <= max

\n

If we subtract min from the equation, this is equivalent to

\n

0 <= (X - min) <= (max - min)

\n

Now, lets multiply this with a random number r\nwhich is

\n

0 <= (X - min) * r <= (max - min) * r

\n

Now, lets add back min to the equation

\n

min <= min + (X - min) * r <= min + (max - min) * r

\n

For, any given X, the above equation satisfies only when r has range of [0,1] For any other values of r the above equation is unsatisfied.

\n

Learn more about ranges [x,y] or (x,y) here

\n

Our next step is to find a function which always results in a value which has a range of [0,1]

\n

Now, the range of r i.e [0,1] is very similar to Math.random() function in Javascript. Isn't it?

\n
\n

The Math.random() function returns a floating-point, pseudo-random\nnumber in the range [0, 1); that is, from 0 (inclusive) up to but not\nincluding 1 (exclusive)

\n
\n

Random Function using Math.random() 0 <= r < 1

\n

Notice that in Math.random() left bound is inclusive and the right bound is exclusive. This means min + (max - min) * r will evaluate to having a range from [min, max)

\n

To include our right bound i.e [min,max] we increase the right bound by 1 and floor the result.

\n
function generateRandomInteger(min, max) {\n  return Math.floor(min + Math.random()*(max - min + 1))\n}\n
\n

To get the random number

\n

generateRandomInteger(-20, 20);

\n", + "upvotes": 126, + "upvoterUsernames": [], + "downvotes": 46, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee1", + "creator": "Razan Paul", + "createdAt": 1485318786000, + "text": "

jsfiddle: https://jsfiddle.net/cyGwf/477/

\n\n

Random Integer: to get a random integer between min and max, use the following code

\n\n
function getRandomInteger(min, max) {\n  min = Math.ceil(min);\n  max = Math.floor(max);\n  return Math.floor(Math.random() * (max - min)) + min;\n}\n
\n\n

Random Floating Point Number: to get a random floating point number between min and max, use the following code

\n\n
function getRandomFloat(min, max) {\n  return Math.random() * (max - min) + min;\n}\n
\n\n

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee4", + "creator": "Sabbir Ahmed", + "createdAt": 1563281268000, + "text": "

This should work:

\n\n
const getRandomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee3", + "creator": "yonatanmn", + "createdAt": 1550064873000, + "text": "

Adding float with fixed precision version based on the int version in @Francisc's answer:

\n\n
function randomFloatFromInterval (min, max, fractionDigits) {\n  const fractionMultiplier = Math.pow(10, fractionDigits)\n  return Math.round(\n    (Math.random() * (max - min) + min) * fractionMultiplier,\n  ) / fractionMultiplier\n}\n
\n\n

so:

\n\n
randomFloatFromInterval(1,3,4) // => 2.2679, 1.509, 1.8863, 2.9741, ...\n
\n\n

and for int answer

\n\n
randomFloatFromInterval(1,3,0) // => 1, 2, 3\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee6", + "creator": "Kamil Kiełczewski", + "createdAt": 1568627893000, + "text": "

Crypto-strong random integer number in range [a,b] (assumption: a < b )

\n\n

\r\n
\r\n
let rand= (a,b)=> a+(b-a+1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32|0\r\n\r\nconsole.log( rand(1,6) );
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee5", + "creator": "maburdi94", + "createdAt": 1563382831000, + "text": "

I discovered a great new way to do this using ES6 default parameters. It is very nifty since it allows either one argument or two arguments. Here it is:

\n\n
function random(n, b = 0) {\n    return Math.random() * (b-n) + n;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee8", + "creator": "Aaron Plocharczyk", + "createdAt": 1575254347000, + "text": "

This is about nine years late, but randojs.com makes this a simple one-liner:

\n\n
rando(1, 6)\n
\n\n

You just need to add this to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions if needed.

\n\n
<script src=\"https://randojs.com/1.0.0.js\"></script>\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee7", + "creator": "Husky931", + "createdAt": 1569842417000, + "text": "

to return 1-6 like a dice basically,

\n\n
return Math.round(Math.random() * 5 + 1);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ee9", + "creator": "gnrfan", + "createdAt": 1577895538000, + "text": "

This works for me and produces values like Python's random.randint standard library function:

\n\n
\nfunction randint(min, max) {\n   return Math.round((Math.random() * Math.abs(max - min)) + min);\n}\n\nconsole.log(\"Random integer: \" + randint(-5, 5));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eea", + "creator": "AyushKatiyar", + "createdAt": 1581325587000, + "text": "

Try using:

\n\n

\r\n
\r\n
function random(min, max) {\r\n   return Math.round((Math.random() *( Math.abs(max - min))) + min);\r\n}\r\nconsole.log(random(1, 6));
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eeb", + "creator": "Rayron Victor", + "createdAt": 1598477016000, + "text": "

This function can generate a random integer number between (and including) min and max numbers:

\n
function randomNumber(min, max) {\n  if (min > max) {\n    let temp = max;\n    max = min;\n    min = temp;\n  }\n\n  if (min <= 0) {\n    return Math.floor(Math.random() * (max + Math.abs(min) + 1)) + min;\n  } else {\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n  }\n}\n
\n

Example:

\n
randomNumber(-2, 3); // can be -2, -1, 0, 1, 2 and 3\nrandomNumber(-5, -2); // can be -5, -4, -3 and -2\nrandomNumber(0, 4); // can be 0, 1, 2, 3 and 4\nrandomNumber(4, 0); // can be 0, 1, 2, 3 and 4\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32923082fcc3049e92ac3", + "creator": "mesqueeb", + "createdAt": 1600237764000, + "text": "this will never roll 6 if max = 6 and min = 0", + "upvotes": 110, + "upvoterUsernames": [], + "downvotes": 110, + "downvoterUsernames": [] + }, + { + "_id": "62f32923082fcc3049e92ac5", + "creator": "Rayron Victor", + "createdAt": 1600369311000, + "text": "@mesqueeb I edited my answer, as Math.random() will never be 1.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32923082fcc3049e92ac6", + "creator": "Rayron Victor", + "createdAt": 1600441480000, + "text": "I updated my answer to remove ambiguity and add the possibility of negative numbers.", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90eec", + "creator": "Ruthe", + "createdAt": 1606966921000, + "text": "

for big number.

\n
var min_num = 900;\nvar max_num = 1000;\nwhile(true){\n    \n    let num_random = Math.random()* max_num;\n    console.log('input : '+num_random);\n    if(num_random >= min_num){\n        console.log(Math.floor(num_random));\n       break; \n    } else {\n        console.log(':::'+Math.floor(num_random));\n    }\n}\n
\n", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90eed", + "creator": "The Onin", + "createdAt": 1609720804000, + "text": "

ES6 / Arrow functions version based on Francis' code (i.e. the top answer):

\n
const randomIntFromInterval = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32923082fcc3049e92ac8", + "creator": "Victor", + "createdAt": 1621275582000, + "text": "Nice! It is much useful to provide the min and max values. I was looking for this answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90eee", + "creator": "M22", + "createdAt": 1612658089000, + "text": "

The top rated solution is not mathematically correct as same as comments under it -> Math.floor(Math.random() * 6) + 1.

\n

Task: generate random number between 1 and 6.

\n

Math.random() returns floating point number between 0 and 1 (like 0.344717274374 or 0.99341293123 for example), which we will use as a percentage, so Math.floor(Math.random() * 6) + 1 returns some percentage of 6 (max: 5, min: 0) and adds 1. The author got lucky that lower bound was 1., because percentage floor will "maximumly" return 5 which is less than 6 by 1, and that 1 will be added by lower bound 1.

\n

The problems occurs when lower bound is greater than 1. For instance,\nTask: generate random between 2 and 6.

\n

(following author's logic)\nMath.floor(Math.random() * 6) + 2, it is obviously seen that if we get 5 here -> Math.random() * 6 and then add 2, the outcome will be 7 which goes beyond the desired boundary of 6.

\n

Another example,\nTask: generate random between 10 and 12.

\n

(following author's logic)\nMath.floor(Math.random() * 12) + 10, (sorry for repeating) it is obvious that we are getting 0%-99% percent of number "12", which will go way beyond desired boundary of 12.

\n

So, the correct logic is to take the difference between lower bound and upper bound add 1, and only then floor it (to substract 1, because Math.random() returns 0 - 0.99, so no way to get full upper bound, thats why we adding 1 to upper bound to get maximumly 99% of (upper bound + 1) and then we floor it to get rid of excess). Once we got the floored percentage of (difference + 1), we can add lower boundary to get the desired randomed number between 2 numbers.

\n

The logic formula for that will be: Math.floor(Math.random() * ((up_boundary - low_boundary) + 1)) + 10.

\n

P.s.: Even comments under the top-rated answer were incorrect, since people forgot to add 1 to the difference, meaning that they will never get the up boundary (yes it might be a case if they dont want to get it at all, but the requirenment was to include the upper boundary).

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32923082fcc3049e92aca", + "creator": "M22", + "createdAt": 1613583478000, + "text": "Math.floor(Math.random() * ((up_boundary - low_boundary) + 1)) + low_boundary", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cd082fcc3049e90eef", + "creator": "Okiemute Gold", + "createdAt": 1614546168000, + "text": "

Using random function, which can be reused.

\n
function randomNum(min, max) {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\nrandomNum(1, 6);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef1", + "creator": "Gass", + "createdAt": 1636030646000, + "text": "

Get a random integer between 0 and 400

\n

\r\n
\r\n
let rand = Math.round(Math.random() * 400)\n\ndocument.write(rand)
\r\n
\r\n
\r\n

\n

Get a random integer between 200 and 1500

\n

\r\n
\r\n
let range = {min: 200, max: 1500}\nlet delta = range.max - range.min\n\nconst rand = Math.round(range.min + Math.random() * delta)\n\ndocument.write(rand)
\r\n
\r\n
\r\n

\n

Using functions

\n

\r\n
\r\n
function randBetween(min, max){\n  let delta = max - min\n  return Math.round(min + Math.random() * delta)\n}\n\ndocument.write(randBetween(10, 15));
\r\n
\r\n
\r\n

\n

\r\n
\r\n
// JavaScript ES6 arrow function\n\nconst randBetween = (min, max) => {\n  let delta = max - min\n  return Math.round(min + Math.random() * delta)\n}\n\ndocument.write(randBetween(10, 20))
\r\n
\r\n
\r\n

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef0", + "creator": "maxxx", + "createdAt": 1621190608000, + "text": "

If the starting number is 1, as in your example (1-6), you can use Math.ceil() method instead of Math.floor().

\n
Math.ceil(Math.random() * 6)\n
\n

instead of

\n
Math.floor(Math.random() * 6) + 1\n
\n

Let's not forget other useful Math methods.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef3", + "creator": "Muluken Getachew", + "createdAt": 1659099424000, + "text": "

Short Answer: It's achievable using a simple array.

\n

you can alternate within array elements.

\n

This solution works even if your values are not consecutive. Values don't even have to be a number.

\n
let array = [1, 2, 3, 4, 5, 6];\nconst randomValue = array[Math.floor(Math.random() * array.length)];\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cd082fcc3049e90ef2", + "creator": "Marco", + "createdAt": 1643918528000, + "text": "

For Postman:\npm.environment.set("random_number", _.random(11111111111, 99999999999))

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 7, + "commentItems": [ + { + "_id": "62f321cd082fcc3049e90ecf", + "creator": "Amjad Masad", + "createdAt": 1297356305000, + "text": "Math.floor( Math.random() * 7 )", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed0", + "creator": "Amjad Masad", + "createdAt": 1297383683000, + "text": "Sure.. Math.floor(Math.random()*6+1)", + "upvotes": 105, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed1", + "creator": "Dan K.K.", + "createdAt": 1384789385000, + "text": "here is a useful gist: gist.github.com/kerimdzhanov/7529623", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed2", + "creator": "vp_arth", + "createdAt": 1435433382000, + "text": "After Bell inequalities violation, we can be sure, that random exists...", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed3", + "creator": "Zafar Kurbonov", + "createdAt": 1506192725000, + "text": "After Bell inequalities violation, we can be sure, that random exists. agree with you", + "upvotes": 310, + "upvoterUsernames": [], + "downvotes": 310, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed4", + "creator": "T.Todua", + "createdAt": 1510126066000, + "text": "I think you should re-consider the accepted answer.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321cd082fcc3049e90ed5", + "creator": "uɥƃnɐʌuop", + "createdAt": 1612991588000, + "text": "Why is this still not part of core javascript? I think we should band together and demand a dollar for each time we've had to google this.", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2069944, + "uvac": 2069974 + } + }, + { + "_id": "62f321bb082fcc3049e8ff0b", + "title": "How to decide when to use Node.js?", + "title-lowercase": "how to decide when to use node.js?", + "creator": "Legend", + "createdAt": 1298265651000, + "status": "open", + "text": "

I am new to this kind of stuff, but lately I've been hearing a lot about how good Node.js is. Considering how much I love working with jQuery and JavaScript in general, I can't help but wonder how to decide when to use Node.js. The web application I have in mind is something like Bitly - takes some content, archives it.

\n\n

From all the homework I have been doing in the last few days, I obtained the following information. Node.js

\n\n\n\n

Some of the sources that I have come across are:

\n\n\n\n

Considering that Node.js can be run almost out-of-the-box on Amazon's EC2 instances, I am trying to understand what type of problems require Node.js as opposed to any of the mighty kings out there like PHP, Python and Ruby. I understand that it really depends on the expertise one has on a language, but my question falls more into the general category of: When to use a particular framework and what type of problems is it particularly suited for?

\n", + "upvotes": 3989, + "upvoterUsernames": [], + "downvotes": 1797, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 573045, + "answers": 15, + "answerItems": [ + { + "_id": "62f3220b082fcc3049e90f7d", + "creator": "Benson", + "createdAt": 1298266251000, + "text": "

You did a great job of summarizing what's awesome about Node.js. My feeling is that Node.js is especially suited for applications where you'd like to maintain a persistent connection from the browser back to the server. Using a technique known as \"long-polling\", you can write an application that sends updates to the user in real time. Doing long polling on many of the web's giants, like Ruby on Rails or Django, would create immense load on the server, because each active client eats up one server process. This situation amounts to a tarpit attack. When you use something like Node.js, the server has no need of maintaining separate threads for each open connection.

\n\n

This means you can create a browser-based chat application in Node.js that takes almost no system resources to serve a great many clients. Any time you want to do this sort of long-polling, Node.js is a great option.

\n\n

It's worth mentioning that Ruby and Python both have tools to do this sort of thing (eventmachine and twisted, respectively), but that Node.js does it exceptionally well, and from the ground up. JavaScript is exceptionally well situated to a callback-based concurrency model, and it excels here. Also, being able to serialize and deserialize with JSON native to both the client and the server is pretty nifty.

\n\n

I look forward to reading other answers here, this is a fantastic question.

\n\n

It's worth pointing out that Node.js is also great for situations in which you'll be reusing a lot of code across the client/server gap. The Meteor framework makes this really easy, and a lot of folks are suggesting this might be the future of web development. I can say from experience that it's a whole lot of fun to write code in Meteor, and a big part of this is spending less time thinking about how you're going to restructure your data, so the code that runs in the browser can easily manipulate it and pass it back.

\n\n

Here's an article on Pyramid and long-polling, which turns out to be very easy to set up with a little help from gevent: TicTacToe and Long Polling with Pyramid.

\n", + "upvotes": 2228, + "upvoterUsernames": [], + "downvotes": 874, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3296a082fcc3049e92bdf", + "creator": "hitautodestruct", + "createdAt": 1455176803000, + "text": "Why use long polling? What happened to the future and sockets?", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f7e", + "creator": "fisherwebdev", + "createdAt": 1298270611000, + "text": "

I believe Node.js is best suited for real-time applications: online games, collaboration tools, chat rooms, or anything where what one user (or robot? or sensor?) does with the application needs to be seen by other users immediately, without a page refresh.

\n\n

I should also mention that Socket.IO in combination with Node.js will reduce your real-time latency even further than what is possible with long polling. Socket.IO will fall back to long polling as a worst case scenario, and instead use web sockets or even Flash if they are available.

\n\n

But I should also mention that just about any situation where the code might block due to threads can be better addressed with Node.js. Or any situation where you need the application to be event-driven.

\n\n

Also, Ryan Dahl said in a talk that I once attended that the Node.js benchmarks closely rival Nginx for regular old HTTP requests. So if we build with Node.js, we can serve our normal resources quite effectively, and when we need the event-driven stuff, it's ready to handle it.

\n\n

Plus it's all JavaScript all the time. Lingua Franca on the whole stack.

\n", + "upvotes": 669, + "upvoterUsernames": [], + "downvotes": 260, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f7f", + "creator": "stewe", + "createdAt": 1326592084000, + "text": "

To make it short:

\n\n

Node.js is well suited for applications that have a lot of concurrent connections and each request only needs very few CPU cycles, because the event loop (with all the other clients) is blocked during execution of a function.

\n\n

A good article about the event loop in Node.js is Mixu's tech blog: Understanding the node.js event loop.

\n", + "upvotes": 299, + "upvoterUsernames": [], + "downvotes": 93, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f81", + "creator": "shash7", + "createdAt": 1367848344000, + "text": "

My piece: nodejs is great for making real time systems like analytics, chat-apps, apis, ad servers, etc.\nHell, I made my first chat app using nodejs and socket.io under 2 hours and that too during exam \nweek!

\n\n

Edit

\n\n

Its been several years since I have started using nodejs and I have used it in making many different things including static file servers, simple analytics, chat apps and much more.\nThis is my take on when to use nodejs

\n\n

When to use

\n\n

When making system which put emphasis on concurrency and speed.

\n\n\n\n

When not to use

\n\n

Its a very versatile webserver so you can use it wherever you want but probably not these places.

\n\n\n\n

Keep in mind that I am just nitpicking. For static file servers, apache is better mainly because it is widely available. The nodejs community has grown larger and more mature over the years and it is safe to say nodejs can be used just about everywhere if you have your own choice of hosting.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f80", + "creator": "Ajay Tiwari", + "createdAt": 1365182231000, + "text": "

There is nothing like Silver Bullet. Everything comes with some cost associated with it. It is like if you eat oily food, you will compromise your health and healthy food does not come with spices like oily food. It is individual choice whether they want health or spices as in their food.\nSame way Node.js consider to be used in specific scenario. If your app does not fit into that scenario you should not consider it for your app development. I am just putting my thought on the same:

\n\n

When to use Node.JS

\n\n
    \n
  1. If your server side code requires very few cpu cycles. In other world you are doing non blocking operation and does not have heavy algorithm/Job which consumes lots of CPU cycles.
  2. \n
  3. If you are from Javascript back ground and comfortable in writing Single Threaded code just like client side JS.
  4. \n
\n\n

When NOT to use Node.JS

\n\n
    \n
  1. Your server request is dependent on heavy CPU consuming algorithm/Job.
  2. \n
\n\n

Scalability Consideration with Node.JS

\n\n
    \n
  1. Node.JS itself does not utilize all core of underlying system and it is single threaded by default, you have to write logic by your own to utilize multi core processor and make it multi threaded.
  2. \n
\n\n

Node.JS Alternatives

\n\n

There are other option to use in place of Node.JS however Vert.x seems to be pretty promising and has lots of additional features like polygot and better scalability considerations.

\n", + "upvotes": 117, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296a082fcc3049e92be4", + "creator": "Erik Reppen", + "createdAt": 1373076893000, + "text": "It is possible to write and bind C components to Node.js apps.", + "upvotes": 187, + "upvoterUsernames": [], + "downvotes": 187, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f83", + "creator": "BoxerBucks", + "createdAt": 1370540549000, + "text": "

Another great thing that I think no one has mentioned about Node.js is the amazing community, the package management system (npm) and the amount of modules that exist that you can include by simply including them in your package.json file.

\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296a082fcc3049e92be7", + "creator": "joeytwiddle", + "createdAt": 1374270372000, + "text": "And these packages are all relatively fresh, so they have the benefit of hindsight and tend to conform to recent web standards.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f82", + "creator": "Joonas", + "createdAt": 1369982040000, + "text": "

I have one real-world example where I have used Node.js. The company where I work got one client who wanted to have a simple static HTML website. This website is for selling one item using PayPal and the client also wanted to have a counter which shows the amount of sold items. Client expected to have huge amount of visitors to this website. I decided to make the counter using Node.js and the Express.js framework.

\n\n

The Node.js application was simple. Get the sold items amount from a Redis database, increase the counter when item is sold and serve the counter value to users via the API.

\n\n

Some reasons why I chose to use Node.js in this case

\n\n
    \n
  1. It is very lightweight and fast. There has been over 200000 visits on this website in three weeks and minimal server resources has been able to handle it all.
  2. \n
  3. The counter is really easy to make to be real time.
  4. \n
  5. Node.js was easy to configure.
  6. \n
  7. There are lots of modules available for free. For example, I found a Node.js module for PayPal.
  8. \n
\n\n

In this case, Node.js was an awesome choice.

\n", + "upvotes": 190, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f85", + "creator": "I_Debug_Everything", + "createdAt": 1402237637000, + "text": "

One more thing node provides is the ability to create multiple v8 instanes of node using node's child process( childProcess.fork() each requiring 10mb memory as per docs) on the fly, thus not affecting the main process running the server. So offloading a background job that requires huge server load becomes a child's play and we can easily kill them as and when needed.

\n\n

I've been using node a lot and in most of the apps we build, require server connections at the same time thus a heavy network traffic. Frameworks like Express.js and the new Koajs (which removed callback hell) have made working on node even more easier.

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f84", + "creator": "Sean Zhao", + "createdAt": 1380314066000, + "text": "

My one more reason to choose Node.js for a new project is:

\n\n

Be able to do pure cloud based development

\n\n

I have used Cloud9 IDE for a while and now I can't imagine without it, it covers all the development lifecycles. All you need is a browser and you can code anytime anywhere on any devices. You don't need to check in code in one Computer(like at home), then checkout in another computer(like at work place).

\n\n

Of course, there maybe cloud based IDE for other languages or platforms (Cloud 9 IDE is adding supports for other languages as well), but using Cloud 9 to do Node.js developement is really a great experience for me.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296b082fcc3049e92beb", + "creator": "Wanny Miarelli", + "createdAt": 1424803212000, + "text": "Actually Cloud9 IDE and the others ( koding the one i use ) support almost all kind of web language.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296b082fcc3049e92bed", + "creator": "matanster", + "createdAt": 1426709728000, + "text": "Are you serious dude? this is the criteria for choosing a stack? :) happy it's working for you!", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f86", + "creator": "Tony O'Hagan", + "createdAt": 1402579443000, + "text": "

The most important reasons to start your next project using Node ...

\n\n\n\n

What to expect ...

\n\n\n\n

Who uses it?

\n\n\n", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 86, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296b082fcc3049e92bf0", + "creator": "refactor", + "createdAt": 1463660709000, + "text": "nested callbacks can be avoided by using ES6 generators for async code", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220b082fcc3049e90f87", + "creator": "Vinayak Bevinakatti", + "createdAt": 1404371871000, + "text": "

It can be used where

\n\n\n\n

On Mobile front, prime-time companies have relied on Node.js for their mobile solutions. Check out why?

\n\n

LinkedIn is a prominent user. Their entire mobile stack is built on Node.js. They went from running 15 servers with 15 instances on each physical machine, to just 4 instances – that can handle double the traffic!

\n\n

eBay launched ql.io, a web query language for HTTP APIs, which uses Node.js as the runtime stack. They were able to tune a regular developer-quality Ubuntu workstation to handle more than 120,000 active connections per node.js process, with each connection consuming about 2kB memory!

\n\n

Walmart re-engineered its mobile app to use Node.js and pushed its JavaScript processing to the server.

\n\n

Read more at: http://www.pixelatingbits.com/a-closer-look-at-mobile-app-development-with-node-js/

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f89", + "creator": "mbert65", + "createdAt": 1407098666000, + "text": "
    \n
  1. Node is great for quick prototypes but I'd never use it again for anything complex.\nI spent 20 years developing a relationship with a compiler and I sure miss it.

  2. \n
  3. Node is especially painful for maintaining code that you haven't visited for awhile. Type info and compile time error detection are GOOD THINGS. Why throw all that out? For what? And dang, when something does go south the stack traces quite often completely useless.

  4. \n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f88", + "creator": "matanster", + "createdAt": 1406201944000, + "text": "

If your application mainly tethers web apis, or other io channels, give or take a user interface, node.js may be a fair pick for you, especially if you want to squeeze out the most scalability, or, if your main language in life is javascript (or javascript transpilers of sorts). If you build microservices, node.js is also okay. Node.js is also suitable for any project that is small or simple.

\n\n

Its main selling point is it allows front-enders take responsibility for back-end stuff rather than the typical divide. Another justifiable selling point is if your workforce is javascript oriented to begin with.

\n\n

Beyond a certain point however, you cannot scale your code without terrible hacks for forcing modularity, readability and flow control. Some people like those hacks though, especially coming from an event-driven javascript background, they seem familiar or forgivable.

\n\n

In particular, when your application needs to perform synchronous flows, you start bleeding over half-baked solutions that slow you down considerably in terms of your development process. If you have computation intensive parts in your application, tread with caution picking (only) node.js. Maybe http://koajs.com/ or other novelties alleviate those originally thorny aspects, compared to when I originally used node.js or wrote this.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f8a", + "creator": "Anshul", + "createdAt": 1421439047000, + "text": "

Node best for concurrent request handling -

\n\n

So, Let’s start with a story. From last 2 years I am working on JavaScript and developing web front end and I am enjoying it. Back end guys provide’s us some API’s written in Java,python (we don’t care) and we simply write a AJAX call, get our data and guess what ! we are done. But in real it is not that easy, If data we are getting is not correct or there is some server error then we stuck and we have to contact our back end guys over the mail or chat(sometimes on whatsApp too :).) This is not cool. What if we wrote our API’s in JavaScript and call those API’s from our front end ? Yes that’s pretty cool because if we face any problem in API we can look into it. Guess what ! you can do this now , How ? – Node is there for you.

\n\n

Ok agreed that you can write your API in JavaScript but what if I am ok with above problem. Do you have any other reason to use node for rest API ?

\n\n

so here is the magic begins. Yes I do have other reasons to use node for our API’s.

\n\n

Let’s go back to our traditional rest API system which is based on either blocking operation or threading. Suppose two concurrent request occurs( r1 and r2) , each of them require database operation. So In traditional system what will happens :

\n\n

1. Waiting Way : Our server starts serving r1 request and waits for query response. after completion of r1 , server starts to serve r2 and does it in same way. So waiting is not a good idea because we don’t have that much time.

\n\n

2. Threading Way : Our server will creates two threads for both requests r1 and r2 and serve their purpose after querying database so cool its fast.But it is memory consuming because you can see we started two threads also problem increases when both request is querying same data then you have to deal with deadlock kind of issues . So its better than waiting way but still issues are there.

\n\n

Now here is , how node will do it:

\n\n

3. Nodeway : When same concurrent request comes in node then it will register an event with its callback and move ahead it will not wait for query response for a particular request.So when r1 request comes then node’s event loop (yes there is an event loop in node which serves this purpose.) register an event with its callback function and move ahead for serving r2 request and similarly register its event with its callback. Whenever any query finishes it triggers its corresponding event and execute its callback to completion without being interrupted.

\n\n

So no waiting, no threading , no memory consumption – yes this is nodeway for serving rest API.

\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220b082fcc3049e90f8b", + "creator": "BEJGAM SHIVA PRASAD", + "createdAt": 1470754967000, + "text": "

I can share few points where&why to use node js.

\n\n
    \n
  1. For realtime applications like chat,collaborative editing better we go with nodejs as it is event base where fire event and data to clients from server.
  2. \n
  3. Simple and easy to understand as it is javascript base where most of people have idea.
  4. \n
  5. Most of current web applications going towards angular js&backbone, with node it is easy to interact with client side code as both will use json data.
  6. \n
  7. Lot of plugins available.
  8. \n
\n\n

Drawbacks:-

\n\n
    \n
  1. Node will support most of databases but best is mongodb which won't support complex joins and others.
  2. \n
  3. Compilation Errors...developer should handle each and every exceptions other wise if any error accord application will stop working where again we need to go and start it manually or using any automation tool.
  4. \n
\n\n

Conclusion:- \nNodejs best to use for simple and real time applications..if you have very big business logic and complex functionality better should not use nodejs.\nIf you want to build an application along with chat and any collaborative functionality.. node can be used in specific parts and remain should go with your convenience technology.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f3220a082fcc3049e90f58", + "creator": "zzzzBov", + "createdAt": 1471365264000, + "text": "This question is being discussed on meta (meta.stackoverflow.com/q/332386/497418).", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 577035, + "uvac": 577050 + } + }, + { + "_id": "62f321bb082fcc3049e8ff07", + "title": "JavaScript check if variable exists (is defined/initialized)", + "title-lowercase": "javascript check if variable exists (is defined/initialized)", + "creator": "Samuel Liew", + "createdAt": 1298605487000, + "status": "open", + "text": "

Which method of checking if a variable has been initialized is better/correct?\n(Assuming the variable could hold anything (string, int, object, function, etc.))

\n
if (elem) { // or !elem\n
\n

or

\n
if (typeof elem !== 'undefined') {\n
\n

or

\n
if (elem != null) {\n
\n", + "upvotes": 2831, + "upvoterUsernames": [], + "downvotes": 614, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2191935, + "answers": 30, + "answerItems": [ + { + "_id": "62f3220c082fcc3049e9102c", + "creator": "Alan Geleynse", + "createdAt": 1298605599000, + "text": "

It depends if you just care that the variable has been defined or if you want it to have a meaningful value.

\n\n

Checking if the type is undefined will check if the variable has been defined yet.

\n\n

=== null or !== null will only check if the value of the variable is exactly null.

\n\n

== null or != null will check if the value is undefined or null.

\n\n

if(value) will check if the variable is undefined, null, 0, or an empty string.

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9102b", + "creator": "Jim Puls", + "createdAt": 1233896215000, + "text": "

You want the typeof operator. Specifically:

\n
if (typeof variable !== 'undefined') {\n    // the variable is defined\n}\n
\n", + "upvotes": 5682, + "upvoterUsernames": [], + "downvotes": 2044, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d6a", + "creator": "Morgan Cheng", + "createdAt": 1233897313000, + "text": "This looks a good solution, but can you explain why this works?", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d6b", + "creator": "Jason S", + "createdAt": 1242229984000, + "text": "@George IV: "just do `if( variable ) " -- um, no, that fails for false and 0.", + "upvotes": 91, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d6c", + "creator": "scotts", + "createdAt": 1273012812000, + "text": "'if( variable )' also fails for testing for the existence of object properties.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d6e", + "creator": "squarecandy", + "createdAt": 1323142479000, + "text": ""if (typeof variable !== 'undefined') { // variable is not undefined }" is working for me too... thanks!", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d70", + "creator": "temporary_user_name", + "createdAt": 1390359756000, + "text": "@anyone Why is this preferable to if (variable === undefined) {} ?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d71", + "creator": "Jim Puls", + "createdAt": 1392069601000, + "text": "See a couple answers further down. The value undefined is mutable.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d73", + "creator": "Ilker Cat", + "createdAt": 1450273763000, + "text": "@Jason S "um, no, that fails for false and 0" -- um, yes, if you know that your variable cannot be false or 0 :)", + "upvotes": 1043, + "upvoterUsernames": [], + "downvotes": 1043, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d75", + "creator": "Mulan", + "createdAt": 1464871440000, + "text": "typeof null; //=> "object"", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d76", + "creator": "Maddy", + "createdAt": 1595924588000, + "text": "you can use !!ArrayName. This will return false if array is undefined or null. In case of empty array it will return true.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9102d", + "creator": "David Tang", + "createdAt": 1298605708000, + "text": "

In the majority of cases you would use:

\n\n
elem != null\n
\n\n

Unlike a simple if (elem), it allows 0, false, NaN and '', but rejects null or undefined, making it a good, general test for the presence of an argument, or property of an object.

\n\n
\n\n

The other checks are not incorrect either, they just have different uses:

\n\n\n\n
\n\n

Also useful is a strict comparison against undefined:

\n\n
if (elem === undefined) ...\n
\n\n

However, because the global undefined can be overridden with another value, it is best to declare the variable undefined in the current scope before using it:

\n\n
var undefined; // really undefined\nif (elem === undefined) ...\n
\n\n

Or:

\n\n
(function (undefined) {\n    if (elem === undefined) ...\n})();\n
\n\n

A secondary advantage of this method is that JS minifiers can reduce the undefined variable to a single character, saving you a few bytes every time.

\n", + "upvotes": 184, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d78", + "creator": "Alex W", + "createdAt": 1378737720000, + "text": "This causes an exception and requires you to use window. before the variable if used in the global context...this is not the best way.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d79", + "creator": "Liglo App", + "createdAt": 1414060185000, + "text": "Because of this overriding issue you should ALWAYS use void(0) instead of undefined.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d7b", + "creator": "rinogo", + "createdAt": 1530056483000, + "text": "+1 since this answer points out that sometimes you may actually want to identify false, 0, etc. as invalid values.", + "upvotes": 999, + "upvoterUsernames": [], + "downvotes": 999, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9102e", + "creator": "Jith", + "createdAt": 1298606911000, + "text": "

It is difficult to distinguish between undefined and null. Null is a value you can assign to a variable when you want to indicate that the variable has no particular value. Undefined\nis a special value which will be the default value of unassigned variables.

\n\n
\n\nvar _undefined;\nvar _null = null;\n\nalert(_undefined); \nalert(_null); \nalert(_undefined == _null);\nalert(_undefined === _null);\n\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d7d", + "creator": "demisx", + "createdAt": 1420179444000, + "text": "Would be helpful to show inline the output of each alert.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9102f", + "creator": "jpsimons", + "createdAt": 1298607309000, + "text": "

It depends on the situation. If you're checking for something that may or may not have been defined globally outside your code (like jQuery perhaps) you want:

\n\n
if (typeof(jQuery) != \"undefined\")\n
\n\n

(No need for strict equality there, typeof always returns a string.) But if you have arguments to a function that may or may not have been passed, they'll always be defined, but null if omitted.

\n\n
function sayHello(name) {\n    if (name) return \"Hello, \" + name;\n    else return \"Hello unknown person\";\n}\nsayHello(); // => \"Hello unknown person\"\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d80", + "creator": "Tom", + "createdAt": 1643684421000, + "text": "I agree here. I don't know why everyone is using strict equality when it's not necessary.", + "upvotes": 4773, + "upvoterUsernames": [], + "downvotes": 4773, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91030", + "creator": "Samuel Liew", + "createdAt": 1309276811000, + "text": "

The typeof operator will check if the variable is really undefined.

\n\n
if (typeof variable === 'undefined') {\n    // variable is undefined\n}\n
\n\n

The typeof operator, unlike the other operators, doesn't throw a ReferenceError exception when used with an undeclared variable.

\n\n

However, do note that typeof null will return \"object\". We have to be careful to avoid the mistake of initializing a variable to null. To be safe, this is what we could use instead:

\n\n
if (typeof variable === 'undefined' || variable === null) {\n    // variable is undefined or null\n}\n
\n\n
\n\n

For more info on using strict comparison === instead of simple equality ==, see:
Which equals operator (== vs ===) should be used in JavaScript comparisons?

\n", + "upvotes": 2050, + "upvoterUsernames": [], + "downvotes": 1013, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f329b6082fcc3049e92d82", + "creator": "Zelphir Kaltstahl", + "createdAt": 1495009168000, + "text": "Warning: This does not work for members of objects, if you try to access them using the dot notation as in some_object.a_member.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329b6082fcc3049e92d84", + "creator": "Fuseteam", + "createdAt": 1562617819000, + "text": "why not just variable != null it seems to catch "undefined" variables just as well", + "upvotes": 2402, + "upvoterUsernames": [], + "downvotes": 2402, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91033", + "creator": "skalee", + "createdAt": 1361071885000, + "text": "

In the particular situation outlined in the question,

\n\n
typeof window.console === \"undefined\"\n
\n\n

is identical to

\n\n
window.console === undefined\n
\n\n

I prefer the latter since it's shorter.

\n\n

Please note that we look up for console only in global scope (which is a window object in all browsers). In this particular situation it's desirable. We don't want console defined elsewhere.

\n\n

@BrianKelley in his great answer explains technical details. I've only added lacking conclusion and digested it into something easier to read.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b7082fcc3049e92d86", + "creator": "john ktejik", + "createdAt": 1412990733000, + "text": "False. the latter throws an exception in my console.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91032", + "creator": "boslior", + "createdAt": 1357200518000, + "text": "
if (typeof console != \"undefined\") {    \n   ...\n}\n
\n\n

Or better

\n\n
if ((typeof console == \"object\") && (typeof console.profile == \"function\")) {    \n   console.profile(f.constructor);    \n}\n
\n\n

Works in all browsers

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b7082fcc3049e92d89", + "creator": "skalee", + "createdAt": 1361071940000, + "text": "Why the latter is better in your opinion?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b7082fcc3049e92d8a", + "creator": "Broxzier", + "createdAt": 1373466684000, + "text": "@skalee I agree the latter is better. This for the simple reason that you check if the types are the ones you want before using them.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91034", + "creator": "shadowstorm", + "createdAt": 1361557392000, + "text": "

The highest answer is correct, use typeof.

\n\n

However, what I wanted to point out was that in JavaScript undefined is mutable (for some ungodly reason). So simply doing a check for varName !== undefined has the potential to not always return as you expect it to, because other libs could have changed undefined. A few answers (@skalee's, for one), seem to prefer not using typeof, and that could get one into trouble.

\n\n

The \"old\" way to handle this was declaring undefined as a var to offset any potential muting/over-riding of undefined. However, the best way is still to use typeof because it will ignore any overriding of undefined from other code. Especially if you are writing code for use in the wild where who knows what else could be running on the page...

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91031", + "creator": "Brian Kelley", + "createdAt": 1350920872000, + "text": "

In JavaScript, a variable can be defined, but hold the value undefined, so the most common answer is not technically correct, and instead performs the following:

\n\n
if (typeof v === \"undefined\") {\n   // no variable \"v\" is defined in the current scope\n   // *or* some variable v exists and has been assigned the value undefined\n} else {\n   // some variable (global or local) \"v\" is defined in the current scope\n   // *and* it contains a value other than undefined\n}\n
\n\n

That may suffice for your purposes. The following test has simpler semantics, which makes it easier to precisely describe your code's behavior and understand it yourself (if you care about such things):

\n\n
if (\"v\" in window) {\n   // global variable v is defined\n} else {\n   // global variable v is not defined\n}\n
\n\n

This, of course, assumes you are running in a browser (where window is a name for the global object). But if you're mucking around with globals like this you're probably in a browser. Subjectively, using 'name' in window is stylistically consistent with using window.name to refer to globals. Accessing globals as properties of window rather than as variables allows you to minimize the number of undeclared variables you reference in your code (for the benefit of linting), and avoids the possibility of your global being shadowed by a local variable. Also, if globals make your skin crawl you might feel more comfortable touching them only with this relatively long stick.

\n", + "upvotes": 320, + "upvoterUsernames": [], + "downvotes": 96, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b7082fcc3049e92d8e", + "creator": "qwertzguy", + "createdAt": 1455100651000, + "text": "For Angular users: Unfortunately, it doesn't seem to be allowed in an ng-if statement.", + "upvotes": 1863, + "upvoterUsernames": [], + "downvotes": 1863, + "downvoterUsernames": [] + }, + { + "_id": "62f329b7082fcc3049e92d90", + "creator": "l00k", + "createdAt": 1572866728000, + "text": "=== makes no sense here.. result of typeof is always string right? == 'undefined' is enough", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f329b7082fcc3049e92d91", + "creator": "asdf3.14159", + "createdAt": 1617648864000, + "text": "This doesn't work for const or let variables", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b7082fcc3049e92d93", + "creator": "T Tse", + "createdAt": 1651178464000, + "text": "window might not be defined if the js isn't run in a browser", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b7082fcc3049e92d95", + "creator": "Ben Aston", + "createdAt": 1653236221000, + "text": "globalThis (added in ES2020) is a platform-independent mechanism for accessing the global object.", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91035", + "creator": "Ravindra Miyani", + "createdAt": 1398403230000, + "text": "

you can use the typeof operator.

\n\n

For example,

\n\n
var dataSet;\n\nalert(\"Variable dataSet is : \" + typeof dataSet);\n
\n\n

Above code snippet will return the output like

\n\n
\n

variable dataSet is : undefined.

\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91036", + "creator": "Zv_oDD", + "createdAt": 1412080552000, + "text": "

The most robust 'is it defined' check is with typeof

\n\n
if (typeof elem === 'undefined')\n
\n\n

If you are just checking for a defined variable to assign a default, for an easy to read one liner\nyou can often do this:

\n\n
elem = elem || defaultElem;\n
\n\n

It's often fine to use, see: Idiomatic way to set default value in javascript

\n\n

There is also this one liner using the typeof keyword:

\n\n
elem = (typeof elem === 'undefined') ? defaultElem : elem;\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91038", + "creator": "Razan Paul", + "createdAt": 1442812801000, + "text": "

Null is a value in JavaScript and typeof null returns \"object\"

\n\n

Therefore, accepted answer will not work if you pass null values. If you pass null values, you need to add an extra check for null values:

\n\n
if ((typeof variable !== \"undefined\") && (variable !== null))  \n{\n   // the variable is defined and not null\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91037", + "creator": "SoEzPz", + "createdAt": 1435952729000, + "text": "

I use two different ways depending on the object.

\n\n
if( !variable ){\n  // variable is either\n  // 1. '';\n  // 2. 0;\n  // 3. undefined;\n  // 4. null;\n  // 5. false;\n}\n
\n\n

Sometimes I do not want to evaluate an empty string as falsey, so then I use this case

\n\n
function invalid( item ){\n  return (item === undefined || item === null);\n}\n\nif( invalid( variable )){\n  // only here if null or undefined;\n}\n
\n\n

If you need the opposite, then in the first instance !variable becomes !!variable, and in the invalid function === become != and the function names changes to notInvalid.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91039", + "creator": "curtwphillips", + "createdAt": 1449992341000, + "text": "

My preference is typeof(elem) != 'undefined' && elem != null.

\n\n

However you choose, consider putting the check in a function like so

\n\n
function existy (x) {\n    return typeof (x) != 'undefined' && x != null;\n}\n
\n\n

If you don't know the variable is declared then continue with typeof (x) != 'undefined' && x != null;

\n\n

Where you know the variable is declared but may not be existy, you could use

\n\n
existy(elem) && doSomething(elem);\n
\n\n

The variable you are checking may be a nested property sometimes. You can use prop || {} to go down the line checking existance to the property in question:

\n\n
var exists = ((((existy(myObj).prop1||{}).prop2||{}).prop3||{})[1]||{}).prop4;\n
\n\n

After each property use (...' || {}').nextProp so that a missing property won't throw an error.

\n\n

Or you could use existy like existy(o) && existy(o.p) && existy(o.p.q) && doSomething(o.p.q)

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9103a", + "creator": "de3", + "createdAt": 1453979495000, + "text": "

To contribute to the debate, if I know the variable should be a string or an object I always prefer if (!variable), so checking if its falsy. This can bring to more clean code so that, for example:

\n\n

\r\n
\r\n
if (typeof data !== \"undefined\" && typeof data.url === \"undefined\") {\r\n    var message = 'Error receiving response';\r\n    if (typeof data.error !== \"undefined\") {\r\n        message = data.error;\r\n    } else if (typeof data.message !== \"undefined\") {\r\n        message = data.message;\r\n    }\r\n    alert(message); \r\n}
\r\n
\r\n
\r\n

\n\n

..could be reduced to:

\n\n

\r\n
\r\n
if (data && !data.url) {\r\n  var message = data.error || data.message || 'Error receiving response';\r\n  alert(message)\r\n} 
\r\n
\r\n
\r\n

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9103b", + "creator": "RajeshKdev", + "createdAt": 1455859615000, + "text": "

There is another short hand way to check this, when you perform simple assignments and related checks. Simply use Conditional (Ternary) Operator.

\n\n
var values = typeof variable !== 'undefined' ? variable : '';\n
\n\n

Also this will be helpful, when you try to declare the Global variable with instance assignment of the reference variable.

\n\n

If you wanted to check variable shouldn't be undefined or null. Then perform below check.

\n\n

When the variable is declared, and if you want to check the value, this is even Simple: and it would perform undefined and null checks together.

\n\n
var values = variable ? variable : '';\n
\n", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92d9c", + "creator": "Fareed Alnamrouti", + "createdAt": 1496133011000, + "text": "your 2nd check will fail with 0 value", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9103c", + "creator": "John Slegers", + "createdAt": 1455882860000, + "text": "

How to check if a variable exists

\n

This is a pretty bulletproof solution for testing if a variable exists and has been initialized :

\n
var setOrNot = typeof variable !== typeof undefined;\n
\n

It is most commonly used in combination with a ternary operator to set a default in case a certain variable has not been initialized :

\n
var dark = typeof darkColor !== typeof undefined ? darkColor : "black";\n
\n
\n

Problems with encapsulation

\n

Unfortunately, you cannot simply encapsulate your check in a function.

\n

You might think of doing something like this :

\n
function isset(variable) {\n    return typeof variable !== typeof undefined;\n}\n
\n

However, this will produce a reference error if you're calling eg. isset(foo) and variable foo has not been defined, because you cannot pass along a non-existing variable to a function :

\n
\n

Uncaught ReferenceError: foo is not defined

\n
\n
\n

Testing whether function parameters are undefined

\n

While our isset function cannot be used to test whether a variable exists or not (for reasons explained hereabove), it does allow us to test whether the parameters of a function are undefined :

\n
var a = '5';\n\nvar test = function(x, y) {\n    console.log(isset(x));\n    console.log(isset(y));\n};\n\ntest(a);\n\n// OUTPUT :\n// ------------\n// TRUE\n// FALSE\n
\n

Even though no value for y is passed along to function test, our isset function works perfectly in this context, because y is known in function test as an undefined value.

\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9103e", + "creator": "Bekim Bacaj", + "createdAt": 1482807343000, + "text": "

These answers (aside from the Fred Gandt solution ) are all either incorrect or incomplete.

\n

Suppose I need my variableName; to carry an undefined value, and therefore it has been declared in a manner such as var variableName; which means it's already initialized; - How do I check if it's already declared?

\n

Or even better - how do I immediately check if "Book1.chapter22.paragraph37" exists with a single call, but not rise a reference error?

\n

We do it by using the most powerful JasvaScript operator, the in operator.:

\n
"[variable||property]" in [context||root] \n>> true||false\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92d9f", + "creator": "user956584", + "createdAt": 1641512625000, + "text": "if ( ("url" in req.body) == false && req.body.url.length > 1", + "upvotes": 326, + "upvoterUsernames": [], + "downvotes": 326, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e9103d", + "creator": "Ferran Maylinch", + "createdAt": 1459875844000, + "text": "

To check if a variable has been declared/set I did this dirty trick.

\n\n

I haven't found a way to extract the code to a function, even with eval.

\n\n
\"use strict\";\n\n// var someVar;\n\nvar declared;\ntry {\n  someVar;\n  declared = true;\n} catch(e) {\n  declared = false;\n}\n\nif (declared) {\n  console.log(\"someVar is declared; now has the value: \" + someVar);\n} else {\n  console.log(\"someVar is not declared\");\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92da0", + "creator": "Melab", + "createdAt": 1579998665000, + "text": "What do you mean by "extract the code to a function"?", + "upvotes": 903, + "upvoterUsernames": [], + "downvotes": 903, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92da2", + "creator": "aross", + "createdAt": 1641405854000, + "text": "While this is a good answer technically, it would be evidence of bad design if you truly require this.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92da3", + "creator": "Aidan Welch", + "createdAt": 1656101313000, + "text": "@aross No typeof will still error, you need to use a try block", + "upvotes": 277, + "upvoterUsernames": [], + "downvotes": 277, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91040", + "creator": "Daniel", + "createdAt": 1553719263000, + "text": "

I'm surprised this wasn't mentioned yet...

\n\n

here are a couple of additional variations using this['var_name']

\n\n

the benefit of using this method that it can be used before a variable is defined.

\n\n
if (this['elem']) {...}; // less safe than the res but works as long as you're note expecting a falsy value\nif (this['elem'] !== undefined) {...}; // check if it's been declared\nif (this['elem'] !== undefined && elem !== null) {...}; // check if it's not null, you can use just elem for the second part\n\n// these will work even if you have an improper variable definition declared here\nelem = null; // <-- no var here!! BAD!\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e9103f", + "creator": "Alireza", + "createdAt": 1504246525000, + "text": "

In many cases, using:

\n\n
if (elem) { // or !elem\n
\n\n

will do the job for you!... this will check these below cases:

\n\n
    \n
  1. undefined: if the value is not defined and it's undefined
  2. \n
  3. null: if it's null, for example, if a DOM element not exists...
  4. \n
  5. empty string: ''
  6. \n
  7. 0: number zero
  8. \n
  9. NaN: not a number
  10. \n
  11. false
  12. \n
\n\n

So it will cover off kind of all cases, but there are always weird cases which we'd like to cover as well, for example, a string with spaces, like this ' ' one, this will be defined in javascript as it has spaces inside string... for example in this case you add one more check using trim(), like:

\n\n
if(elem) {\n\nif(typeof elem === 'string' && elem.trim()) {\n///\n
\n\n

Also, these checks are for values only, as objects and arrays work differently in Javascript, empty array [] and empty object {} are always true.

\n\n

I create the image below to show a quick brief of the answer:

\n\n

\"undefined,

\n", + "upvotes": 409, + "upvoterUsernames": [], + "downvotes": 101, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92da6", + "creator": "Thiago Yoithi", + "createdAt": 1511978542000, + "text": "Is the first case suitable when checking for an empty array too?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92da8", + "creator": "Alireza", + "createdAt": 1511993849000, + "text": "@ThiagoYoithi, yes, you need to pass Array.length in this case which is 0 when it's empty, like if (myArray.length) {...}", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92da9", + "creator": "Thiago Yoithi", + "createdAt": 1512061041000, + "text": "Yeah, but I would like to know the behavior in this approach: let arrayVar = []; if(arrayVar) // Is the result of this condition true, or false?", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92daa", + "creator": "ropo", + "createdAt": 1542621900000, + "text": "I get a "ReferenceError: elem is not defined"", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92dac", + "creator": "Fanky", + "createdAt": 1557844476000, + "text": "Then the answer is misleading when it says if(elem) checks for undefined (while it returns not defined error), isn't it?", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92dae", + "creator": "john ktejik", + "createdAt": 1587934038000, + "text": "NOt correct. Doesn't catch completely undefined variables which is what the OP asked.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91042", + "creator": "Kamil Kiełczewski", + "createdAt": 1589710934000, + "text": "

Try-catch

\n

If variable was not defined at all (for instance: external library which define global variable is not yet loaded - e.g. google maps), you can check this without break code execution using try-catch block as follows (you don't need to use strict mode)

\n

\r\n
\r\n
try{\n  notDefinedVariable;\n} catch(e) {\n  console.log('detected: variable not exists');\n}\n\nconsole.log('but the code is still executed');\n\nnotDefinedVariable; // without try-catch wrapper code stops here\n\nconsole.log('code execution stops. You will NOT see this message on console');
\r\n
\r\n
\r\n

\n

BONUS: (referring to other answers) Why === is more clear than == (source)

\n

if( a == b )

\n

\"Enter

\n

if( a === b )

\n

\"Enter

\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92daf", + "creator": "johnsnails", + "createdAt": 1590752627000, + "text": "FYI, (a == b) placed onto Game of Life grid was not all that exciting.", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91043", + "creator": "shreyasm-dev", + "createdAt": 1591223360000, + "text": "

You could use a try...catch block like the following:

\n

\r\n
\r\n
var status = 'Variable exists'\n\ntry {\n  myVar\n} catch (ReferenceError) {\n  status = 'Variable does not exist'\n}\n\nconsole.log(status)
\r\n
\r\n
\r\n

\n

A disadvantage is you cannot put it in a function as it would throw a ReferenceError

\n

\r\n
\r\n
function variableExists(x) {\n  var status = true\n  try {\n    x\n  } catch (ReferenceError) {\n    status = false\n  }\n  \n  return status\n}\n\nconsole.log(variableExists(x))
\r\n
\r\n
\r\n

\n

Edit:

\n

If you were working in front-end Javascript and you needed to check if a variable was not initialized (var x = undefined would count as not initialized), you could use:

\n

\r\n
\r\n
function globalVariableExists(variable) {\n  if (window[variable] != undefined) {\n    return true\n  }\n  \n  return false\n}\n\nvar x = undefined\n\nconsole.log(globalVariableExists(\"x\"))\n\nconsole.log(globalVariableExists(\"y\"))\n\nvar z = 123\n\nconsole.log(globalVariableExists(\"z\"))
\r\n
\r\n
\r\n

\n

Edit 2:

\n

If you needed to check if a variable existed in the current scope, you could simply pass this to the function, along with the name of the variable contained in a string:

\n

\r\n
\r\n
function variableExists(variable, thisObj) {\n  if (thisObj[variable] !== undefined) {\n    return true\n  }\n  \n  return false\n}\n\nclass someClass {\n  constructor(name) { \n    this.x = 99\n    this.y = 99\n    this.z = 99\n    this.v = 99\n    \n    console.log(variableExists(name, this))\n  }\n}\n\nnew someClass('x')\nnew someClass('y')\nnew someClass('z')\nnew someClass('v')\nnew someClass('doesNotExist')
\r\n
\r\n
\r\n

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91041", + "creator": "user2878850", + "createdAt": 1557521747000, + "text": "

Short way to test a variable is not declared (not undefined) is

\n\n
if (typeof variable === \"undefined\") {\n  ...\n}\n
\n\n

I found it useful for detecting script running outside a browser (not having declared window variable).

\n", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b8082fcc3049e92db1", + "creator": "Jason", + "createdAt": 1563461065000, + "text": "is this the "canonical way" that is portable?", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + }, + { + "_id": "62f329b8082fcc3049e92db2", + "creator": "ha9u63ar", + "createdAt": 1581584342000, + "text": "this code doesn't work and you can verify this by using any browser console", + "upvotes": 3180, + "upvoterUsernames": [], + "downvotes": 3180, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91044", + "creator": "John Lord", + "createdAt": 1597700420000, + "text": "
if (variable === undefined) {}\n
\n

works just fine, and only checks for undefined.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91045", + "creator": "HoldOffHunger", + "createdAt": 1599596639000, + "text": "

In ReactJS, things are a bit more complicated! This is because it is a compiled environment, which follows ESLint's no-undef rule since react-scripts@2.0.3 (released Oct. 1st, 2018). The documentation here is helpful to anyone interested in this problem...

\n
\n

In JavaScript, prior to ES6, variable and function declarations are hoisted to the top of a scope, so it's possible to use identifiers before their formal declarations in code....

\n

This [new] rule [of ES6] will warn when it encounters a reference to an identifier that has not yet been declared.

\n
\n

So, while it's possible to have an undefined (or "uninitialized") variable, it is not possible to have an undeclared variable in ReactJS without turning off the eslint rules.

\n

This can be very frustrating -- there are so many projects on GitHub that simply take advantage of the pre-ES6 standards; and directly compiling these without any adjustments is basically impossible.

\n

But, for ReactJS, you can use eval(). If you have an undeclared variable like...

\n
if(undeclaredvar) {...}\n
\n

You can simply rewrite this part as...

\n
if(eval('typeof undeclaredvar !== "undefined"')) {...}\n
\n

For instance...

\n

\r\n
\r\n
if(eval(\"false\")) {\n  console.log(\"NO!\");\n}\nif(eval(\"true\")) {\n  console.log(\"YEAH!\");\n}
\r\n
\r\n
\r\n

\n

For those importing GitHub repositories into a ReactJS project, this is simply the only way to check if a variable is declared. Before closing, I'd like to remind you that there are security issues with eval() if use incorrectly.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91047", + "creator": "Trevor", + "createdAt": 1614217836000, + "text": "

I prefer this method for it's accuracy and succinctness:

\n

\r\n
\r\n
var x\nif (x === void 0) {\n  console.log(`x is undefined`)\n} else {\n  console.log(`x is defined`)\n}
\r\n
\r\n
\r\n

\n

As has been mentioned in other comments and answers, undefined isn't guaranteed to be undefined. Because it's not a keyword, it can be redefined as a variable in scopes other than the global scope. Here's little example that demonstrates this nuance:

\n

\r\n
\r\n
var undefined = 'bar'\nconsole.log(`In the global scope: ${undefined}`)\n\nfunction foo() {\n  var undefined = 'defined'\n  var x\n  if (x === undefined) {\n    console.log(`x === undefined`)\n  } else {\n    console.log(`x !== undefined`)\n  }\n  if (x === void 0) {\n    console.log(`x === void 0`)\n  } else {\n    console.log(`x !== void 0`)\n  }\n}\n\nfoo()
\r\n
\r\n
\r\n

\n

See void for compatibility (supported in IE5!?!! Wow!).

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b9082fcc3049e92db6", + "creator": "mckeed", + "createdAt": 1658617054000, + "text": "Does this have any advantage over typeof x === 'undefined'? Redefining undefined doesn't affect that does it?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e91048", + "creator": "Praveen Kumar", + "createdAt": 1643250998000, + "text": "

For the if condition to work correctly, we have to use the keyword let for creating variables.

\n
let name = undefined; \nif (name) { \n    alert('valid')\n};\n
\n", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 312, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e91046", + "creator": "daCoda", + "createdAt": 1603938643000, + "text": "

You can also use !! before a variable to check if it's defined. E.g.

\n
let dog = "woof";\nlet chineseCat; // Undefined.\nconsole.log("1.");\nconsole.log(!!dog && !!chineseCat ? "Both are defined" : "Both are NOT defined");\n\nchineseCat= "mao"; // dog and chineseCat are now defined.\nconsole.log("2.");\nconsole.log(!!dog && !!chineseCat ? "Both are defined" : "Both are NOT defined");\n
\n

Output:

\n
1.\nBoth are NOT defined\n2. \nBoth are defined\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329b9082fcc3049e92db9", + "creator": "DanCat", + "createdAt": 1604089532000, + "text": "is this a node construct and if so which version ... I was searching for exactly this and can't seem to find it ... JavaScript :shakesfirst:", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 82, + "downvoterUsernames": [] + }, + { + "_id": "62f329b9082fcc3049e92dbb", + "creator": "jcaron", + "createdAt": 1611928818000, + "text": "This is quite wrong, this checks only whether the variable is truthy, not defined. !!0 is false even though 0 is defined.", + "upvotes": 63, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 2194766, + "uvac": 2194796 + } + }, + { + "_id": "62f321bb082fcc3049e8feeb", + "title": "How do I refresh a page using JavaScript?", + "title-lowercase": "how do i refresh a page using javascript?", + "creator": "luca", + "createdAt": 1300881331000, + "status": "open", + "text": "

How do I refresh a page using JavaScript?

\n", + "upvotes": 4061, + "upvoterUsernames": [], + "downvotes": 1387, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2930715, + "answers": 30, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90ad2", + "creator": "Thorben", + "createdAt": 1300881468000, + "text": "

This works on all browsers:

\n
location.reload();\n
\n", + "upvotes": 479, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ad3", + "creator": "Roy", + "createdAt": 1300881471000, + "text": "

Use location.reload().

\n

For example, to reload whenever an element with id="something" is clicked:

\n
$('#something').click(function() {\n    location.reload();\n});\n
\n

The reload() function takes an optional parameter that can be set to true to force a reload from the server rather than the cache. The parameter defaults to false, so by default the page may reload from the browser's cache.

\n", + "upvotes": 5292, + "upvoterUsernames": [], + "downvotes": 1189, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f3275a082fcc3049e923f2", + "creator": "Yster", + "createdAt": 1449566431000, + "text": "This didn't work for me. This worked though: window.location.href=window.location.href;", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e923f4", + "creator": "twharmon", + "createdAt": 1475736023000, + "text": "This didn't work for me. window.location.href=window.location.href; and location.href=location.href; worked.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e923f6", + "creator": "Sagar Naliyapara", + "createdAt": 1493102377000, + "text": "window.location.reload(true); will hard refresh, otherwise by default it's false", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e923f8", + "creator": "user9258013", + "createdAt": 1548514836000, + "text": "Just run window.location.reload()!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e923fa", + "creator": "Mark Baijens", + "createdAt": 1577984771000, + "text": "@FaridAbbas location.reload(); is standard javascript. You don't need jQuery for it.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ad5", + "creator": "JohnP", + "createdAt": 1300881679000, + "text": "

The question should be,

\n\n

How to refresh a page with JavaScript

\n\n
window.location.href = window.location.href; //This is a possibility\nwindow.location.reload(); //Another possiblity\nhistory.go(0); //And another\n
\n\n

You're spoiled for choice.

\n", + "upvotes": 99, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ad4", + "creator": "David", + "createdAt": 1300881503000, + "text": "

Lots of ways will work, I suppose:

\n\n\n", + "upvotes": 230, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275a082fcc3049e923fe", + "creator": "Yster", + "createdAt": 1449565818000, + "text": "The only one that worked for me was this one: window.location.href=window.location.href;", + "upvotes": 7701, + "upvoterUsernames": [], + "downvotes": 7701, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e923ff", + "creator": "Rohit Singh", + "createdAt": 1617264612000, + "text": "window.location.href=window.location.href worked for me", + "upvotes": 595, + "upvoterUsernames": [], + "downvotes": 595, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ad6", + "creator": "Peter", + "createdAt": 1317380430000, + "text": "

To reload a page with jQuery, do:

\n\n
$.ajax({\n    url: \"\",\n    context: document.body,\n    success: function(s,x){\n        $(this).html(s);\n    }\n});\n
\n\n

The approach here that I used was Ajax jQuery. I tested it on Chrome 13. Then I put the code in the handler that will trigger the reload. The URL is \"\", which means this page.

\n", + "upvotes": 226, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ad7", + "creator": "SumairIrshad", + "createdAt": 1340366328000, + "text": "

If the current page was loaded by a POST request, you may want to use

\n\n
window.location = window.location.pathname;\n
\n\n

instead of

\n\n
window.location.reload();\n
\n\n

because window.location.reload() will prompt for confirmation if called on a page that was loaded by a POST request.

\n", + "upvotes": 175, + "upvoterUsernames": [], + "downvotes": 64, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275a082fcc3049e92402", + "creator": "mrmillsy", + "createdAt": 1384528084000, + "text": "This will lose the querystring however, whereas window.location = window.location will not", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ad9", + "creator": "Suleman Mirza", + "createdAt": 1372104487000, + "text": "

The jQuery Load function can also perform a page refresh:

\n\n
$('body').load('views/file.html', function () {\n    $(this).fadeIn(5000);\n});\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275a082fcc3049e92405", + "creator": "Mark Amery", + "createdAt": 1445313322000, + "text": "No, this isn't a page refresh.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ad8", + "creator": "Ionică Bizău", + "createdAt": 1371980854000, + "text": "

There are multiple unlimited ways to refresh a page with JavaScript:

\n\n
    \n
  1. location.reload()
  2. \n
  3. history.go(0)
  4. \n
  5. location.href = location.href
  6. \n
  7. location.href = location.pathname
  8. \n
  9. location.replace(location.pathname)
  10. \n
  11. location.reload(false)

    \n\n
    \n

    If we needed to pull the document from\n the web-server again (such as where the document contents\n change dynamically) we would pass the argument as true.

    \n
  12. \n
\n\n

You can continue the list being creative:

\n\n\n\n

\r\n
\r\n
var methods = [\r\n  \"location.reload()\",\r\n  \"history.go(0)\",\r\n  \"location.href = location.href\",\r\n  \"location.href = location.pathname\",\r\n  \"location.replace(location.pathname)\",\r\n  \"location.reload(false)\"\r\n];\r\n\r\nvar $body = $(\"body\");\r\nfor (var i = 0; i < methods.length; ++i) {\r\n  (function(cMethod) {\r\n    $body.append($(\"<button>\", {\r\n      text: cMethod\r\n    }).on(\"click\", function() {\r\n      eval(cMethod); // don't blame me for using eval\r\n    }));\r\n  })(methods[i]);\r\n}
\r\n
button {\r\n  background: #2ecc71;\r\n  border: 0;\r\n  color: white;\r\n  font-weight: bold;\r\n  font-family: \"Monaco\", monospace;\r\n  padding: 10px;\r\n  border-radius: 4px;\r\n  cursor: pointer;\r\n  transition: background-color 0.5s ease;\r\n  margin: 2px;\r\n}\r\nbutton:hover {\r\n  background: #27ae60;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 892, + "upvoterUsernames": [], + "downvotes": 392, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275a082fcc3049e92407", + "creator": "Ionică Bizău", + "createdAt": 1373383340000, + "text": "@Cԃաԃ I see no difference... Maybe the cache is the issue? I will take a look soon.", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e92409", + "creator": "Amal Murali", + "createdAt": 1387987858000, + "text": "location.href = location.href is what I usually use, but thanks for the others. Very useful! +1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3275a082fcc3049e9240b", + "creator": "Ionică Bizău", + "createdAt": 1388497785000, + "text": "@Cԃաԃ Finally I can reproduce what you see and I asked here.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ada", + "creator": "Pinch", + "createdAt": 1374762424000, + "text": "

You may want to use

\n\n
location.reload(forceGet)\n
\n\n

forceGet is a boolean and optional.

\n\n

The default is false which reloads the page from the cache.

\n\n

Set this parameter to true if you want to force the browser to get the page from the server to get rid of the cache as well.

\n\n

Or just

\n\n
location.reload()\n
\n\n

if you want quick and easy with caching.

\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 48, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90adb", + "creator": "user762330", + "createdAt": 1381696330000, + "text": "

window.location.reload() will reload from the server and will load all your data, scripts, images, etc. again.

\n\n

So if you just want to refresh the HTML, the window.location = document.URL will return much quicker and with less traffic. But it will not reload the page if there is a hash (#) in the URL.

\n", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90adc", + "creator": "Fer To", + "createdAt": 1443777934000, + "text": "

As the question is generic, let's try to sum up possible solutions for the answer:

\n\n

Simple plain JavaScript Solution:

\n\n

The easiest way is a one line solution placed in an appropriate way:

\n\n
location.reload();\n
\n\n

What many people are missing here, because they hope to get some \"points\" is that the reload() function itself offers a Boolean as a parameter (details: https://developer.mozilla.org/en-US/docs/Web/API/Location/reload).

\n\n
\n

The Location.reload() method reloads the resource from the current\n URL. Its optional unique parameter is a Boolean, which, when it is\n true, causes the page to always be reloaded from the server. If it is\n false or not specified, the browser may reload the page from its\n cache.

\n
\n\n

This means there are two ways:

\n\n

Solution1: Force reloading the current page from the server

\n\n
location.reload(true);\n
\n\n

Solution2: Reloading from cache or server (based on browser and your config)

\n\n
location.reload(false);\nlocation.reload();\n
\n\n

And if you want to combine it with jQuery an listening to an event, I would recommend using the \".on()\" method instead of \".click\" or other event wrappers, e.g. a more proper solution would be:

\n\n
$('#reloadIt').on('eventXyZ', function() {\n    location.reload(true);\n});\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90add", + "creator": "William Entriken", + "createdAt": 1463524247000, + "text": "

Here is a solution that asynchronously reloads a page using jQuery. It avoids the flicker caused by window.location = window.location. This example shows a page that reloads continuously, as in a dashboard. It is battle-tested and is running on an information display TV in Times Square.

\n\n
<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    ...\n    <meta http-equiv=\"refresh\" content=\"300\">\n    <script src=\"//ajax.googleapis.com/ajax/libs/jquery/2.2.3/jquery.min.js\"></script>\n    <script>\n    function refresh() {\n      $.ajax({\n        url: \"\",\n        dataType: \"text\",\n        success: function(html) {\n          $('#fu').replaceWith($.parseHTML(html));\n          setTimeout(refresh,2000);\n        }\n      });\n    }\n    refresh();\n    </script>\n  </head>\n  <body>\n    <div id=\"fu\">\n      ...\n    </div>\n  </body>\n</html>\n
\n\n

Notes:

\n\n\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90adf", + "creator": "Poorna Senani Gamage", + "createdAt": 1520270281000, + "text": "

You can use JavaScript location.reload() method.\nThis method accepts a boolean parameter. true or false. If the parameter is true; the page always reloaded from the server. If it is false; which is the default or with empty parameter browser reload the page from it's cache.

\n\n

With true parameter

\n\n
<button type=\"button\" onclick=\"location.reload(true);\">Reload page</button>\n
\n\n

With default/ false parameter

\n\n
 <button type=\"button\" onclick=\"location.reload();\">Reload page</button>\n
\n\n

Using jquery

\n\n
<button id=\"Reloadpage\">Reload page</button>\n<script type=\"text/javascript\">\n    $('#Reloadpage').click(function() {\n        location.reload();\n    }); \n</script>\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ade", + "creator": "Mujah Maskey", + "createdAt": 1473955460000, + "text": "

I found

\n\n
window.location.href = \"\";\n
\n\n

or

\n\n
window.location.href = null;\n
\n\n

also makes a page refresh.

\n\n

This makes it very much easier to reload the page removing any hash.\nThis is very nice when I am using AngularJS in the iOS simulator, so that I don't have to rerun the app.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275b082fcc3049e92411", + "creator": "Junaid Atari", + "createdAt": 1503596882000, + "text": "nice shoot, i wasn't aware of that.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ae1", + "creator": "Alireza", + "createdAt": 1527396804000, + "text": "

You don't need anything from jQuery, to reload a page using pure JavaScript, just use reload function on location property like this:

\n\n
window.location.reload();\n
\n\n

By default, this will reload the page using the browser cache (if exists)...

\n\n

If you'd like to do force reload the page, just pass a true value to reload method like below...

\n\n
window.location.reload(true);\n
\n\n

Also if you are already in window scope, you can get rid of window and do:

\n\n
location.reload();\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae0", + "creator": "Frank", + "createdAt": 1527073058000, + "text": "

Use onclick=\"return location.reload();\" within the button tag.

\n\n
<button id=\"refersh-page\" name=\"refersh-page\" type=\"button\" onclick=\"return location.reload();\">Refesh Page</button>\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae2", + "creator": "Unmitigated", + "createdAt": 1531247342000, + "text": "

Simple Javascript Solution:

\n\n
 location = location; \n
\n\n

\r\n
\r\n
<button onClick=\"location = location;\">Reload</button>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae3", + "creator": "J. Moreno", + "createdAt": 1532504355000, + "text": "

If you are using jQuery and want to refresh, then try adding your jQuery in a javascript function:

\n\n

I wanted to hide an iframe from a page when clicking oh an h3, for me it worked but I wasn't able to click the item that allowed me to view the iframe to begin with unless I refreshed the browser manually...not ideal.

\n\n

I tried the following:

\n\n
var hide = () => {\n    $(\"#frame\").hide();//jQuery\n    location.reload(true);//javascript\n};\n
\n\n

Mixing plain Jane javascript with your jQuery should work.

\n\n
// code where hide (where location.reload was used)function was integrated, below    \n    iFrameInsert = () => {\n        var file = `Fe1FVoW0Nt4`;\n        $(\"#frame\").html(`<iframe width=\\\"560\\\" height=\\\"315\\\" src=\\\"https://www.youtube.com/embed/${file}\\\" frameborder=\\\"0\\\" allow=\\\"autoplay; encrypted-media\\\" allowfullscreen></iframe><h3>Close Player</h3>`);\n        $(\"h3\").enter code hereclick(hide);\n}\n\n// View Player \n$(\"#id-to-be-clicked\").click(iFrameInsert);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae5", + "creator": "Roshana Pitigala", + "createdAt": 1538495847000, + "text": "

All the answers here are good. Since the question specifies about reloading the page with jquery, I just thought adding something more for future readers.

\n
\n

jQuery is a cross-platform JavaScript library designed to simplify the client-side scripting of HTML.

\n

~ Wikipedia ~

\n
\n

So you'll understand that the foundation of jquery, or jquery is based on javascript. So going with pure javascript is way better when it comes to simple things.

\n

But if you need a jquery solution, here's one.

\n
$(location).attr('href', '');\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae4", + "creator": "dcangulo", + "createdAt": 1537340176000, + "text": "

Here are some lines of code you can use to reload the page using jQuery.

\n\n

It uses the jQuery wrapper and extracts the native dom element.

\n\n

Use this if you just want a jQuery feeling on your code and you don't care about speed/performance of the code.

\n\n

Just pick from 1 to 10 that suits your needs or add some more based on the pattern and answers before this.

\n\n
<script>\n  $(location)[0].reload(); //1\n  $(location).get(0).reload(); //2\n  $(window)[0].location.reload(); //3\n  $(window).get(0).location.reload(); //4\n  $(window)[0].$(location)[0].reload(); //5\n  $(window).get(0).$(location)[0].reload(); //6\n  $(window)[0].$(location).get(0).reload(); //7\n  $(window).get(0).$(location).get(0).reload(); //8\n  $(location)[0].href = ''; //9\n  $(location).get(0).href = ''; //10\n  //... and many other more just follow the pattern.\n</script>\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae6", + "creator": "Na Nonthasen", + "createdAt": 1540605961000, + "text": "

It is shortest in JavaScript.

\n\n
window.location = '';\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae7", + "creator": "H.Ostwal", + "createdAt": 1549288767000, + "text": "

use

\n\n
location.reload();\n
\n\n

or

\n\n
window.location.reload();\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3275c082fcc3049e92419", + "creator": "Romain Valeri", + "createdAt": 1549302772000, + "text": "OP mentioned specifically "with jQuery" (even though as already noted by many, this maybe should not be a jQuery question)", + "upvotes": 465, + "upvoterUsernames": [], + "downvotes": 465, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90ae9", + "creator": "Kamil Kiełczewski", + "createdAt": 1572252081000, + "text": "

Probably shortest (12 chars) - use history

\n\n
history.go()\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90ae8", + "creator": "ankitkanojia", + "createdAt": 1568715567000, + "text": "

There are many ways to reload the current pages, but somehow using those approaches you can see page updated but not with few cache values will be there, so overcome that issue or if you wish to make hard requests then use the below code.

\n\n
    location.reload(true);\n    //Here, it will make a hard request or reload the current page and clear the cache as well.\n\n\n    location.reload(false); OR location.reload();\n    //It can be reload the page with cache\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aeb", + "creator": "ankit singh", + "createdAt": 1574934792000, + "text": "
<i id=\"refresh\" class=\"fa fa-refresh\" aria-hidden=\"true\"></i>\n\n<script>\n$(document).on('click','#refresh',function(){\n   location.reload(true);\n});\n</script>\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aea", + "creator": "infomasud", + "createdAt": 1573158052000, + "text": "

You can write it in two ways. 1st is the standard way of reloading the page also called as simple refresh

\n\n
location.reload(); //simple refresh\n
\n\n

And another is called the hard refresh. Here you pass the boolean expression and set it to true. This will reload the page destroying the older cache and displaying the contents from scratch.

\n\n
location.reload(true);//hard refresh\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aed", + "creator": "user15255379", + "createdAt": 1619373007000, + "text": "

Y'all may need to use

\n
    location.reload(forceGet)\n
\n

forceGet is a boolean and optional.

\n

The default is false, which reloads the page of the cache.

\n", + "upvotes": 583, + "upvoterUsernames": [], + "downvotes": 583, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aec", + "creator": "Ankush Kumar", + "createdAt": 1583395888000, + "text": "
$(document).on(\"click\", \"#refresh_btn\", function(event) \n{\n     window.location.replace(window.location.href);\n});\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aee", + "creator": "Tharindu Lakshan", + "createdAt": 1619373184000, + "text": "

you may need to use

\n
 location.reload()\n
\n

or also may need to use

\n
location.reload(forceGet)\n
\n

forceGet is a boolean and optional.

\n

Set this parameter to true if you want to force the browser to take the page from the server to receive rid of the cache as well

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90aef", + "creator": "Pri Nce", + "createdAt": 1636969461000, + "text": "

This works for me.

\n
function reload(){\n    location.reload(true);\n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90ace", + "creator": "ˈvɔlə", + "createdAt": 1407160592000, + "text": "Since jQuery is a javascript framework for easy DOM-manipulation and event-binding, I would recommend you to ask for javascript instead of jQuery.", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90acf", + "creator": "Stardust", + "createdAt": 1451671162000, + "text": "You don't need jQuery for this.", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90ad0", + "creator": "Kartik Chugh", + "createdAt": 1502423736000, + "text": "More relevant than ever: needsmorejquery.com", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90ad1", + "creator": "I am the Most Stupid Person", + "createdAt": 1563511369000, + "text": "@Stardust You don't need jQuery for anything which you do with jQuery because of everything possible with JavaScript if you can do it jQuery.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2934780, + "uvac": 2934810 + } + }, + { + "_id": "62f321bb082fcc3049e8fee3", + "title": "Is there a standard function to check for null, undefined, or blank variables in JavaScript?", + "title-lowercase": "is there a standard function to check for null, undefined, or blank variables in javascript?", + "creator": "Alex", + "createdAt": 1301670864000, + "status": "open", + "text": "

Is there a universal JavaScript function that checks that a variable has a value and ensures that it's not undefined or null? I've got this code, but I'm not sure if it covers all cases:

\n\n
function isEmpty(val){\n    return (val === undefined || val == null || val.length <= 0) ? true : false;\n}\n
\n", + "upvotes": 4321, + "upvoterUsernames": [], + "downvotes": 1464, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2573129, + "answers": 44, + "answerItems": [ + { + "_id": "62f321c7082fcc3049e90a00", + "creator": "Mike Samuel", + "createdAt": 1301671180000, + "text": "
function isEmpty(value){\n  return (value == null || value.length === 0);\n}\n
\n\n

This will return true for

\n\n
undefined  // Because undefined == null\n\nnull\n\n[]\n\n\"\"\n
\n\n

and zero argument functions since a function's length is the number of declared parameters it takes.

\n\n

To disallow the latter category, you might want to just check for blank strings

\n\n
function isEmpty(value){\n  return (value == null || value === '');\n}\n
\n", + "upvotes": 139, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f326ce082fcc3049e9223c", + "creator": "Ian Boyd", + "createdAt": 1393081573000, + "text": "undefined == null but undefined !== null", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e909ff", + "creator": "jAndy", + "createdAt": 1301671064000, + "text": "

You can just check if the variable has a truthy value or not. That means

\n
if( value ) {\n}\n
\n

will evaluate to true if value is not:

\n\n

The above list represents all possible falsy values in ECMA-/Javascript. Find it in the specification at the ToBoolean section.

\n

Furthermore, if you do not know whether a variable exists (that means, if it was declared) you should check with the typeof operator. For instance

\n
if( typeof foo !== 'undefined' ) {\n    // foo could get resolved and it's defined\n}\n
\n

If you can be sure that a variable is declared at least, you should directly check if it has a truthy value like shown above.

\n", + "upvotes": 10273, + "upvoterUsernames": [], + "downvotes": 4858, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f326ce082fcc3049e9223f", + "creator": "Christophe Roussy", + "createdAt": 1416565738000, + "text": "Also note that this will not check for strings which only contain whitespace characters.", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92240", + "creator": "Aaron Gray", + "createdAt": 1423706062000, + "text": "Wow. JavaScript is prettier than Ruby for once. What is the world coming to?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92242", + "creator": "Brad", + "createdAt": 1434575454000, + "text": "any IE10 version of this?", + "upvotes": 2825, + "upvoterUsernames": [], + "downvotes": 2825, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92244", + "creator": "BentOnCoding", + "createdAt": 1437672041000, + "text": "This is a really simple answer that will lead to a lot of false positives. You need to be more specific when checking values.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92246", + "creator": "x0n", + "createdAt": 1453180029000, + "text": "apparently the latest fashion is to test for undefined against (void 0) as undefined may well be a legitimate variable in scope.", + "upvotes": 120, + "upvoterUsernames": [], + "downvotes": 120, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92248", + "creator": "Jim", + "createdAt": 1455127280000, + "text": "I wonder if this reference to "truthiness" predates Stephen Colbert's coinage of the term?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92249", + "creator": "iaforek", + "createdAt": 1507126998000, + "text": "If the value is either empty object {} or empty array [] this will return true.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9224b", + "creator": "Sam Alexander", + "createdAt": 1508531101000, + "text": "undefined values resolve as undefined in this case, not false", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9224d", + "creator": "user378380", + "createdAt": 1551917640000, + "text": "This is definitely what you don't want to do, it will return true too often. Definite javascript mistake.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9224f", + "creator": "Ed of the Mountain", + "createdAt": 1565036671000, + "text": "How to include test for an empty object? const emptyObject = {}, will return true if tested.", + "upvotes": 3477, + "upvoterUsernames": [], + "downvotes": 3477, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92251", + "creator": "Nicholas Hamilton", + "createdAt": 1571538763000, + "text": "I cant believe this answer got 4000 votes and marked as correct. null, undefined and false(y) are not the same.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92253", + "creator": "Dulguun Otgon", + "createdAt": 1573696043000, + "text": "Also remember that !!"0" === true, sometimes it sneaks in like when you get it from <input>", + "upvotes": 679, + "upvoterUsernames": [], + "downvotes": 679, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92255", + "creator": "Leon", + "createdAt": 1592639351000, + "text": "what about "null"?", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92257", + "creator": "Omar bakhsh", + "createdAt": 1610897707000, + "text": "in es6 val?doSomthing():null;", + "upvotes": 77, + "upvoterUsernames": [], + "downvotes": 77, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92258", + "creator": "Jonathan", + "createdAt": 1612445959000, + "text": "0 is pretty much always considered defined and the most common source of bugs with this type of code. Seen it way too many times.", + "upvotes": 2446, + "upvoterUsernames": [], + "downvotes": 2446, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92259", + "creator": "fcaserio", + "createdAt": 1614957985000, + "text": "this method dont work if the variable is zero (0)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9225b", + "creator": "jAndy", + "createdAt": 1615329462000, + "text": "@fcaserio The value 0 is mentioned in the original post.", + "upvotes": 318, + "upvoterUsernames": [], + "downvotes": 318, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9225c", + "creator": "Switch386", + "createdAt": 1623870143000, + "text": "this doesn't really answer the question", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e9225e", + "creator": "AndreasS", + "createdAt": 1639861530000, + "text": "Does not answer question. How about false, 0? Event the proposal in the question is better.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f326ce082fcc3049e92260", + "creator": "Phate", + "createdAt": 1648203069000, + "text": "what about the opposite check?", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a07", + "creator": "Arif", + "createdAt": 1421996168000, + "text": "

! check for empty strings (\"\"), null, undefined, false and the number 0 and NaN. Say, if a string is empty var name = \"\" then console.log(!name) returns true.

\n\n
function isEmpty(val){\n  return !val;\n}\n
\n\n

this function will return true if val is empty, null, undefined, false, the number 0 or NaN.

\n\n

OR

\n\n

According to your problem domain you can just use like !val or !!val.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270b082fcc3049e92262", + "creator": "Allen Linatoc", + "createdAt": 1460681098000, + "text": "Why use isEmpty(val) if you could just do !val?", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a08", + "creator": "Vix", + "createdAt": 1423737163000, + "text": "

You may find the following function useful:

\n\n
function typeOf(obj) {\n  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();\n}\n
\n\n

Or in ES7 (comment if further improvements)

\n\n
function typeOf(obj) {\n  const { toString } = Object.prototype;\n  const stringified = obj::toString();\n  const type = stringified.split(' ')[1].slice(0, -1);\n\n  return type.toLowerCase();\n}\n
\n\n

Results:

\n\n
typeOf(); //undefined\ntypeOf(null); //null\ntypeOf(NaN); //number\ntypeOf(5); //number\ntypeOf({}); //object\ntypeOf([]); //array\ntypeOf(''); //string\ntypeOf(function () {}); //function\ntypeOf(/a/) //regexp\ntypeOf(new Date()) //date\ntypeOf(new WeakMap()) //weakmap\ntypeOf(new Map()) //map\n
\n\n

\"Note that the bind operator (::) is not part of ES2016 (ES7) nor any later edition of the ECMAScript standard at all. It's currently a stage 0 (strawman) proposal for being introduced to the language.\" – Simon Kjellberg. the author wishes to add his support for this beautiful proposal to receive royal ascension.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270b082fcc3049e92265", + "creator": "GollyJer", + "createdAt": 1504412812000, + "text": "@Vix, why is the ES7 version any better?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270b082fcc3049e92267", + "creator": "Vix", + "createdAt": 1504445842000, + "text": "It isn't, was experimenting with more readable ways of expressing the same functionality making use of: destructuring assignment, bind operator.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a09", + "creator": "JerryP", + "createdAt": 1430514109000, + "text": "

This condition check

\n\n
if (!!foo) {\n    //foo is defined\n}\n
\n\n

is all you need.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270b082fcc3049e92269", + "creator": "Gorky", + "createdAt": 1592804208000, + "text": "Unless foo is 0", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a0a", + "creator": "l3x", + "createdAt": 1440164380000, + "text": "

If you prefer plain javascript try this:

\n\n
  /**\n   * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a\n   * length of `0` and objects with no own enumerable properties are considered\n   * \"empty\".\n   *\n   * @static\n   * @memberOf _\n   * @category Objects\n   * @param {Array|Object|string} value The value to inspect.\n   * @returns {boolean} Returns `true` if the `value` is empty, else `false`.\n   * @example\n   *\n   * _.isEmpty([1, 2, 3]);\n   * // => false\n   *\n   * _.isEmpty([]);\n   * // => true\n   *\n   * _.isEmpty({});\n   * // => true\n   *\n   * _.isEmpty('');\n   * // => true\n   */\n\nfunction isEmpty(value) {\n    if (!value) {\n      return true;\n    }\n    if (isArray(value) || isString(value)) {\n      return !value.length;\n    }\n    for (var key in value) {\n      if (hasOwnProperty.call(value, key)) {\n        return false;\n      }\n    }\n    return true;\n  }\n
\n\n

Otherwise, if you are already using underscore or lodash, try:

\n\n
_.isEmpty(value)\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270b082fcc3049e9226c", + "creator": "kalyanbk", + "createdAt": 1493062099000, + "text": "This would fail if value is boolean and has the value true.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270b082fcc3049e9226e", + "creator": "GFoley83", + "createdAt": 1508096970000, + "text": "Plain javascript doesn't have isArray or isString functions on the window.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3270b082fcc3049e92270", + "creator": "Ry-", + "createdAt": 1554390067000, + "text": "@l3x: Is that a joke?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a0c", + "creator": "rickdog", + "createdAt": 1472939350000, + "text": "

This will check if variable of indeterminate nesting is undefined

\n\n
function Undef(str) \n{\n  var ary = str.split('.');\n  var w = window;\n  for (i in ary) {\n    try      { if (typeof(w = w[ary[i]]) === \"undefined\") return true; }\n    catch(e) { return true; }\n  }\n  return false;\n}\n\nif (!Undef(\"google.translate.TranslateElement\")) {\n
\n\n

The above checks if the Google translate function TranslateElement exists. This is equivalent to:

\n\n
if (!(typeof google === \"undefined\" \n || typeof google.translate === \"undefined\" \n || typeof google.translate.TranslateElement === \"undefined\")) {\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a0b", + "creator": "Pedro Pereira", + "createdAt": 1467279081000, + "text": "

I think using the ? operator is slightly cleaner.

\n\n
var ? function_if_exists() : function_if_doesnt_exist();\n
\n", + "upvotes": 737, + "upvoterUsernames": [], + "downvotes": 737, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a0e", + "creator": "Mark Giblin", + "createdAt": 1489139831000, + "text": "

Although an oldie, what forget is that they should wrap their code block and then catch the error and then test...

\n\n
function checkup( t ){\n  try{\n    for(p in t){\n      if( p.hasOwnProperty( t ) ){\n        return true;\n      }\n    }\n    return false;\n  }catch(e){\n    console.log(\"ERROR : \"+e);\n    return e;\n  }\n}\n
\n\n

So you really don't have to check for a potential problem before hand, you simply catch it and then deal with it how you want.

\n", + "upvotes": 198, + "upvoterUsernames": [], + "downvotes": 198, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a0d", + "creator": "Hriju", + "createdAt": 1475752709000, + "text": "

For my case I tried with if null,'', !variable, But it did not work.

\n\n

See my code below to get the text from an html field

\n\n
var status=$(this).text(); //for example (for my case)\n
\n\n

if there was no value(no text) in the status variable ,I was trying to set the value 'novalue' to status variable.

\n\n

the following code worked.

\n\n
if(status == false)\n{\n   status='novalue';\n} \n
\n\n

when there was no text found for satus variable the above code assigned 'novalue' to the status variable

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a10", + "creator": "Keith Blanchard", + "createdAt": 1497991482000, + "text": "
var myNewValue = myObject && myObject.child && myObject.child.myValue;\n
\n\n

This will never throw an error. If myObject, child, or myValue is null then myNewValue will be null. No errors will be thrown

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a0f", + "creator": "Luca C.", + "createdAt": 1496392226000, + "text": "
function isEmpty(val){\n    return !val;\n}\n
\n\n

but this solution is over-engineered, if you dont'want to modify the function later for busines-model needings, then is cleaner to use it directly in code:

\n\n
if(!val)...\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a11", + "creator": "centurian", + "createdAt": 1512206853000, + "text": "

For everyone coming here for having similar question, the following works great and I have it in my library the last years:

\n\n
(function(g3, $, window, document, undefined){\n   g3.utils = g3.utils || {};\n/********************************Function type()********************************\n* Returns a lowercase string representation of an object's constructor.\n* @module {g3.utils}\n* @function {g3.utils.type}\n* @public\n* @param {Type} 'obj' is any type native, host or custom.\n* @return {String} Returns a lowercase string representing the object's \n* constructor which is different from word 'object' if they are not custom.\n* @reference http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/\n* http://stackoverflow.com/questions/3215046/differentiating-between-arrays-and-hashes-in-javascript\n* http://javascript.info/tutorial/type-detection\n*******************************************************************************/\ng3.utils.type = function (obj){\n   if(obj === null)\n      return 'null';\n   else if(typeof obj === 'undefined')\n      return 'undefined';\n   return Object.prototype.toString.call(obj).match(/^\\[object\\s(.*)\\]$/)[1].toLowerCase();\n};\n}(window.g3 = window.g3 || {}, jQuery, window, document));\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a12", + "creator": "ddagsan", + "createdAt": 1513145344000, + "text": "

It may be usefull.

\n\n

All values in array represent what you want to be (null, undefined or another things) and you search what you want in it.

\n\n
var variablesWhatILookFor = [null, undefined, ''];\nvariablesWhatILookFor.indexOf(document.DocumentNumberLabel) > -1\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270c082fcc3049e92278", + "creator": "JoshKisb", + "createdAt": 1515265664000, + "text": "could you explain please. what is going on there", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e9227a", + "creator": "ddagsan", + "createdAt": 1544530053000, + "text": "The array contains some variable that you assumed as empty.", + "upvotes": 1886, + "upvoterUsernames": [], + "downvotes": 1886, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e9227c", + "creator": "ddagsan", + "createdAt": 1560508186000, + "text": "@JoshKisb all values in array represent what you want to be (null, undefined or another things) and you search what you want in it.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e9227d", + "creator": "Nick stands with Ukraine", + "createdAt": 1560508353000, + "text": "@ddagsan While JoshKisb may appreciate your reply, you should put your explaination in your answer rather than in the comments", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a13", + "creator": "GorvGoyl", + "createdAt": 1519821697000, + "text": "

This function check for empty object {},empty array [], null, undefined and blank string \"\"

\n\n
function isEmpty(val) {\n  //check for empty object {}, array []\n  if (val !== null && typeof val === 'object') {\n    if (Object.keys(obj).length === 0) {\n      return true;\n    }\n  }\n  //check for undefined, null and \"\" \n  else if (val == null || val === \"\") {\n    return true;\n  }\n  return false;\n}\n
\n\n
\n

var val={};
\n isEmpty(val) -> true
\n val=[];
\n isEmpty(val) -> true
\n isEmpty(undefined) -> true
\n isEmpty(null) -> true
\n isEmpty(\"\") -> true
\n isEmpty(false) -> false
\n isEmpty(0) -> false

\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a14", + "creator": "jonathan klevin", + "createdAt": 1522309818000, + "text": "

You can directly use the equality operator

\n\n
<script>\n    var firstName;\n    var lastName = null;\n    /* Since null == undefined is true, the following statements will catch both null and undefined */\n        if(firstName == null){\n            alert('Variable \"firstName\" is undefined.');\n        }    \n        if(lastName == null){\n           alert('Variable \"lastName\" is null.');\n        }\n</script>\n
\n\n

demo @ How to determine if variable is undefined or null using JavaScript

\n", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 248, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a15", + "creator": "Ravikant", + "createdAt": 1525772680000, + "text": "

Try With Different Logic. You can use bellow code for check all four(4) condition for validation like not null, not blank, not undefined and not zero only use this code (!(!(variable))) in javascript and jquery.

\n\n
function myFunction() {\n    var data;  //The Values can be like as null, blank, undefined, zero you can test\n\n    if(!(!(data)))\n    {\n        alert(\"data \"+data);\n    } \n    else \n    {\n        alert(\"data is \"+data);\n    }\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a16", + "creator": "jales cardoso", + "createdAt": 1526648919000, + "text": "
function isEmpty(obj) {\n    if (typeof obj == 'number') return false;\n    else if (typeof obj == 'string') return obj.length == 0;\n    else if (Array.isArray(obj)) return obj.length == 0;\n    else if (typeof obj == 'object') return obj == null || Object.keys(obj).length == 0;\n    else if (typeof obj == 'boolean') return false;\n    else return !obj;\n}\n
\n\n

In ES6 with trim to handle whitespace strings:

\n\n
const isEmpty = value => {\n    if (typeof value === 'number') return false\n    else if (typeof value === 'string') return value.trim().length === 0\n    else if (Array.isArray(value)) return value.length === 0\n    else if (typeof value === 'object') return value == null || Object.keys(value).length === 0\n    else if (typeof value === 'boolean') return false\n    else return !value\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270c082fcc3049e92282", + "creator": "Fabian von Ellerts", + "createdAt": 1538743468000, + "text": "great function, thank you! handles every type of value - numbers are left out in all other solutions!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a18", + "creator": "nobjta_9x_tq", + "createdAt": 1538365342000, + "text": "
try{\n\n     let vari = obj.propTest; // obj may be don't have propTest property\n\n        ...\n} catch(NullException){\n    // do something here\n}\n
\n\n

I think using try catch will avoid any error of null check, also in Angular or JavaScript\nJust catching null exception and process in it.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270c082fcc3049e92285", + "creator": "Gufran Hasan", + "createdAt": 1538380836000, + "text": "You should add an explanation to justify your answer.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e92287", + "creator": "nobjta_9x_tq", + "createdAt": 1538389603000, + "text": "I tried to code in Angular with some solutions in this question, but I saw that try catch is safe way to process in this case :D", + "upvotes": 2562, + "upvoterUsernames": [], + "downvotes": 2562, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a17", + "creator": "BlackBeard", + "createdAt": 1530200035000, + "text": "

If you are using TypeScript and don't want to account for \"values those are false\" then this is the solution for you:

\n\n

First: import { isNullOrUndefined } from 'util';

\n\n

Then: isNullOrUndefined(this.yourVariableName)

\n\n

Please Note: As mentioned below this is now deprecated, use value === undefined || value === null instead. ref.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270c082fcc3049e92289", + "creator": "BlackBeard", + "createdAt": 1538536148000, + "text": "@atomictom I thought its a typescript thing. Can you please provide the link of its documentation?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e9228b", + "creator": "ticktock", + "createdAt": 1579644078000, + "text": "Why would the deprecate a useful simple thing like this? geeeeees.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a1a", + "creator": "Alexandre Magro", + "createdAt": 1544651186000, + "text": "

A solution I like a lot:

\n\n

Let's define that a blank variable is null, or undefined, or if it has length, it is zero, or if it is an object, it has no keys:

\n\n
function isEmpty (value) {\n  return (\n    // null or undefined\n    (value == null) ||\n\n    // has length and it's zero\n    (value.hasOwnProperty('length') && value.length === 0) ||\n\n    // is an Object and has no keys\n    (value.constructor === Object && Object.keys(value).length === 0)\n  )\n}\n
\n\n

Returns:

\n\n\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a19", + "creator": "cubefox", + "createdAt": 1543927491000, + "text": "
return val || 'Handle empty variable'\n
\n\n

is a really nice and clean way to handle it in a lot of places, can also be used to assign variables

\n\n
const res = val || 'default value'\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270c082fcc3049e9228e", + "creator": "Molomby", + "createdAt": 1553042554000, + "text": "A lot of places but not when the default is true and you're trying to supply or return a val of false.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3270c082fcc3049e92290", + "creator": "cubefox", + "createdAt": 1553176256000, + "text": "@Molomby that's a very specific edge case but even that is easily handled const res = falsyValue ? true : falsyValue", + "upvotes": 4371, + "upvoterUsernames": [], + "downvotes": 4371, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a1b", + "creator": "yaserso", + "createdAt": 1571898416000, + "text": "

If you want to avoid getting true if the value is any of the following, according to jAndy's answer:

\n\n\n\n

One possible solution that might avoid getting truthy values is the following:

\n\n
function isUsable(valueToCheck) {\n    if (valueToCheck === 0     || // Avoid returning false if the value is 0.\n        valueToCheck === ''    || // Avoid returning false if the value is an empty string.\n        valueToCheck === false || // Avoid returning false if the value is false.\n        valueToCheck)             // Returns true if it isn't null, undefined, or NaN.\n    {\n        return true;\n    } else {\n        return false;\n    }\n}\n
\n\n

It would be used as follows:

\n\n
if (isUsable(x)) {\n    // It is usable!\n}\n// Make sure to avoid placing the logical NOT operator before the parameter (isUsable(!x)) and instead, use it before the function, to check the returned value.\nif (!isUsable(x)) {\n    // It is NOT usable!\n}\n
\n\n
\n\n

In addition to those scenarios, you may want to return false if the object or array is empty:

\n\n\n\n

You would go about it this way:

\n\n
function isEmptyObject(valueToCheck) {\n    if(typeof valueToCheck === 'object' && !Object.keys(valueToCheck).length){\n        // Object is empty!\n        return true;\n    } else {\n        // Object is not empty!\n        return false;\n    }\n}\n\nfunction isEmptyArray(valueToCheck) {\n    if(Array.isArray(valueToCheck) && !valueToCheck.length) {\n        // Array is empty!\n        return true;\n    } else {\n        // Array is not empty!\n        return false;\n    }\n}\n
\n\n

If you wish to check for all whitespace strings (\" \"), you may do the following:

\n\n
function isAllWhitespace(){\n    if (valueToCheck.match(/^ *$/) !== null) {\n        // Is all whitespaces!\n        return true;\n    } else {\n        // Is not all whitespaces!\n        return false;\n    }\n}\n
\n\n
\n\n

Note: hasOwnProperty returns true for empty strings, 0, false, NaN, null, and undefined, if the variable was declared as any of them, so it might not be the best to use. The function may be modified to use it to show that it was declared, but is not usable.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a1c", + "creator": "Narasimha Reddy - Geeker", + "createdAt": 1578568413000, + "text": "

This covers empty Array and empty Object also

\n\n
\n

null, undefined, ' ', 0, [ ], { }

\n
\n\n
isEmpty = (value) => (!value  || (typeof v === 'object' &&\n   Object.keys(value).length < 1));\n
\n", + "upvotes": 160, + "upvoterUsernames": [], + "downvotes": 160, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a1d", + "creator": "Daniel Delgado", + "createdAt": 1583782114000, + "text": "
\n

Take a look at the new ECMAScript Nullish coalescing operator

\n
\n\n

You can think of this feature - the ?? operator - as a way to “fall back” to a default value when dealing with null or undefined.

\n\n
let x = foo ?? bar();\n
\n\n\n\n

Again, the above code is equivalent to the following.

\n\n
let x = (foo !== null && foo !== undefined) ? foo : bar();\n
\n\n\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a1e", + "creator": "Pascal Polleunus", + "createdAt": 1585442725000, + "text": "

Code on GitHub

\n\n
const isEmpty = value => (\n  (!value && value !== 0 && value !== false)\n  || (Array.isArray(value) && value.length === 0)\n  || (isObject(value) && Object.keys(value).length === 0)\n  || (typeof value.size === 'number' && value.size === 0)\n\n  // `WeekMap.length` is supposed to exist!?\n  || (typeof value.length === 'number'\n      && typeof value !== 'function' && value.length === 0)\n);\n\n// Source: https://levelup.gitconnected.com/javascript-check-if-a-variable-is-an-object-and-nothing-else-not-an-array-a-set-etc-a3987ea08fd7\nconst isObject = value =>\n  Object.prototype.toString.call(value) === '[object Object]';\n
\n\n

Poor man's tests 😁

\n\n
const test = () => {\n  const run = (label, values, expected) => {\n    const length = values.length;\n    console.group(`${label} (${length} tests)`);\n    values.map((v, i) => {\n      console.assert(isEmpty(v) === expected, `${i}: ${v}`);\n    });\n    console.groupEnd();\n  };\n\n  const empty = [\n    null, undefined, NaN, '', {}, [],\n    new Set(), new Set([]), new Map(), new Map([]),\n  ];\n  const notEmpty = [\n    ' ', 'a', 0, 1, -1, false, true, {a: 1}, [0],\n    new Set([0]), new Map([['a', 1]]),\n    new WeakMap().set({}, 1),\n    new Date(), /a/, new RegExp(), () => {},\n  ];\n  const shouldBeEmpty = [\n    {undefined: undefined}, new Map([[]]),\n  ];\n\n  run('EMPTY', empty, true);\n  run('NOT EMPTY', notEmpty, false);\n  run('SHOULD BE EMPTY', shouldBeEmpty, true);\n};\n
\n\n

Test results:

\n\n
EMPTY (10 tests)\nNOT EMPTY (16 tests)\nSHOULD BE EMPTY (2 tests)\n  Assertion failed: 0: [object Object]\n  Assertion failed: 1: [object Map]\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a20", + "creator": "Sean Bannister", + "createdAt": 1587493727000, + "text": "

Most of the existing answers failed for my use case, most returned empty if a function was assigned to the variable or if NaN was returned. Pascal's answer was good.

\n\n

Here's my implementation, please test and let me know if you find anything. You can see how I tested this function here.

\n\n
function isEmpty(value) {\n  return (\n    // Null or undefined.\n    (value == null) ||\n    // Check if a Set() or Map() is empty\n    (value.size === 0) ||\n    // NaN - The only JavaScript value that is unequal to itself.\n    (value !== value) ||\n    // Length is zero && it's not a function.\n    (value.length === 0 && typeof value !== \"function\") ||\n    // Is an Object && has no keys.\n    (value.constructor === Object && Object.keys(value).length === 0)\n  )\n}\n
\n\n

Returns:

\n\n\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a1f", + "creator": "Shahid Hussain Abbasi", + "createdAt": 1587019155000, + "text": "

Below worked for me. Please do a slight change to make this fast

\n\n
function isEmpty(obj) {\n    if (!obj) return true;\n    if (typeof obj == 'number') return false;\n    else if (typeof obj == 'string') return obj.length == 0;\n    else if (Array.isArray(obj)) return obj.length == 0;\n    else if (typeof obj == 'object') return obj == null || Object.keys(obj).length == 0;\n    else if (typeof obj == 'boolean') return false;\n}\n
\n", + "upvotes": 205, + "upvoterUsernames": [], + "downvotes": 205, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a21", + "creator": "Sunny Sultan", + "createdAt": 1588760080000, + "text": "

The optional chaining operator provides a way to simplify accessing values through connected objects when it's possible that a reference or function may be undefined or null.

\n\n
let customer = {\n  name: \"Carl\",\n  details: {\n    age: 82,\n    location: \"Paradise Falls\" // detailed address is unknown\n  }\n};\nlet customerCity = customer.details?.address?.city;\n
\n\n

The nullish coalescing operator may be used after optional chaining in order to build a default value when none was found:

\n\n
let customer = {\n  name: \"Carl\",\n  details: { age: 82 }\n};\nconst customerCity = customer?.city ?? \"Unknown city\";\nconsole.log(customerCity); // Unknown city\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a22", + "creator": "piyush nath", + "createdAt": 1589221974000, + "text": "

you can always use loadash functions, like _.isNil or _.isUndefined. They are pretty easy to use.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270d082fcc3049e92298", + "creator": "Aurovrata", + "createdAt": 1600678404000, + "text": "your answer is not useful as it provides no documentation links towards the functions you mention.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a23", + "creator": "Hassan Ali Shahzad", + "createdAt": 1591107480000, + "text": "
function notEmpty(value){\n  return (typeof value !== 'undefined' && value.trim().length);\n}\n
\n\n

it will also check white spaces (' ') along with following:

\n\n\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a24", + "creator": "Shuvojit Saha", + "createdAt": 1592906619000, + "text": "

Try Boolean() and isNaN() (for number type) to check a variable has a value or not.

\n

\r\n
\r\n
function isEmpty(val) {\n  return typeof val === 'number' ? isNaN(val) : !Boolean(val);\n}\n\nvar emptyVals = [undefined, null, false, NaN, ''];\nemptyVals.forEach(v => console.log(isEmpty(v)));
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a26", + "creator": "Uladzislau Ulasenka", + "createdAt": 1608207029000, + "text": "

Will return false only for undefined and null:

\n

return value ?? false

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3270e082fcc3049e9229d", + "creator": "Pi Delport", + "createdAt": 1609867343000, + "text": "Not exactly: false ?? false === false", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e9229f", + "creator": "kli", + "createdAt": 1615236681000, + "text": "Yeah, this suggestion will return false for the false input as well...", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3270e082fcc3049e922a1", + "creator": "bnieland", + "createdAt": 1619783168000, + "text": "To be clear, this answer is incorrect in that , !(0 ?? false) === true", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a25", + "creator": "user7396942", + "createdAt": 1602613571000, + "text": "
    \n
  1. you can use the arguments
  2. \n
  3. become arguments to array
  4. \n
  5. filter
  6. \n
\n

\r\n
\r\n
function validateAttrs(arg1, arg2, arg3,arg4){\n    var args = Object.values(arguments);\n    return (args.filter(x=> x===null || !x)).length<=0\n}\nconsole.log(validateAttrs('1',2, 3, 4));\nconsole.log(validateAttrs('1',2, 3, null));\nconsole.log(validateAttrs('1',undefined, 3, 4));\nconsole.log(validateAttrs('1',2, '', 4));\nconsole.log(validateAttrs('1',2, 3, null));
\r\n
\r\n
\r\n

\n", + "upvotes": 562, + "upvoterUsernames": [], + "downvotes": 562, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a27", + "creator": "thenewjames", + "createdAt": 1623767420000, + "text": "

You could use the nullish coalescing operator ?? to check for null and undefined values. See the MDN Docs

\n
null ?? 'default string'; // returns "default string"\n\n0 ?? 42;  // returns 0\n\n(null || undefined) ?? "foo"; // returns "foo"\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a28", + "creator": "Yilmaz", + "createdAt": 1637436810000, + "text": "

this is my solution to check if data is empty or not.

\n
const _isEmpty = (data) => {\n  return (\n    // this way we can also check for undefined values. null==undefined is true\n    data == null ||\n    data == "" ||\n    (Array.isArray(data) && data.length === 0) ||\n    // we want {} to be false. we cannot use !! because !!{} turns to be true\n    // !!{}=true and !!{name:"yilmaz"}=true. !! does not work ofr objects\n    (data.constructor === Object && Object.keys(data).length === 0)\n\n  );\n};\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a2a", + "creator": "bnieland", + "createdAt": 1648760694000, + "text": "

Check for undefined and null ONLY using "nullish coalescing"

\n
if ((myVariable ?? undefined) !== undefined) {\n    // handle myVariable has a value, including 0 or ""\n}\nelse {\n    // handle undefined or null only\n}\n
\n

from chrome console

\n
{const x=undefined; (x ?? undefined) !== undefined}\nfalse\n\n{const x=null; (x ?? undefined) !== undefined}\nfalse\n\n{const x=0; (x ?? undefined) !== undefined}\ntrue\n\n{const x=""; (x ?? undefined) !== undefined}\ntrue\n\n{const x={}; (x ?? undefined) !== undefined}\ntrue\n\n{const x=[]; (x ?? undefined) !== undefined}\ntrue\n\n{const x="a"; (x ?? undefined) !== undefined}\ntrue\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a29", + "creator": "Pakpoom Tiwakornkit", + "createdAt": 1646563591000, + "text": "

I think this makes your code looks simpler

\n

To check if variable IS undefined or null

\n
var a=undefined, b=null, c='hello world', d;\nif(a !== (a ?? {})) { /**/ } // true\nif(b !== (b ?? {})) { /**/ } // true\nif(c !== (c ?? {})) { /**/ } // false\nif(d !== (d ?? {})) { /**/ } // true\n
\n

To check if variable is NOT undefined or null

\n
var a=undefined, b=null, c='hello world', d;\nif(a === (a ?? {})) { /**/ } // false\nif(b === (b ?? {})) { /**/ } // false\nif(c === (c ?? {})) { /**/ } // true\nif(d === (d ?? {})) { /**/ } // false\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a03", + "creator": "herostwist", + "createdAt": 1301671515000, + "text": "

If the variable hasn't been declared, you wont be able to test for undefined using a function because you will get an error.

\n\n
if (foo) {}\nfunction (bar) {}(foo)\n
\n\n

Both will generate an error if foo has not been declared.

\n\n

If you want to test if a variable has been declared you can use

\n\n
typeof foo != \"undefined\"\n
\n\n

if you want to test if foo has been declared and it has a value you can use

\n\n
if (typeof foo != \"undefined\" && foo) {\n    //code here\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a02", + "creator": "tcooc", + "createdAt": 1301671276000, + "text": "

You are a bit overdoing it. To check if a variable is not given a value, you would only need to check against undefined and null.

\n\n
function isEmpty(value){\n    return (typeof value === \"undefined\" || value === null);\n}\n
\n\n

This is assuming 0, \"\", and objects(even empty object and array) are valid \"values\".

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c7082fcc3049e90a01", + "creator": "Salman A", + "createdAt": 1301671256000, + "text": "

The verbose method to check if value is undefined or null is:

\n
return value === undefined || value === null;\n
\n

You can also use the == operator but this expects one to know all the rules:

\n
return value == null; // also returns true if value is undefined\n
\n", + "upvotes": 359, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c2082fcc3049e92e65", + "creator": "Bryan Downing", + "createdAt": 1412906174000, + "text": "arg == null is pretty common in my experience.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c2082fcc3049e92e67", + "creator": "x0n", + "createdAt": 1453180161000, + "text": "return value === (void 0) is safer than testing against undefined which may well be a legitimate variable in scope, sadly.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f329c2082fcc3049e92e68", + "creator": "Sharky", + "createdAt": 1476698243000, + "text": "@ChristiaanWesterbeek then your initial comment is wrong.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a04", + "creator": "guya", + "createdAt": 1384100034000, + "text": "

This is the safest check and I haven't seen it posted here exactly like that:

\n\n
if (typeof value !== 'undefined' && value) {\n    //deal with value'\n};\n
\n\n

It will cover cases where value was never defined, and also any of these:

\n\n\n\n

Edited: Changed to strict equality (!==) because it's the norm by now ;)

\n", + "upvotes": 104, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c2082fcc3049e92e6a", + "creator": "rmcsharry", + "createdAt": 1606865492000, + "text": "@guya This deserves a vote simply for the edit after 7 years! Especially given the above discussion back in the day ;) Kudos Sir!", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c2082fcc3049e92e6c", + "creator": "Lucio Crusca", + "createdAt": 1625570247000, + "text": "I think this is the most readable code. Among all answers, this should be the accepted one.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a06", + "creator": "krupar", + "createdAt": 1395235391000, + "text": "

The first answer with best rating is wrong. If value is undefined it will throw an exception in modern browsers. You have to use:

\n\n
if (typeof(value) !== \"undefined\" && value)\n
\n\n

or

\n\n
if (typeof value  !== \"undefined\" && value)\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c2082fcc3049e92e6f", + "creator": "shaosh", + "createdAt": 1433551906000, + "text": "I got the same error here. if(x), if(!x), if(!!x) will all throw error if x is undefined.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c2082fcc3049e92e71", + "creator": "Madbreaks", + "createdAt": 1517446429000, + "text": "if(value === 0) gameOver(); ;)", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [] + }, + { + "_id": "62f329c2082fcc3049e92e72", + "creator": "Madbreaks", + "createdAt": 1517446929000, + "text": "This answer is also wrong because it returns false when value is zero, which is not what op is looking for.", + "upvotes": 986, + "upvoterUsernames": [], + "downvotes": 986, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c7082fcc3049e90a05", + "creator": "DavidWainwright", + "createdAt": 1392219914000, + "text": "

Here's mine - returns true if value is null, undefined, etc or blank (ie contains only blank spaces):

\n\n
function stringIsEmpty(value) {\n\n    return value ? value.trim().length == 0 : true;\n\n}\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 8, + "commentItems": [ + { + "_id": "62f321c6082fcc3049e909de", + "creator": "Darren Griffith", + "createdAt": 1476398534000, + "text": "I made a fiddle for testing: jsfiddle.net/J7m7m/886", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909df", + "creator": "David Baucum", + "createdAt": 1501696552000, + "text": "Protip, never do (truthy statement) ? true : false;. Just do (truthy statement);.", + "upvotes": 257, + "upvoterUsernames": [], + "downvotes": 99, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e0", + "creator": "Madbreaks", + "createdAt": 1517446547000, + "text": "@GeorgeJempty not a dup, since the other answer asks about strings in particular, whereas this one asks about variables.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e1", + "creator": "Madbreaks", + "createdAt": 1517446997000, + "text": "Any correct answer to this question relies entirely on how you define "blank".", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e2", + "creator": "Jay", + "createdAt": 1548860089000, + "text": "@David out of curiosity, why not? I've used both with no apparent ill effects.", + "upvotes": 1159, + "upvoterUsernames": [], + "downvotes": 1159, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e3", + "creator": "Alex", + "createdAt": 1579429097000, + "text": "@frodo2975, thanks. Didn’t know you can convert a value to a Boolean this way in JavaScript. That’s the same as C++. Interesting", + "upvotes": 2776, + "upvoterUsernames": [], + "downvotes": 2776, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e4", + "creator": "Shahid Hussain Abbasi", + "createdAt": 1587019212000, + "text": "This become fail when input is boolean.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c6082fcc3049e909e5", + "creator": "Rodrigo Borba", + "createdAt": 1621988957000, + "text": "wouldn't if(variable == null || variable.length <= 0) do the trick? undefined results in TRUE null results in TRUE [] results in TRUE", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2577458, + "uvac": 2577502 + } + }, + { + "_id": "62f321bb082fcc3049e8feac", + "title": "How can I remove a specific item from an array?", + "title-lowercase": "how can i remove a specific item from an array?", + "creator": "Walker", + "createdAt": 1303597038000, + "status": "open", + "text": "

How do I remove a specific value from an array? Something like:

\n
array.remove(value);  // removes all elements with value\n
\n

I have to use core JavaScript. Frameworks are not allowed.

\n", + "upvotes": 14062, + "upvoterUsernames": [], + "downvotes": 3236, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 10274290, + "answers": 126, + "answerItems": [ + { + "_id": "62f321bc082fcc3049e8ffa3", + "creator": "Zirak", + "createdAt": 1303597228000, + "text": "

\r\n
\r\n
Array.prototype.removeByValue = function (val) {\n  for (var i = 0; i < this.length; i++) {\n    if (this[i] === val) {\n      this.splice(i, 1);\n      i--;\n    }\n  }\n  return this;\n}\n\nvar fruits = ['apple', 'banana', 'carrot', 'orange'];\nfruits.removeByValue('banana');\n\nconsole.log(fruits);\n// -> ['apple', 'carrot', 'orange']
\r\n
\r\n
\r\n

\n", + "upvotes": 382, + "upvoterUsernames": [], + "downvotes": 142, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa4", + "creator": "Peter Olson", + "createdAt": 1303597256000, + "text": "

I don't know how you are expecting array.remove(int) to behave. There are three possibilities I can think of that you might want.

\n

To remove an element of an array at an index i:

\n
array.splice(i, 1);\n
\n

If you want to remove every element with value number from the array:

\n
for (var i = array.length - 1; i >= 0; i--) {\n if (array[i] === number) {\n  array.splice(i, 1);\n }\n}\n
\n

If you just want to make the element at index i no longer exist, but you don't want the indexes of the other elements to change:

\n
delete array[i];\n
\n", + "upvotes": 2687, + "upvoterUsernames": [], + "downvotes": 1060, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32256082fcc3049e910e0", + "creator": "Mahbubul Islam", + "createdAt": 1658466400000, + "text": "Great options to do the remove task.", + "upvotes": 671, + "upvoterUsernames": [], + "downvotes": 671, + "downvoterUsernames": [] + }, + { + "_id": "62f32256082fcc3049e910e2", + "creator": "Cristian Traìna", + "createdAt": 1658477347000, + "text": "delete array[i] would degrade the performance of the application and is considered a bad pratice", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffa6", + "creator": "xavierm02", + "createdAt": 1303597965000, + "text": "

It depends on whether you want to keep an empty spot or not.

\n\n

If you do want an empty slot:

\n\n
array[index] = undefined;\n
\n\n

If you don't want an empty slot:

\n\n
//To keep the original:\n//oldArray = [...array];\n\n//This modifies the array.\narray.splice(index, 1);\n
\n\n

And if you need the value of that item, you can just store the returned array's element:

\n\n
var value = array.splice(index, 1)[0];\n
\n\n

If you want to remove at either end of the array, you can use array.pop() for the last one or array.shift() for the first one (both return the value of the item as well).

\n\n

If you don't know the index of the item, you can use array.indexOf(item) to get it (in a if() to get one item or in a while() to get all of them). array.indexOf(item) returns either the index or -1 if not found. 

\n", + "upvotes": 1109, + "upvoterUsernames": [], + "downvotes": 496, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa7", + "creator": "Loupax", + "createdAt": 1350555195000, + "text": "

If you want a new array with the deleted positions removed, you can always delete the specific element and filter out the array. It might need an extension of the array object for browsers that don't implement the filter method, but in the long term it's easier since all you do is this:

\n\n
var my_array = [1, 2, 3, 4, 5, 6];\ndelete my_array[4];\nconsole.log(my_array.filter(function(a){return typeof a !== 'undefined';}));\n
\n\n

It should display [1, 2, 3, 4, 6].

\n", + "upvotes": 135, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa5", + "creator": "Tom Wadley", + "createdAt": 1303597430000, + "text": "

Find the index of the array element you want to remove using indexOf, and then remove that index with splice.

\n
\n

The splice() method changes the contents of an array by removing\nexisting elements and/or adding new elements.

\n
\n

\r\n
\r\n
const array = [2, 5, 9];\n\nconsole.log(array);\n\nconst index = array.indexOf(5);\nif (index > -1) { // only splice array when item is found\n  array.splice(index, 1); // 2nd parameter means remove one item only\n}\n\n// array = [2, 9]\nconsole.log(array); 
\r\n
\r\n
\r\n

\n

The second parameter of splice is the number of elements to remove. Note that splice modifies the array in place and returns a new array containing the elements that have been removed.

\n
\n

For the reason of completeness, here are functions. The first function removes only a single occurrence (i.e. removing the first match of 5 from [2,5,9,1,5,8,5]), while the second function removes all occurrences:

\n

\r\n
\r\n
function removeItemOnce(arr, value) {\n  var index = arr.indexOf(value);\n  if (index > -1) {\n    arr.splice(index, 1);\n  }\n  return arr;\n}\n\nfunction removeItemAll(arr, value) {\n  var i = 0;\n  while (i < arr.length) {\n    if (arr[i] === value) {\n      arr.splice(i, 1);\n    } else {\n      ++i;\n    }\n  }\n  return arr;\n}\n// Usage\nconsole.log(removeItemOnce([2,5,9,1,5,8,5], 5))\nconsole.log(removeItemAll([2,5,9,1,5,8,5], 5))
\r\n
\r\n
\r\n

\n

In TypeScript, these functions can stay type-safe with a type parameter:

\n
function removeItem<T>(arr: Array<T>, value: T): Array<T> { \n  const index = arr.indexOf(value);\n  if (index > -1) {\n    arr.splice(index, 1);\n  }\n  return arr;\n}\n
\n", + "upvotes": 28598, + "upvoterUsernames": [], + "downvotes": 13205, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32256082fcc3049e910e7", + "creator": "Stefan Fabian", + "createdAt": 1606815844000, + "text": "@Andrew sets and arrays are two completely different collection types.", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32256082fcc3049e910e9", + "creator": "Rashid Iqbal", + "createdAt": 1610035473000, + "text": "function remove(item,array) { var new_array = [] new_ array = array.filter((ar)=> ar != item) return new_array }", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32256082fcc3049e910eb", + "creator": "mikep", + "createdAt": 1616501333000, + "text": "Return statement is misleading. Array is already modified. It can confuse that origin array will be untouched.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32256082fcc3049e910ed", + "creator": "deb", + "createdAt": 1617897808000, + "text": "Array.pull() is what humankind needs the most.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32256082fcc3049e910ef", + "creator": "GreenAsJade", + "createdAt": 1659480583000, + "text": "If only json supported sets, then we could use sets instead :(", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffa8", + "creator": "Saša", + "createdAt": 1356089552000, + "text": "

There are two major approaches

\n
    \n
  1. splice(): anArray.splice(index, 1);

    \n
     let fruits = ['Apple', 'Banana', 'Mango', 'Orange']\n let removed = fruits.splice(2, 1);\n // fruits is ['Apple', 'Banana', 'Orange']\n // removed is ['Mango']\n
    \n
  2. \n
  3. delete: delete anArray[index];

    \n
     let fruits = ['Apple', 'Banana', 'Mango', 'Orange']\n let removed = delete fruits(2);\n // fruits is ['Apple', 'Banana', undefined, 'Orange']\n // removed is true\n
    \n
  4. \n
\n

Be careful when you use the delete for an array. It is good for deleting attributes of objects, but not so good for arrays. It is better to use splice for arrays.

\n

Keep in mind that when you use delete for an array you could get wrong results for anArray.length. In other words, delete would remove the element, but it wouldn't update the value of the length property.

\n

You can also expect to have holes in index numbers after using delete, e.g. you could end up with having indexes 1, 3, 4, 8, 9, and 11 and length as it was before using delete. In that case, all indexed for loops would crash, since indexes are no longer sequential.

\n

If you are forced to use delete for some reason, then you should use for each loops when you need to loop through arrays. As the matter of fact, always avoid using indexed for loops, if possible. That way the code would be more robust and less prone to problems with indexes.

\n", + "upvotes": 413, + "upvoterUsernames": [], + "downvotes": 116, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffa9", + "creator": "zykadelic", + "createdAt": 1363166909000, + "text": "

Update: This method is recommended only if you cannot use ECMAScript 2015 (formerly known as ES6). If you can use it, other answers here provide much neater implementations.

\n\n
\n\n

This gist here will solve your problem, and also deletes all occurrences of the argument instead of just 1 (or a specified value).

\n\n
Array.prototype.destroy = function(obj){\n    // Return null if no objects were found and removed\n    var destroyed = null;\n\n    for(var i = 0; i < this.length; i++){\n\n        // Use while-loop to find adjacent equal objects\n        while(this[i] === obj){\n\n            // Remove this[i] and store it within destroyed\n            destroyed = this.splice(i, 1)[0];\n        }\n    }\n\n    return destroyed;\n}\n
\n\n

Usage:

\n\n
var x = [1, 2, 3, 3, true, false, undefined, false];\n\nx.destroy(3);         // => 3\nx.destroy(false);     // => false\nx;                    // => [1, 2, true, undefined]\n\nx.destroy(true);      // => true\nx.destroy(undefined); // => undefined\nx;                    // => [1, 2]\n\nx.destroy(3);         // => null\nx;                    // => [1, 2]\n
\n", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffaa", + "creator": "Ekramul Hoque", + "createdAt": 1364900170000, + "text": "

Check out this code. It works in every major browser.

\n

\r\n
\r\n
remove_item = function(arr, value) {\n var b = '';\n for (b in arr) {\n  if (arr[b] === value) {\n   arr.splice(b, 1);\n   break;\n  }\n }\n return arr;\n};\n\nvar array = [1,3,5,6,5,9,5,3,55]\nvar res = remove_item(array,5);\nconsole.log(res)
\r\n
\r\n
\r\n

\n", + "upvotes": 114, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffab", + "creator": "yckart", + "createdAt": 1368447727000, + "text": "

You can iterate over each array-item and splice it if it exist in your array.

\n\n
function destroy(arr, val) {\n    for (var i = 0; i < arr.length; i++) if (arr[i] === val) arr.splice(i, 1);\n    return arr;\n}\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32257082fcc3049e910f4", + "creator": "Renze de Waal", + "createdAt": 1390498448000, + "text": "destroy( [1,2,3,3,3,4,5], 3 ) returns [1,2,3,4,5]]. i should not be incremented when the array is spliced.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffac", + "creator": "Enrico", + "createdAt": 1375880275000, + "text": "

Create new array:

\n\n
var my_array = new Array();\n
\n\n

Add elements to this array:

\n\n
my_array.push(\"element1\");\n
\n\n

The function indexOf (returns index or -1 when not found):

\n\n
var indexOf = function(needle)\n{\n    if (typeof Array.prototype.indexOf === 'function') // Newer browsers\n    {\n        indexOf = Array.prototype.indexOf;\n    }\n    else // Older browsers\n    {\n        indexOf = function(needle)\n        {\n            var index = -1;\n\n            for (var i = 0; i < this.length; i++)\n            {\n                if (this[i] === needle)\n                {\n                    index = i;\n                    break;\n                }\n            }\n            return index;\n        };\n    }\n\n    return indexOf.call(this, needle);\n};\n
\n\n

Check index of this element (tested with Firefox and Internet Explorer 8 (and later)):

\n\n
var index = indexOf.call(my_array, \"element1\");\n
\n\n

Remove 1 element located at index from the array

\n\n
my_array.splice(index, 1);\n
\n", + "upvotes": 29, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffad", + "creator": "Ben Lesh", + "createdAt": 1376162483000, + "text": "

A friend was having issues in Internet Explorer 8 and showed me what he did. I told him it was wrong, and he told me he got the answer here. The current top answer will not work in all browsers (Internet Explorer 8 for example), and it will only remove the first occurrence of the item.

\n

Remove ALL instances from an array

\n
function removeAllInstances(arr, item) {\n   for (var i = arr.length; i--;) {\n     if (arr[i] === item) arr.splice(i, 1);\n   }\n}\n
\n

It loops through the array backwards (since indices and length will change as items are removed) and removes the item if it's found. It works in all browsers.

\n", + "upvotes": 736, + "upvoterUsernames": [], + "downvotes": 354, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffae", + "creator": "Jeff Noel", + "createdAt": 1376330199000, + "text": "

You can do a backward loop to make sure not to screw up the indexes, if there are multiple instances of the element.

\n

\r\n
\r\n
var myElement = \"chocolate\";\nvar myArray = ['chocolate', 'poptart', 'poptart', 'poptart', 'chocolate', 'poptart', 'poptart', 'chocolate'];\n\n/* Important code */\nfor (var i = myArray.length - 1; i >= 0; i--) {\n  if (myArray[i] == myElement) myArray.splice(i, 1);\n}\nconsole.log(myArray);
\r\n
\r\n
\r\n

\n

Live Demo

\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb0", + "creator": "slosd", + "createdAt": 1379555620000, + "text": "

There is no need to use indexOf or splice. However, it performs better if you only want to remove one occurrence of an element.

\n\n

Find and move (move):

\n\n
function move(arr, val) {\n  var j = 0;\n  for (var i = 0, l = arr.length; i < l; i++) {\n    if (arr[i] !== val) {\n      arr[j++] = arr[i];\n    }\n  }\n  arr.length = j;\n}\n
\n\n

Use indexOf and splice (indexof):

\n\n
function indexof(arr, val) {\n  var i;\n  while ((i = arr.indexOf(val)) != -1) {\n    arr.splice(i, 1);\n  }\n}\n
\n\n

Use only splice (splice):

\n\n
function splice(arr, val) {\n  for (var i = arr.length; i--;) {\n    if (arr[i] === val) {\n      arr.splice(i, 1);\n    }\n  }\n}\n
\n\n

Run-times on nodejs for array with 1000 elements (average over 10000 runs):

\n\n

indexof is approximately 10x slower than move. Even if improved by removing the call to indexOf in splice it performs much worse than move.

\n\n
Remove all occurrences:\n    move 0.0048 ms\n    indexof 0.0463 ms\n    splice 0.0359 ms\n\nRemove first occurrence:\n    move_one 0.0041 ms\n    indexof_one 0.0021 ms\n
\n", + "upvotes": 293, + "upvoterUsernames": [], + "downvotes": 127, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffaf", + "creator": "Roger", + "createdAt": 1377889620000, + "text": "

John Resig posted a good implementation:

\n\n
// Array Remove - By John Resig (MIT Licensed)\nArray.prototype.remove = function(from, to) {\n  var rest = this.slice((to || from) + 1 || this.length);\n  this.length = from < 0 ? this.length + from : from;\n  return this.push.apply(this, rest);\n};\n
\n\n

If you don’t want to extend a global object, you can do something like the following, instead:

\n\n
// Array Remove - By John Resig (MIT Licensed)\nArray.remove = function(array, from, to) {\n    var rest = array.slice((to || from) + 1 || array.length);\n    array.length = from < 0 ? array.length + from : from;\n    return array.push.apply(array, rest);\n};\n
\n\n

But the main reason I am posting this is to warn users against the alternative implementation suggested in the comments on that page (Dec 14, 2007):

\n\n
Array.prototype.remove = function(from, to){\n  this.splice(from, (to=[0,from||1,++to-from][arguments.length])<0?this.length+to:to);\n  return this.length;\n};\n
\n\n

It seems to work well at first, but through a painful process I discovered it fails when trying to remove the second to last element in an array. For example, if you have a 10-element array and you try to remove the 9th element with this:

\n\n
myArray.remove(8);\n
\n\n

You end up with an 8-element array. Don't know why but I confirmed John's original implementation doesn't have this problem.

\n", + "upvotes": 217, + "upvoterUsernames": [], + "downvotes": 100, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb1", + "creator": "Don Vincent Preziosi", + "createdAt": 1380154364000, + "text": "
Array.prototype.removeItem = function(a) {\n    for (i = 0; i < this.length; i++) {\n        if (this[i] == a) {\n            for (i2 = i; i2 < this.length - 1; i2++) {\n                this[i2] = this[i2 + 1];\n            }\n            this.length = this.length - 1\n            return;\n        }\n    }\n}\n\nvar recentMovies = ['Iron Man', 'Batman', 'Superman', 'Spiderman'];\nrecentMovies.removeItem('Superman');\n
\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb2", + "creator": "NullPointer", + "createdAt": 1381226993000, + "text": "

I also ran into the situation where I had to remove an element from Array. .indexOf was not working in Internet Explorer, so I am sharing my working jQuery.inArray() solution:

\n\n
var index = jQuery.inArray(val, arr);\nif (index > -1) {\n    arr.splice(index, 1);\n    //console.log(arr);\n}\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb3", + "creator": "sofiax", + "createdAt": 1389871674000, + "text": "

I'm pretty new to JavaScript and needed this functionality. I merely wrote this:

\n\n
function removeFromArray(array, item, index) {\n  while((index = array.indexOf(item)) > -1) {\n    array.splice(index, 1);\n  }\n}\n
\n\n

Then when I want to use it:

\n\n
//Set-up some dummy data\nvar dummyObj = {name:\"meow\"};\nvar dummyArray = [dummyObj, \"item1\", \"item1\", \"item2\"];\n\n//Remove the dummy data\nremoveFromArray(dummyArray, dummyObj);\nremoveFromArray(dummyArray, \"item2\");\n
\n\n

Output - As expected.\n[\"item1\", \"item1\"]

\n\n

You may have different needs than I, so you can easily modify it to suit them. I hope this helps someone.

\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb4", + "creator": "Ardi", + "createdAt": 1390920295000, + "text": "

Based on all the answers which were mainly correct and taking into account the best practices suggested (especially not using Array.prototype directly), I came up with the below code:

\n\n
function arrayWithout(arr, values) {\n  var isArray = function(canBeArray) {\n    if (Array.isArray) {\n      return Array.isArray(canBeArray);\n    }\n    return Object.prototype.toString.call(canBeArray) === '[object Array]';\n  };\n\n  var excludedValues = (isArray(values)) ? values : [].slice.call(arguments, 1);\n  var arrCopy = arr.slice(0);\n\n  for (var i = arrCopy.length - 1; i >= 0; i--) {\n    if (excludedValues.indexOf(arrCopy[i]) > -1) {\n      arrCopy.splice(i, 1);\n    }\n  }\n\n  return arrCopy;\n}\n
\n\n

Reviewing the above function, despite the fact that it works fine, I realised there could be some performance improvement. Also using ES6 instead of ES5 is a much better approach. To that end, this is the improved code:

\n\n
const arrayWithoutFastest = (() => {\n  const isArray = canBeArray => ('isArray' in Array) \n    ? Array.isArray(canBeArray) \n    : Object.prototype.toString.call(canBeArray) === '[object Array]';\n\n  let mapIncludes = (map, key) => map.has(key);\n  let objectIncludes = (obj, key) => key in obj;\n  let includes;\n\n  function arrayWithoutFastest(arr, ...thisArgs) {\n    let withoutValues = isArray(thisArgs[0]) ? thisArgs[0] : thisArgs;\n\n    if (typeof Map !== 'undefined') {\n      withoutValues = withoutValues.reduce((map, value) => map.set(value, value), new Map());\n      includes = mapIncludes;\n    } else {\n      withoutValues = withoutValues.reduce((map, value) => { map[value] = value; return map; } , {}); \n      includes = objectIncludes;\n    }\n\n    const arrCopy = [];\n    const length = arr.length;\n\n    for (let i = 0; i < length; i++) {\n      // If value is not in exclude list\n      if (!includes(withoutValues, arr[i])) {\n        arrCopy.push(arr[i]);\n      }\n    }\n\n    return arrCopy;\n  }\n\n  return arrayWithoutFastest;  \n})();\n
\n\n

How to use:

\n\n
const arr = [1,2,3,4,5,\"name\", false];\n\narrayWithoutFastest(arr, 1); // will return array [2,3,4,5,\"name\", false]\narrayWithoutFastest(arr, 'name'); // will return [2,3,4,5, false]\narrayWithoutFastest(arr, false); // will return [2,3,4,5]\narrayWithoutFastest(arr,[1,2]); // will return [3,4,5,\"name\", false];\narrayWithoutFastest(arr, {bar: \"foo\"}); // will return the same array (new copy)\n
\n\n

I am currently writing a blog post in which I have benchmarked several solutions for Array without problem and compared the time it takes to run. I will update this answer with the link once I finish that post. Just to let you know, I have compared the above against lodash's without and in case the browser supports Map, it beats lodash! Notice that I am not using Array.prototype.indexOf or Array.prototype.includes as wrapping the exlcudeValues in a Map or Object makes querying faster!

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb6", + "creator": "wharding28", + "createdAt": 1391442107000, + "text": "

I know there are a lot of answers already, but many of them seem to over complicate the problem. Here is a simple, recursive way of removing all instances of a key - calls self until index isn't found. Yes, it only works in browsers with indexOf, but it's simple and can be easily polyfilled.

\n\n

Stand-alone function

\n\n
function removeAll(array, key){\n    var index = array.indexOf(key);\n\n    if(index === -1) return;\n\n    array.splice(index, 1);\n    removeAll(array,key);\n}\n
\n\n

Prototype method

\n\n
Array.prototype.removeAll = function(key){\n    var index = this.indexOf(key);\n\n    if(index === -1) return;\n\n    this.splice(index, 1);\n    this.removeAll(key);\n}\n
\n", + "upvotes": 48, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e91100", + "creator": "Peter Mortensen", + "createdAt": 1567376056000, + "text": "But why a return in the middle? It is effectively a goto statement.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffb5", + "creator": "Nigel Sheridan-Smith", + "createdAt": 1391056055000, + "text": "

In CoffeeScript:

\n\n
my_array.splice(idx, 1) for ele, idx in my_array when ele is this_value\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb7", + "creator": "Salvador Dali", + "createdAt": 1392069961000, + "text": "

You can do it easily with the filter method:

\n

\r\n
\r\n
function remove(arrOriginal, elementToRemove){\n    return arrOriginal.filter(function(el){return el !== elementToRemove});\n}\nconsole.log(remove([1, 2, 1, 0, 3, 1, 4], 1));
\r\n
\r\n
\r\n

\n

This removes all elements from the array and also works faster than a combination of slice and indexOf.

\n", + "upvotes": 178, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffb8", + "creator": "mboeckle", + "createdAt": 1395309414000, + "text": "

I like this version of splice, removing an element by its value using $.inArray:

\n\n
$(document).ready(function(){\n    var arr = [\"C#\",\"Ruby\",\"PHP\",\"C\",\"C++\"];\n    var itemtoRemove = \"PHP\";\n    arr.splice($.inArray(itemtoRemove, arr),1);\n});\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e91104", + "creator": "Hontoni", + "createdAt": 1398877556000, + "text": "Removes last item if searched item not found", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f32258082fcc3049e91106", + "creator": "mboeckle", + "createdAt": 1398963606000, + "text": "yes correct, you should know which element you want to remove like in the other examples.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32258082fcc3049e91108", + "creator": "Dughall", + "createdAt": 1460993497000, + "text": "This is jQuery, not core JavaScript.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffba", + "creator": "vatsal", + "createdAt": 1401443855000, + "text": "

Underscore.js can be used to solve issues with multiple browsers. It uses in-build browser methods if present. If they are absent like in the case of older Internet Explorer versions it uses its own custom methods.

\n\n

A simple example to remove elements from array (from the website):

\n\n
_.without([1, 2, 1, 0, 3, 1, 4], 0, 1); // => [2, 3, 4]\n
\n", + "upvotes": 223, + "upvoterUsernames": [], + "downvotes": 111, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e9110a", + "creator": "EigenFool", + "createdAt": 1587027383000, + "text": "though elegant and concise, OP clearly mentioned core JS only", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffb9", + "creator": "amd", + "createdAt": 1399032018000, + "text": "

This provides a predicate instead of a value.

\n

NOTE: it will update the given array, and return the affected rows.

\n

Usage

\n
var removed = helper.remove(arr, row => row.id === 5 );\n\nvar removed = helper.removeAll(arr, row => row.name.startsWith('BMW'));\n
\n

Definition

\n
var helper = {\n // Remove and return the first occurrence\n\n remove: function(array, predicate) {\n  for (var i = 0; i < array.length; i++) {\n   if (predicate(array[i])) {\n    return array.splice(i, 1);\n   }\n  }\n },\n\n // Remove and return all occurrences\n\n removeAll: function(array, predicate) {\n  var removed = [];\n\n  for (var i = 0; i < array.length; ) {\n   if (predicate(array[i])) {\n    removed.push(array.splice(i, 1));\n    continue;\n   }\n   i++;\n  }\n  return removed;\n },\n};\n
\n", + "upvotes": 211, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e9110d", + "creator": "Masoud Aghaei", + "createdAt": 1612295757000, + "text": "put your code in code snippet so other users could see the result", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffbc", + "creator": "Do Hoa Vinh", + "createdAt": 1410948874000, + "text": "

Use jQuery's InArray:

\n\n
A = [1, 2, 3, 4, 5, 6];\nA.splice($.inArray(3, A), 1);\n//It will return A=[1, 2, 4, 5, 6]`   \n
\n\n

Note: inArray will return -1, if the element was not found.

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e91110", + "creator": "CSᵠ", + "createdAt": 1418410291000, + "text": "but OP said: "good ol' fashioned JavaScript - no frameworks allowed"", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32258082fcc3049e91112", + "creator": "Scott 混合理论", + "createdAt": 1466156320000, + "text": "for Chrome 50.0, A.splice(-1, 1); will remove the last one in A.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffbb", + "creator": "penguin", + "createdAt": 1403740650000, + "text": "
var index,\n    input = [1,2,3],\n    indexToRemove = 1;\n    integers = [];\n\nfor (index in input) {\n    if (input.hasOwnProperty(index)) {\n        if (index !== indexToRemove) {\n            integers.push(result); \n        }\n    }\n}\ninput = integers;\n
\n\n

This solution will take an array of input and will search through the input for the value to remove. This will loop through the entire input array and the result will be a second array integers that has had the specific index removed. The integers array is then copied back into the input array.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32258082fcc3049e91115", + "creator": "Christophe Roussy", + "createdAt": 1411656036000, + "text": "This is very inefficient when the array is large.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffbd", + "creator": "Nejc Lepen", + "createdAt": 1413987001000, + "text": "

Removing the value with index and splice!

\n\n
function removeArrValue(arr,value) {\n    var index = arr.indexOf(value);\n    if (index > -1) {\n        arr.splice(index, 1);\n    }\n    return arr;\n}\n
\n", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffbe", + "creator": "Matt Brock", + "createdAt": 1417030764000, + "text": "

If you must support older versions of Internet Explorer, I recommend using the following polyfill (note: this is not a framework). It's a 100% backwards-compatible replacement of all modern array methods (JavaScript 1.8.5 / ECMAScript 5 Array Extras) that works for Internet Explorer 6+, Firefox 1.5+, Chrome, Safari, & Opera.

\n\n

https://github.com/plusdude/array-generics

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc0", + "creator": "flurdy", + "createdAt": 1428573657000, + "text": "

If you have complex objects in the array you can use filters? \nIn situations where $.inArray or array.splice is not as easy to use. Especially if the objects are perhaps shallow in the array.

\n\n

E.g. if you have an object with an Id field and you want the object removed from an array:

\n\n
this.array = this.array.filter(function(element, i) {\n    return element.id !== idToRemove;\n});\n
\n", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffbf", + "creator": "Ryan", + "createdAt": 1420660397000, + "text": "

There are many fantastic answers here, but for me, what worked most simply wasn't removing my element from the array completely, but simply setting the value of it to null.

\n\n

This works for most cases I have and is a good solution since I will be using the variable later and don't want it gone, just empty for now. Also, this approach is completely cross-browser compatible.

\n\n
array.key = null;\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc2", + "creator": "Eugene Tiurin", + "createdAt": 1447585742000, + "text": "

The following method will remove all entries of a given value from an array without creating a new array and with only one iteration which is superfast. And it works in ancient Internet Explorer 5.5 browser:

\n\n

\r\n
\r\n
function removeFromArray(arr, removeValue) {\r\n  for (var i = 0, k = 0, len = arr.length >>> 0; i < len; i++) {\r\n    if (k > 0)\r\n      arr[i - k] = arr[i];\r\n\r\n    if (arr[i] === removeValue)\r\n      k++;\r\n  }\r\n\r\n  for (; k--;)\r\n    arr.pop();\r\n}\r\n\r\nvar a = [0, 1, 0, 2, 0, 3];\r\n\r\ndocument.getElementById('code').innerHTML =\r\n  'Initial array [' + a.join(', ') + ']';\r\n//Initial array [0, 1, 0, 2, 0, 3]\r\n\r\nremoveFromArray(a, 0);\r\n\r\ndocument.getElementById('code').innerHTML +=\r\n  '<br>Resulting array [' + a.join(', ') + ']';\r\n//Resulting array [1, 2, 3]
\r\n
<code id=\"code\"></code>
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32259082fcc3049e9111a", + "creator": "Ankur Loriya", + "createdAt": 1450362179000, + "text": "What is meaning of this code I could not understand.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32259082fcc3049e9111c", + "creator": "Eugene Tiurin", + "createdAt": 1451070997000, + "text": "@AnkurLoriya This code removes all 0s from the given array", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffc1", + "creator": "Chun Yang", + "createdAt": 1440531288000, + "text": "

You can use lodash _.pull (mutate array), _.pullAt (mutate array) or _.without (does't mutate array),

\n\n
var array1 = ['a', 'b', 'c', 'd']\n_.pull(array1, 'c')\nconsole.log(array1) // ['a', 'b', 'd']\n\nvar array2 = ['e', 'f', 'g', 'h']\n_.pullAt(array2, 0)\nconsole.log(array2) // ['f', 'g', 'h']\n\nvar array3 = ['i', 'j', 'k', 'l']\nvar newArray = _.without(array3, 'i') // ['j', 'k', 'l']\nconsole.log(array3) // ['i', 'j', 'k', 'l']\n
\n", + "upvotes": 132, + "upvoterUsernames": [], + "downvotes": 57, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32259082fcc3049e9111e", + "creator": "some-non-descript-user", + "createdAt": 1443128510000, + "text": "That's not core JS as the OP requested, is it?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32259082fcc3049e91120", + "creator": "Chun Yang", + "createdAt": 1443670710000, + "text": "@some-non-descript-user You are right. But a lot of users like me come here looking for a general answer not just for the OP only.", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffc3", + "creator": "MEC", + "createdAt": 1452433004000, + "text": "

Remove last occurrence or all occurrences, or first occurrence?

\n\n
var array = [2, 5, 9, 5];\n\n// Remove last occurrence (or all occurrences)\nfor (var i = array.length; i--;) {\n  if (array[i] === 5) {\n     array.splice(i, 1);\n     break; // Remove this line to remove all occurrences\n  }\n}\n
\n\n

or

\n\n
var array = [2, 5, 9, 5];\n\n// Remove first occurrence\nfor (var i = 0; array.length; i++) {\n  if (array[i] === 5) {\n     array.splice(i, 1);\n     break; // Do not remove this line\n  }\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc4", + "creator": "Kamuran Sönecek", + "createdAt": 1454057629000, + "text": "

By my solution you can remove one or more than one item in an array thanks to pure JavaScript. There is no need for another JavaScript library.

\n\n
var myArray = [1,2,3,4,5]; // First array\n\nvar removeItem = function(array,value) {  // My clear function\n    if(Array.isArray(value)) {  // For multi remove\n        for(var i = array.length - 1; i >= 0; i--) {\n            for(var j = value.length - 1; j >= 0; j--) {\n                if(array[i] === value[j]) {\n                    array.splice(i, 1);\n                };\n            }\n        }\n    }\n    else { // For single remove\n        for(var i = array.length - 1; i >= 0; i--) {\n            if(array[i] === value) {\n                array.splice(i, 1);\n            }\n        }\n    }\n}\n\nremoveItem(myArray,[1,4]); // myArray will be [2,3,5]\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc6", + "creator": "bjfletcher", + "createdAt": 1461579685000, + "text": "

A more modern, ECMAScript 2015 (formerly known as Harmony or ES 6) approach. Given:

\n
const items = [1, 2, 3, 4];\nconst index = 2;\n
\n

Then:

\n
items.filter((x, i) => i !== index);\n
\n

Yielding:

\n
[1, 2, 4]\n
\n

You can use Babel and a polyfill service to ensure this is well supported across browsers.

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32259082fcc3049e91124", + "creator": "bjfletcher", + "createdAt": 1464732268000, + "text": "@Seraph For that, you'd probably want to use splice or slice.", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 58, + "downvoterUsernames": [] + }, + { + "_id": "62f32259082fcc3049e91126", + "creator": "jadinerky", + "createdAt": 1660086451000, + "text": "The property Array.prototype.filter() is now fully supported, no need for babel or any polyfill", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffc5", + "creator": "Lars Gyrup Brink Nielsen", + "createdAt": 1460353636000, + "text": "

Vanilla JavaScript (ES5.1) – in place edition

\n\n

Browser support: Internet Explorer 9 or later (detailed browser support)

\n\n
/**\n * Removes all occurences of the item from the array.\n *\n * Modifies the array “in place”, i.e. the array passed as an argument\n * is modified as opposed to creating a new array. Also returns the modified\n * array for your convenience.\n */\nfunction removeInPlace(array, item) {\n    var foundIndex, fromIndex;\n\n    // Look for the item (the item can have multiple indices)\n    fromIndex = array.length - 1;\n    foundIndex = array.lastIndexOf(item, fromIndex);\n\n    while (foundIndex !== -1) {\n        // Remove the item (in place)\n        array.splice(foundIndex, 1);\n\n        // Bookkeeping\n        fromIndex = foundIndex - 1;\n        foundIndex = array.lastIndexOf(item, fromIndex);\n    }\n\n    // Return the modified array\n    return array;\n}\n
\n\n

Vanilla JavaScript (ES5.1) – immutable edition

\n\n

Browser support: Same as vanilla JavaScript in place edition

\n\n
/**\n * Removes all occurences of the item from the array.\n *\n * Returns a new array with all the items of the original array except\n * the specified item.\n */\nfunction remove(array, item) {\n    var arrayCopy;\n\n    arrayCopy = array.slice();\n\n    return removeInPlace(arrayCopy, item);\n}\n
\n\n

Vanilla ES6 – immutable edition

\n\n

Browser support: Chrome 46, Edge 12, Firefox 16, Opera 37, Safari 8 (detailed browser support)

\n\n
/**\n * Removes all occurences of the item from the array.\n *\n * Returns a new array with all the items of the original array except\n * the specified item.\n */\nfunction remove(array, item) {\n    // Copy the array\n    array = [...array];\n\n    // Look for the item (the item can have multiple indices)\n    let fromIndex = array.length - 1;\n    let foundIndex = array.lastIndexOf(item, fromIndex);\n\n    while (foundIndex !== -1) {\n        // Remove the item by generating a new array without it\n        array = [\n            ...array.slice(0, foundIndex),\n            ...array.slice(foundIndex + 1),\n        ];\n\n        // Bookkeeping\n        fromIndex = foundIndex - 1;\n        foundIndex = array.lastIndexOf(item, fromIndex)\n    }\n\n    // Return the new array\n    return array;\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc7", + "creator": "Redu", + "createdAt": 1462651859000, + "text": "

I think many of the JavaScript instructions are not well thought out for functional programming. Splice returns the deleted element where most of the time you need the reduced array. This is bad.

\n\n

Imagine you are doing a recursive call and have to pass an array with one less item, probably without the current indexed item. Or imagine you are doing another recursive call and has to pass an array with an element pushed.

\n\n

In neither of these cases you can do myRecursiveFunction(myArr.push(c)) or myRecursiveFunction(myArr.splice(i,1)). The first idiot will in fact pass the length of the array and the second idiot will pass the deleted element as a parameter.

\n\n

So what I do in fact... For deleting an array element and passing the resulting to a function as a parameter at the same time I do as follows

\n\n
myRecursiveFunction(myArr.slice(0,i).concat(a.slice(i+1)))\n
\n\n

When it comes to push that's more silly... I do like,

\n\n
myRecursiveFunction((myArr.push(c),myArr))\n
\n\n

I believe in a proper functional language a method mutating the object it's called upon must return a reference to the very object as a result.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffc8", + "creator": "user4109793", + "createdAt": 1463803724000, + "text": "
Array.prototype.remove = function(x) {\n    var y=this.slice(x+1);\n    var z=[];\n    for(i=0;i<=x-1;i++) {\n        z[z.length] = this[i];\n    }\n\n    for(i=0;i<y.length;i++){\n        z[z.length]=y[i];\n    }\n\n    return z;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32259082fcc3049e91129", + "creator": "Peter Mortensen", + "createdAt": 1586653789000, + "text": "An explanation would be in order.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32259082fcc3049e9112a", + "creator": "Heretic Monkey", + "createdAt": 1594142780000, + "text": "@PeterMortensen But unlikely since the OP has been deleted :).", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffca", + "creator": "Shawn Deprey", + "createdAt": 1471024777000, + "text": "

I made a fairly efficient extension to the base JavaScript array:

\n\n
Array.prototype.drop = function(k) {\n  var valueIndex = this.indexOf(k);\n  while(valueIndex > -1) {\n    this.removeAt(valueIndex);\n    valueIndex = this.indexOf(k);\n  }\n};\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e9112d", + "creator": "ankhzet", + "createdAt": 1529398898000, + "text": "Also, no removeAt in ES standard. I suppose, this is some IE-only stuff? That should be mentioned in answer.", + "upvotes": 427, + "upvoterUsernames": [], + "downvotes": 427, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffc9", + "creator": "Mahendra Kulkarni", + "createdAt": 1467105182000, + "text": "

Use jQuery.grep():

\n\n

\r\n
\r\n
var y = [1, 2, 3, 9, 4]\r\nvar removeItem = 9;\r\n\r\ny = jQuery.grep(y, function(value) {\r\n  return value != removeItem;\r\n});\r\nconsole.log(y)
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e91130", + "creator": "codingsplash", + "createdAt": 1470477411000, + "text": "The OP specifically said no frameworks. Hence the downvote.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffcb", + "creator": "rajat44", + "createdAt": 1475568470000, + "text": "

You can use ES6. For example to delete the value '3' in this case:

\n\n
var array=['1','2','3','4','5','6']\nvar newArray = array.filter((value)=>value!='3');\nconsole.log(newArray);\n
\n\n

Output :

\n\n
[\"1\", \"2\", \"4\", \"5\", \"6\"]\n
\n", + "upvotes": 219, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e91133", + "creator": "Claudio Holanda", + "createdAt": 1489678060000, + "text": "This answer is nice because it creates a copy of the original array, instead of modifying the original directly.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffcc", + "creator": "Abdennour TOUMI", + "createdAt": 1475869371000, + "text": "

ES6 & without mutation: (October 2016)

\n\n

\r\n
\r\n
const removeByIndex = (list, index) =>\r\n      [\r\n        ...list.slice(0, index),\r\n        ...list.slice(index + 1)\r\n      ];\r\n         \r\noutput = removeByIndex([33,22,11,44],1) //=> [33,11,44]\r\n      \r\nconsole.log(output)
\r\n
\r\n
\r\n

\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e91136", + "creator": "Sebastian Simon", + "createdAt": 1586284204000, + "text": "Why not just use filter then? array.filter((_, index) => index !== removedIndex);.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffce", + "creator": "Ali Akram", + "createdAt": 1490008617000, + "text": "

I made a function:

\n\n
function pop(valuetoremove, myarray) {\n    var indexofmyvalue = myarray.indexOf(valuetoremove);\n    myarray.splice(indexofmyvalue, 1);\n}\n
\n\n

And used it like this:

\n\n
pop(valuetoremove, myarray);\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e91138", + "creator": "Jamiec", + "createdAt": 1490709970000, + "text": "If you're going to name it pop at least make it do what the method name implies!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffcd", + "creator": "Stelios Voskos", + "createdAt": 1480452878000, + "text": "

While most of the previous answers answer the question, it is not clear enough why the slice() method has not been used. Yes, filter() meets the immutability criteria, but how about doing the following shorter equivalent?

\n\n
const myArray = [1,2,3,4];\n
\n\n

And now let’s say that we should remove the second element from the array, we can simply do:

\n\n
const newArray = myArray.slice(0, 1).concat(myArray.slice(2, 4));\n\n// [1,3,4]\n
\n\n

This way of deleting an element from an array is strongly encouraged today in the community due to its simple and immutable nature. In general, methods which cause mutation should be avoided. For example, you are encouraged to replace push() with concat() and splice() with slice().

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffcf", + "creator": "mpen", + "createdAt": 1491768541000, + "text": "

Remove one value, using loose comparison, without mutating the original array, ES6

\n\n
/**\n * Removes one instance of `value` from `array`, without mutating the original array. Uses loose comparison.\n *\n * @param {Array} array Array to remove value from\n * @param {*} value Value to remove\n * @returns {Array} Array with `value` removed\n */\nexport function arrayRemove(array, value) {\n    for(let i=0; i<array.length; ++i) {\n        if(array[i] == value) {\n            let copy = [...array];\n            copy.splice(i, 1);\n            return copy;\n        }\n    }\n    return array;\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225a082fcc3049e9113c", + "creator": "darmis", + "createdAt": 1569963875000, + "text": "export const arrayRemove = (array, value) => [...array.filter(item => item !== value)]; Perhaps this could be simpler.", + "upvotes": 489, + "upvoterUsernames": [], + "downvotes": 489, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffd0", + "creator": "alejandro", + "createdAt": 1493947969000, + "text": "

Remove element at index i, without mutating the original array:

\n\n
/**\n* removeElement\n* @param {Array} array\n* @param {Number} index\n*/\nfunction removeElement(array, index) {\n   return Array.from(array).splice(index, 1);\n}\n\n// Another way is\nfunction removeElement(array, index) {\n   return array.slice(0).splice(index, 1);\n}\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd2", + "creator": "Alireza", + "createdAt": 1494409140000, + "text": "

OK, for example you have the array below:

\n\n
var num = [1, 2, 3, 4, 5];\n
\n\n

And we want to delete number 4. You can simply use the below code:

\n\n
num.splice(num.indexOf(4), 1); // num will be [1, 2, 3, 5];\n
\n\n

If you are reusing this function, you write a reusable function which will be attached to the native array function like below:

\n\n
Array.prototype.remove = Array.prototype.remove || function(x) {\n  const i = this.indexOf(x);\n  if(i===-1)\n      return;\n  this.splice(i, 1); // num.remove(5) === [1, 2, 3];\n}\n
\n\n

But how about if you have the below array instead with a few [5]s in the array?

\n\n
var num = [5, 6, 5, 4, 5, 1, 5];\n
\n\n

We need a loop to check them all, but an easier and more efficient way is using built-in JavaScript functions, so we write a function which use a filter like below instead:

\n\n
const _removeValue = (arr, x) => arr.filter(n => n!==x);\n//_removeValue([1, 2, 3, 4, 5, 5, 6, 5], 5) // Return [1, 2, 3, 4, 6]\n
\n\n

Also there are third-party libraries which do help you to do this, like Lodash or Underscore. For more information, look at lodash _.pull, _.pullAt or _.without.

\n", + "upvotes": 75, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd1", + "creator": "Aidan Hoolachan", + "createdAt": 1494266305000, + "text": "

2017-05-08

\n\n

Most of the given answers work for strict comparison, meaning that both objects reference the exact same object in memory (or are primitive types), but often you want to remove a non-primitive object from an array that has a certain value. For instance, if you make a call to a server and want to check a retrieved object against a local object.

\n\n
const a = {'field': 2} // Non-primitive object\nconst b = {'field': 2} // Non-primitive object with same value\nconst c = a            // Non-primitive object that reference the same object as \"a\"\n\nassert(a !== b) // Don't reference the same item, but have same value\nassert(a === c) // Do reference the same item, and have same value (naturally)\n\n//Note: there are many alternative implementations for valuesAreEqual\nfunction valuesAreEqual (x, y) {\n   return  JSON.stringify(x) === JSON.stringify(y)\n}\n\n\n//filter will delete false values\n//Thus, we want to return \"false\" if the item\n// we want to delete is equal to the item in the array\nfunction removeFromArray(arr, toDelete){\n    return arr.filter(target => {return !valuesAreEqual(toDelete, target)})\n}\n\nconst exampleArray = [a, b, b, c, a, {'field': 2}, {'field': 90}];\nconst resultArray = removeFromArray(exampleArray, a);\n\n//resultArray = [{'field':90}]\n
\n\n

There are alternative/faster implementations for valuesAreEqual, but this does the job. You can also use a custom comparator if you have a specific field to check (for example, some retrieved UUID vs a local UUID).

\n\n

Also note that this is a functional operation, meaning that it does not mutate the original array.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd4", + "creator": "Partial Science", + "createdAt": 1500076724000, + "text": "

There are already a lot of answers, but because no one has done it with a one liner yet, I figured I'd show my method. It takes advantage of the fact that the string.split() function will remove all of the specified characters when creating an array. Here is an example:

\n\n

\r\n
\r\n
var ary = [1,2,3,4,1234,10,4,5,7,3];\r\nout = ary.join(\"-\").split(\"-4-\").join(\"-\").split(\"-\");\r\nconsole.log(out);
\r\n
\r\n
\r\n

\n\n

In this example, all of the 4's are being removed from the array ary. However, it is important to note that any array containing the character \"-\" will cause issues with this example. In short, it will cause the join(\"-\") function to piece your string together improperly. In such a situation, all of the the \"-\" strings in the above snipet can be replaced with any string that will not be used in the original array. Here is another example:

\n\n

\r\n
\r\n
var ary = [1,2,3,4,'-',1234,10,'-',4,5,7,3];\r\nout = ary.join(\"!@#\").split(\"!@#4!@#\").join(\"!@#\").split(\"!@#\");\r\nconsole.log(out);
\r\n
\r\n
\r\n

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd3", + "creator": "nsantana", + "createdAt": 1494554454000, + "text": "

Remove by Index

\n\n

A function that returns a copy of array without the element at index:

\n\n
/**\n* removeByIndex\n* @param {Array} array\n* @param {Number} index\n*/\nfunction removeByIndex(array, index){\n      return array.filter(function(elem, _index){\n          return index != _index;\n    });\n}\nl = [1,3,4,5,6,7];\nconsole.log(removeByIndex(l, 1));\n\n$> [ 1, 4, 5, 6, 7 ]\n
\n\n

Remove by Value

\n\n

Function that return a copy of array without the Value.

\n\n
/**\n* removeByValue\n* @param {Array} array\n* @param {Number} value\n*/\nfunction removeByValue(array, value){\n      return array.filter(function(elem, _index){\n          return value != elem;\n    });\n}\nl = [1,3,4,5,6,7];\nconsole.log(removeByValue(l, 5));\n\n$> [ 1, 3, 4, 6, 7]\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd5", + "creator": "hailong", + "createdAt": 1501455352000, + "text": "

I post my code that removes an array element in place, and reduce the array length as well.

\n\n
function removeElement(idx, arr) {\n    // Check the index value\n    if (idx < 0 || idx >= arr.length) {\n        return;\n    }\n    // Shift the elements\n    for (var i = idx; i > 0; --i) {\n        arr[i] = arr[i - 1];\n    }\n    // Remove the first element in array\n    arr.shift();\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd6", + "creator": "cjjenkinson", + "createdAt": 1505740674000, + "text": "

For anyone looking to replicate a method that will return a new array that has duplicate numbers or strings removed, this has been put together from existing answers:

\n\n
function uniq(array) {\n  var len = array.length;\n  var dupFree = [];\n  var tempObj = {};\n\n  for (var i = 0; i < len; i++) {\n    tempObj[array[i]] = 0;\n  }\n\n  console.log(tempObj);\n\n  for (var i in tempObj) {\n    var element = i;\n    if (i.match(/\\d/)) {\n      element = Number(i);\n    }\n    dupFree.push(element);\n  }\n\n  return dupFree;\n}\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd7", + "creator": "Adeel Imran", + "createdAt": 1514316726000, + "text": "

You should never mutate your array as this is against the functional programming pattern. You can create a new array without referencing the one you want to change data of using the ECMAScript 6 method filter;

\n
var myArray = [1, 2, 3, 4, 5, 6];\n
\n

Suppose you want to remove 5 from the array, you can simply do it like this:

\n
myArray = myArray.filter(value => value !== 5);\n
\n

This will give you a new array without the value you wanted to remove. So the result will be:

\n
 [1, 2, 3, 4, 6]; // 5 has been removed from this array\n
\n

For further understanding you can read the MDN documentation on Array.filter.

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd8", + "creator": "Sujith S", + "createdAt": 1518682486000, + "text": "

\r\n
\r\n
var array = [2, 5, 9];\nvar res = array.splice(array.findIndex(x => x==5), 1);\n\nconsole.log(res)
\r\n
\r\n
\r\n

\n

Using Array.findindex, we can reduce the number of lines of code.

\n

developer.mozilla.org

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225b082fcc3049e91145", + "creator": "pwilcox", + "createdAt": 1532875485000, + "text": "You better be sure you know the element is in the array, otherwise findindex returns -1 and consequently removes the 9.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffda", + "creator": "Gaurang Patel", + "createdAt": 1520574020000, + "text": "

A very naive implementation would be as follows:

\n\n

\r\n
\r\n
Array.prototype.remove = function(data) {\r\n    const dataIdx = this.indexOf(data)\r\n    if(dataIdx >= 0) {\r\n        this.splice(dataIdx ,1);\r\n    }\r\n    return this.length;\r\n}\r\n\r\nlet a = [1,2,3];\r\n// This will change arr a to [1, 3]\r\na.remove(2);
\r\n
\r\n
\r\n

\n\n

I return the length of the array from the function to comply with the other methods, like Array.prototype.push().

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffd9", + "creator": "Victor", + "createdAt": 1520534144000, + "text": "

I just created a polyfill on the Array.prototype via Object.defineProperty to remove a desired element in an array without leading to errors when iterating over it later via for .. in ..

\n\n
if (!Array.prototype.remove) {\n  // Object.definedProperty is used here to avoid problems when iterating with \"for .. in ..\" in Arrays\n  // https://stackoverflow.com/questions/948358/adding-custom-functions-into-array-prototype\n  Object.defineProperty(Array.prototype, 'remove', {\n    value: function () {\n      if (this == null) {\n        throw new TypeError('Array.prototype.remove called on null or undefined')\n      }\n\n      for (var i = 0; i < arguments.length; i++) {\n        if (typeof arguments[i] === 'object') {\n          if (Object.keys(arguments[i]).length > 1) {\n            throw new Error('This method does not support more than one key:value pair per object on the arguments')\n          }\n          var keyToCompare = Object.keys(arguments[i])[0]\n\n          for (var j = 0; j < this.length; j++) {\n            if (this[j][keyToCompare] === arguments[i][keyToCompare]) {\n              this.splice(j, 1)\n              break\n            }\n          }\n        } else {\n          var index = this.indexOf(arguments[i])\n          if (index !== -1) {\n            this.splice(index, 1)\n          }\n        }\n      }\n      return this\n    }\n  })\n} else {\n  var errorMessage = 'DANGER ALERT! Array.prototype.remove has already been defined on this browser. '\n  errorMessage += 'This may lead to unwanted results when remove() is executed.'\n  console.log(errorMessage)\n}\n
\n\n

Removing an integer value

\n\n
var a = [1, 2, 3]\na.remove(2)\na // Output => [1, 3]\n
\n\n

Removing a string value

\n\n
var a = ['a', 'ab', 'abc']\na.remove('abc')\na // Output => ['a', 'ab']\n
\n\n

Removing a boolean value

\n\n
var a = [true, false, true]\na.remove(false)\na // Output => [true, true]\n
\n\n

It is also possible to remove an object inside the array via this Array.prototype.remove method. You just need to specify the key => value of the Object you want to remove.

\n\n

Removing an object value

\n\n
var a = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 3, b: 2}]\na.remove({a: 1})\na // Output => [{a: 2, b: 2}, {a: 3, b: 2}]\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffdb", + "creator": "Sanya Kravchuk", + "createdAt": 1523619064000, + "text": "
let array = [5,5,4,4,2,3,4]    \nlet newArray = array.join(',').replace('5','').split(',')\n
\n\n

This example works if you want to remove one current item.

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 38, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225b082fcc3049e91149", + "creator": "Masoud Aghaei", + "createdAt": 1612294871000, + "text": "your code just remove first 5 if you wanna remove all 5 you have to use regex in your replace function", + "upvotes": 3528, + "upvoterUsernames": [], + "downvotes": 3528, + "downvoterUsernames": [] + }, + { + "_id": "62f3225b082fcc3049e9114a", + "creator": "Masoud Aghaei", + "createdAt": 1612294988000, + "text": "and what do you want to do if we have 55 or sth similar in the array and we want to keep that element?", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffdc", + "creator": "Steven Spungin", + "createdAt": 1524787668000, + "text": "

Your question did not indicate if order or distinct values are a requirement.

\n\n

If you don't care about order, and will not have the same value in the container more than once, use a Set. It will be way faster, and more succinct.

\n\n
var aSet = new Set();\n\naSet.add(1);\naSet.add(2);\naSet.add(3);\n\naSet.delete(2);\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffdd", + "creator": "Aram Grigoryan", + "createdAt": 1528210020000, + "text": "

I have another good solution for removing from an array:

\n\n
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];\n\nconst result = words.filter(word => word.length > 6);\n\nconsole.log(result);\n// expected output: Array [\"exuberant\", \"destruction\", \"present\"]\n
\n\n

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffde", + "creator": "Srikrushna", + "createdAt": 1530208121000, + "text": "

Delete an element from last

\n\n
arrName.pop();\n
\n\n

Delete an element from first

\n\n
arrName.shift();\n
\n\n

Delete from the middle

\n\n
arrName.splice(starting index, number of element you wnt to delete);\n\nExample: arrName.splice(1, 1);\n
\n\n

Delete one element from last

\n\n
arrName.splice(-1);\n
\n\n

Delete by using an array index number

\n\n
 delete arrName[1];\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe0", + "creator": "Thilina Sampath", + "createdAt": 1533634645000, + "text": "

You have 1 to 9 in the array, and you want remove 5. Use the below code:

\n\n

\r\n
\r\n
var numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];\r\n\r\nvar newNumberArray = numberArray.filter(m => {\r\n  return m !== 5;\r\n});\r\n\r\nconsole.log(\"new Array, 5 removed\", newNumberArray);
\r\n
\r\n
\r\n

\n\n
\n\n

If you want to multiple values. Example:- 1,7,8

\n\n

\r\n
\r\n
var numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];\r\n\r\nvar newNumberArray = numberArray.filter(m => {\r\n  return (m !== 1) && (m !== 7) && (m !== 8);\r\n});\r\n\r\nconsole.log(\"new Array, 1,7 and 8 removed\", newNumberArray);
\r\n
\r\n
\r\n

\n\n
\n\n

If you want to remove an array value in an array. Example: [3,4,5]

\n\n

\r\n
\r\n
var numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];\r\nvar removebleArray = [3,4,5];\r\n\r\nvar newNumberArray = numberArray.filter(m => {\r\n    return !removebleArray.includes(m);\r\n});\r\n\r\nconsole.log(\"new Array, [3,4,5] removed\", newNumberArray);
\r\n
\r\n
\r\n

\n\n

Includes supported browser is link.

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffdf", + "creator": "Zibri", + "createdAt": 1532234539000, + "text": "
    Array.prototype.remove = function(start, end) {\n        var n = this.slice((end || start) + 1 || this.length);\n        return this.length = start < 0 ? this.length + start : start,\n        this.push.apply(this, n)\n    }\n
\n\n

start and end can be negative. In that case they count from the end of the array.

\n\n

If only start is specified, only one element is removed.

\n\n

The function returns the new array length.

\n\n
z = [0,1,2,3,4,5,6,7,8,9];\n\nnewlength = z.remove(2,6);\n
\n\n

(8) [0, 1, 7, 8, 9]

\n\n
z=[0,1,2,3,4,5,6,7,8,9];\n\nnewlength = z.remove(-4,-2);\n
\n\n

(7) [0, 1, 2, 3, 4, 5, 9]

\n\n
z=[0,1,2,3,4,5,6,7,8,9];\n\nnewlength = z.remove(3,-2);\n
\n\n

(4) [0, 1, 2, 9]

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225c082fcc3049e9114f", + "creator": "mekbib.awoke", + "createdAt": 1566563036000, + "text": "this may collide with other code, libraries, i would rather implement a remove function for that specific array", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffe2", + "creator": "AmerllicA", + "createdAt": 1540921062000, + "text": "

I want to answer based on ECMAScript 6. Assume you have an array like below:

\n
let arr = [1,2,3,4];\n
\n

If you want to delete at a special index like 2, write the below code:

\n
arr.splice(2, 1); //=> arr became [1,2,4]\n
\n

But if you want to delete a special item like 3 and you don't know its index, do like below:

\n
arr = arr.filter(e => e !== 3); //=> arr became [1,2,4]\n
\n

Hint: please use an arrow function for filter callback unless you will get an empty array.

\n", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe1", + "creator": "Phil", + "createdAt": 1536743796000, + "text": "

I had this problem myself (in a situation where replacing the array was acceptable) and solved it with a simple:

\n\n
var filteredItems = this.items.filter(function (i) {\n    return i !== item;\n});\n
\n\n

To give the above snippet a bit of context:

\n\n
self.thingWithItems = {\n    items: [],\n    removeItem: function (item) {\n        var filteredItems = this.items.filter(function (i) {\n            return i !== item;\n        });\n\n        this.items = filteredItems;\n    }\n};\n
\n\n

This solution should work with both reference and value items. It all depends whether you need to maintain a reference to the original array as to whether this solution is applicable.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe3", + "creator": "Sebastien H.", + "createdAt": 1545405047000, + "text": "

To me the simpler is the better, and as we are in 2018 (near 2019) I give you this (near) one-liner to answer the original question:

\n\n
Array.prototype.remove = function (value) {\n    return this.filter(f => f != value)\n}\n
\n\n

The useful thing is that you can use it in a curry expression such as:

\n\n
[1,2,3].remove(2).sort()\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe4", + "creator": "Shahriar Ahmad", + "createdAt": 1546299207000, + "text": "
var arr =[1,2,3,4,5];\n\narr.splice(0,1)\n\nconsole.log(arr)\n
\n\n

Output [2, 3, 4, 5];

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe5", + "creator": "Max Alexander Hanna", + "createdAt": 1546958076000, + "text": "

Removing a particular element/string from an array can be done in a one-liner:

\n
theArray.splice(theArray.indexOf("stringToRemoveFromArray"), 1);\n
\n

where:

\n

theArray: the array you want to remove something particular from

\n

stringToRemoveFromArray: the string you want to be removed and 1 is the number of elements you want to remove.

\n

NOTE: If "stringToRemoveFromArray" is not located in the array, this will remove the last element of the array.

\n

It's always good practice to check if the element exists in your array first, before removing it.

\n
if (theArray.indexOf("stringToRemoveFromArray") >= 0){\n   theArray.splice(theArray.indexOf("stringToRemoveFromArray"), 1);\n}\n
\n
\n

Depending if you have newer or older version of Ecmascript running on your client's computers:

\n
var array=['1','2','3','4','5','6']\nvar newArray = array.filter((value)=>value!='3');\n
\n

OR

\n
var array = ['1','2','3','4','5','6'];\nvar newArray = array.filter(function(item){ return item !== '3' });\n
\n

Where '3' is the value you want to be removed from the array.\nThe array would then become : ['1','2','4','5','6']

\n", + "upvotes": 151, + "upvoterUsernames": [], + "downvotes": 74, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225c082fcc3049e91156", + "creator": "jdavid05", + "createdAt": 1554292366000, + "text": "This is the answer that worked for me when trying to update an array based on radio button toggling.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3225c082fcc3049e91158", + "creator": "Fusion", + "createdAt": 1554593739000, + "text": "Beware, if "stringToRemoveFromArray" is not located your in array, this will remove last element of array.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffe6", + "creator": "Chang", + "createdAt": 1548608152000, + "text": "

To remove a particular element or subsequent elements, Array.splice() method works well.

\n\n

The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements, and it returns the removed item(s).

\n\n

Syntax: array.splice(index, deleteCount, item1, ....., itemX)

\n\n

Here index is mandatory and rest arguments are optional.

\n\n

For example:

\n\n
let arr = [1, 2, 3, 4, 5, 6];\narr.splice(2,1);\nconsole.log(arr);\n// [1, 2, 4, 5, 6]\n
\n\n

Note: Array.splice() method can be used if you know the index of the element which you want to delete. But we may have a few more cases as mentioned below:

\n\n
    \n
  1. In case you want to delete just last element, you can use Array.pop()

  2. \n
  3. In case you want to delete just first element, you can use Array.shift()

  4. \n
  5. If you know the element alone, but not the position (or index) of the element, and want to delete all matching elements using Array.filter() method:

    \n\n
    let arr = [1, 2, 1, 3, 4, 1, 5, 1];\n\nlet newArr = arr.filter(function(val){\n    return val !== 1;\n});\n//newArr => [2, 3, 4, 5]\n
    \n\n

    Or by using the splice() method as:

    \n\n
    let arr = [1, 11, 2, 11, 3, 4, 5, 11, 6, 11];\n    for (let i = 0; i < arr.length-1; i++) {\n       if ( arr[i] === 11) {\n         arr.splice(i, 1);\n       }\n    }\n    console.log(arr);\n    // [1, 2, 3, 4, 5, 6]\n
    \n\n

    Or suppose we want to delete del from the array arr:

    \n\n
    let arr = [1, 2, 3, 4, 5, 6];\nlet del = 4;\nif (arr.indexOf(4) >= 0) {\n    arr.splice(arr.indexOf(4), 1)\n}\n
    \n\n

    Or

    \n\n
    let del = 4;\nfor(var i = arr.length - 1; i >= 0; i--) {\n    if(arr[i] === del) {\n       arr.splice(i, 1);\n    }\n}\n
  6. \n
  7. If you know the element alone but not the position (or index) of the element, and want to delete just very first matching element using splice() method:

    \n\n
    let arr = [1, 11, 2, 11, 3, 4, 5, 11, 6, 11];\n\nfor (let i = 0; i < arr.length-1; i++) {\n  if ( arr[i] === 11) {\n    arr.splice(i, 1);\n    break;\n  }\n}\nconsole.log(arr);\n// [1, 11, 2, 11, 3, 4, 5, 11, 6, 11]\n
  8. \n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225c082fcc3049e9115b", + "creator": "Chang", + "createdAt": 1548618387000, + "text": "Note: Array.prototype.filter is ECMAScript 5.1 (No IE8)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3225c082fcc3049e9115d", + "creator": "Little Brain", + "createdAt": 1559226707000, + "text": "For option 4, should the loop be 'i < arr.length' not 'i < arr.length-1'?", + "upvotes": 867, + "upvoterUsernames": [], + "downvotes": 867, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffe7", + "creator": "Ashish", + "createdAt": 1548672438000, + "text": "

The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements.

\n\n

array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

\n\n

start

\n\n

The index at which to start changing the array (with origin 0). If greater than the length of the array, the actual starting index will be set to the length of the array. If negative, it will begin that many elements from the end of the array (with origin -1) and will be set to 0 if the absolute value is greater than the length of the array.

\n\n

deleteCount Optional

\n\n

An integer indicating the number of old array elements to remove.

\n\n

If deleteCount is omitted, or if its value is larger than array.length - start (that is, if it is greater than the number of elements left in the array, starting at start), then all of the elements from start through the end of the array will be deleted.\nIf deleteCount is 0 or negative, no elements are removed. In this case, you should specify at least one new element (see below).

\n\n

item1, item2, ... Optional

\n\n

The elements to add to the array, beginning at the start index. If you don't specify any elements, splice() will only remove elements from the array.

\n\n

For more references, kindly go through:

\n\n

Array.prototype.splice()

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffe8", + "creator": "alejoko", + "createdAt": 1549118519000, + "text": "

Take profit of reduce method as follows:

\n\n

Case a) if you need to remove an element by index:

\n\n
function remove(arr, index) {\n  return arr.reduce((prev, x, i) => prev.concat(i !== index ? [x] : []), []);\n}\n
\n\n

case b) if you need to remove an element by the value of the element (int):

\n\n
function remove(arr, value) {\n  return arr.reduce((prev, x, i) => prev.concat(x !== value ? [x] : []), []);\n}\n
\n\n

So in this way we can return a new array (will be in a cool functional way - much better than using push or splice) with the element removed.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffea", + "creator": "codepleb", + "createdAt": 1555145490000, + "text": "

Oftentimes it's better to just create a new array with the filter function.

\n\n
let array = [1,2,3,4];\narray = array.filter(i => i !== 4); // [1,2,3]\n
\n\n

This also improves readability IMHO. I'm not a fan of slice, although it know sometimes you should go for it.

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3225d082fcc3049e9115f", + "creator": "MHOOS", + "createdAt": 1561024051000, + "text": "@codepleb, can you elaborate on why you prefer filter over splice and why you think filter is more readable?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3225d082fcc3049e91161", + "creator": "eightyfive", + "createdAt": 1561025246000, + "text": "Albeit not recommended for lengthy arrays.", + "upvotes": 266, + "upvoterUsernames": [], + "downvotes": 266, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8ffe9", + "creator": "Ashish", + "createdAt": 1549344885000, + "text": "

Splice, filter and delete to remove an element from an array

\n\n

Every array has its index, and it helps to delete a particular element with their index.

\n\n

The splice() method

\n\n
array.splice(index, 1);    \n
\n\n

The first parameter is index and the second is the number of elements you want to delete from that index.

\n\n

So for a single element, we use 1.

\n\n

The delete method

\n\n
delete array[index]\n
\n\n

The filter() method

\n\n

If you want to delete an element which is repeated in an array then filter the array:

\n\n
removeAll = array.filter(e => e != elem);\n
\n\n

Where elem is the element you want to remove from the array and array is your array name.

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffeb", + "creator": "Atchutha rama reddy Karri", + "createdAt": 1556080370000, + "text": "
[2,3,5].filter(i => ![5].includes(i))\n
\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffec", + "creator": "mekbib.awoke", + "createdAt": 1566562813000, + "text": "

You can extend the array object to define a custom delete function as follows:

\n\n

\r\n
\r\n
let numbers = [1,2,4,4,5,3,45,9];\r\n\r\nnumbers.delete = function(value){\r\n    var indexOfTarget = this.indexOf(value)\r\n\r\n    if(indexOfTarget !== -1)\r\n    {\r\n        console.log(\"array before delete \" + this)\r\n        this.splice(indexOfTarget, 1)\r\n        console.log(\"array after delete \" + this)\r\n    }\r\n    else{\r\n        console.error(\"element \" + value + \" not found\")\r\n    }\r\n}\r\nnumbers.delete(888)\r\n// Expected output:\r\n// element 888 not found\r\nnumbers.delete(1)\r\n\r\n// Expected output;\r\n// array before delete 1,2,4,4,5,3,45,9\r\n// array after delete 2,4,4,5,3,45,9
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffee", + "creator": "Konrad Borowski", + "createdAt": 1567330969000, + "text": "

Remove single element

\n\n
function removeSingle(array, element) {\n    const index = array.indexOf(element)\n    if (index >= 0) {\n        array.splice(index, 1)\n    }\n}\n
\n\n

Remove multiple elements, in-place

\n\n

This is more complicated to ensure the algorithm runs in O(N) time.

\n\n
function removeAll(array, element) {\n    let newLength = 0\n    for (const elem of array) {\n        if (elem !== number) {\n            array[newLength++] = elem\n        }\n    }\n    array.length = newLength\n}\n
\n\n

Remove multiple elements, creating new object

\n\n
array.filter(elem => elem !== number)\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff2", + "creator": "Ajay Thakur", + "createdAt": 1572844250000, + "text": "

You can create a prototype for that. Just pass the array element and the value which you want to remove from the array element:

\n\n

\r\n
\r\n
Array.prototype.removeItem = function(array,val) {\r\n    array.forEach((arrayItem,index) => {\r\n        if (arrayItem == val) {\r\n            array.splice(index, 1);\r\n        }\r\n    });\r\n    return array;\r\n}\r\nvar DummyArray = [1, 2, 3, 4, 5, 6];\r\nconsole.log(DummyArray.removeItem(DummyArray, 3));
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff1", + "creator": "pritam", + "createdAt": 1570001799000, + "text": "

Most of the answers here give a solution using -

\n\n
    \n
  1. indexOf and splice
  2. \n
  3. delete
  4. \n
  5. filter
  6. \n
  7. regular for loop
  8. \n
\n\n

Although all the solutions should work with these methods, I thought we could use string manipulation.

\n\n

Points to note about this solution -

\n\n
    \n
  1. It will leave holes in the data (they could be removed with an extra filter)
  2. \n
  3. This solution works for not just primitive search values, but also objects.
  4. \n
\n\n

The trick is to -

\n\n
    \n
  1. stringify input data set and the search value
  2. \n
  3. replace the search value in the input data set with an empty string
  4. \n
  5. return split data on delimiter ,.
  6. \n
\n\n
    remove = (input, value) => {\n        const stringVal = JSON.stringify(value);\n        const result = JSON.stringify(input)\n\n        return result.replace(stringVal, \"\").split(\",\");\n    }\n
\n\n

A JSFiddle with tests for objects and numbers is created here - https://jsfiddle.net/4t7zhkce/33/

\n\n

Check the remove method in the fiddle.

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff3", + "creator": "Taylor Hawkes", + "createdAt": 1573071928000, + "text": "

To find and remove a particular string from an array of strings:

\n\n
var colors = [\"red\",\"blue\",\"car\",\"green\"];\nvar carIndex = colors.indexOf(\"car\"); // Get \"car\" index\n// Remove car from the colors array\ncolors.splice(carIndex, 1); // colors = [\"red\", \"blue\", \"green\"]\n
\n\n

Source: https://www.codegrepper.com/?search_term=remove+a+particular+element+from+array

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff4", + "creator": "Josh", + "createdAt": 1576746346000, + "text": "

Remove a specific element from an array can be done in one line with the filter option, and it's supported by all browsers: https://caniuse.com/#search=filter%20array

\n\n
function removeValueFromArray(array, value) {\n    return array.filter(e => e != value)\n}\n
\n\n

I tested this function here: https://bit.dev/joshk/jotils/remove-value-from-array/~code#test.ts

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3229a082fcc3049e91169", + "creator": "Peter Mortensen", + "createdAt": 1583688317000, + "text": "Perhaps qualify "all browsers"?", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8fff5", + "creator": "Arun s", + "createdAt": 1577444314000, + "text": "

You can use splice to remove objects or values from an array.

\n\n

Let's consider an array of length 5, with values 10,20,30,40,50, and I want to remove the value 30 from it.

\n\n

\r\n
\r\n
var array = [10,20,30,40,50];\r\nif (array.indexOf(30) > -1) {\r\n   array.splice(array.indexOf(30), 1);\r\n}\r\nconsole.log(array); // [10,20,40,50]
\r\n
\r\n
\r\n

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff6", + "creator": "Ravi Makwana", + "createdAt": 1578563689000, + "text": "

I found this blog post which is showing nine ways to do it:

\n\n

9 Ways to Remove Elements From A JavaScript Array - Plus How to Safely Clear JavaScript Arrays

\n\n

I prefer to use filter():

\n\n
var filtered_arr = arr.filter(function(ele){\n   return ele != value;\n})\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3229a082fcc3049e9116c", + "creator": "Daniel Waltrip", + "createdAt": 1579745399000, + "text": "This doesn't remove the item from the array. It creates a brand new array with some items from the original array filtered out.", + "upvotes": 1377, + "upvoterUsernames": [], + "downvotes": 1377, + "downvoterUsernames": [] + }, + { + "_id": "62f3229a082fcc3049e9116e", + "creator": "Ravi Makwana", + "createdAt": 1579753834000, + "text": "Yes it will return new array filtered. let me update code", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8fff7", + "creator": "Janardan Prajapati", + "createdAt": 1580213074000, + "text": "

You can use

\n\n
Array.splice(index);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3229a082fcc3049e9116f", + "creator": "majid savalanpour", + "createdAt": 1592128026000, + "text": "Your code remove all elements after index. For remove 1 element at index 3 you can use Array.splice(index, 1).", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8fff8", + "creator": "ifelse.codes", + "createdAt": 1580500607000, + "text": "

If the array contains duplicate values and you want to remove all the occurrences of your target then this is the way to go...

\n\n
let data = [2, 5, 9, 2, 8, 5, 9, 5];\nlet target = 5;\ndata = data.filter(da => da !== target);\n
\n\n

Note: - the filter doesn't change the original array; instead it creates a new array.

\n\n

So assigning again is important.

\n\n

That's led to another problem. You can't make the variable const. It should be let or var.

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fff9", + "creator": "Ahmad", + "createdAt": 1590926283000, + "text": "

An immutable way of removing an element from an array using the ES6 spread operator.

\n

Let's say you want to remove 4.

\n
let array = [1,2,3,4,5]\nconst index = array.indexOf(4)\nlet new_array = [...array.slice(0,index), ...array.slice(index+1, array.length)]\nconsole.log(new_array)\n=> [1, 2, 3, 5]\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fffa", + "creator": "katma", + "createdAt": 1591114696000, + "text": "

An immutable and one-liner way:

\n
const newArr = targetArr.filter(e => e !== elementToDelete);\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fffc", + "creator": "Abdelrhman Arnos", + "createdAt": 1597432303000, + "text": "

In ES6, the Set collection provides a delete method to delete a specific value from the array, then convert the Set collection to an array by spread operator.

\n

\r\n
\r\n
function deleteItem(list, val) {\n    const set = new Set(list);\n    set.delete(val);\n    \n    return [...set];\n}\n\nconst letters = ['A', 'B', 'C', 'D', 'E'];\nconsole.log(deleteItem(letters, 'C')); // ['A', 'B', 'D', 'E']
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fffb", + "creator": "karthicvel", + "createdAt": 1595667029000, + "text": "

Using .indexOf() and .splice() - Mutable Pattern

\n

There are two scenarios here:

\n
    \n
  1. we know the index
  2. \n
\n

\r\n
\r\n
const drinks = [ 'Tea', 'Coffee', 'Milk'];\nconst id = 1;\nconst removedDrink = drinks.splice(id,  1);\nconsole.log(removedDrink)
\r\n
\r\n
\r\n

\n
    \n
  1. we don’t know the index but know the value.\n
    \r\n
    \r\n
    const drinks =  ['Tea','Coffee', 'Milk'];\nconst id = drinks.indexOf('Coffee'); // 1\nconst removedDrink = drinks.splice(id,  1);\n// [\"Coffee\"]\nconsole.log(removedDrink);\n// [\"Tea\", \"Milk\"]\nconsole.log(drinks);
    \r\n
    \r\n
    \r\n
  2. \n
\n

Using .filter() - Immutable Pattern

\n

The best way you can think about this is - instead of “removing” the item, you’ll be “creating” a new array that just does not include that item. So we must find it, and omit it entirely.

\n

\r\n
\r\n
const drinks = ['Tea','Coffee', 'Milk'];\nconst id = 'Coffee';\nconst idx = drinks.indexOf(id);\nconst removedDrink = drinks[idx];\nconst filteredDrinks = drinks.filter((drink, index) => drink == removedDrink);\n\nconsole.log(\"Filtered Drinks Array:\"+ filteredDrinks);\nconsole.log(\"Original Drinks Array:\"+ drinks);
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fffd", + "creator": "Coding", + "createdAt": 1604366463000, + "text": "

The simplest possible way to do this is probably using the filter function. Here's an example:

\n

\r\n
\r\n
let array = [\"hello\", \"world\"]\nlet newarray = array.filter(item => item !== \"hello\");\nconsole.log(newarray);\n// [\"world\"]
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8fffe", + "creator": "MSA", + "createdAt": 1604843418000, + "text": "

The splice() function is able to give you back an item in the array as well as remove item / items from a specific index:

\n

\r\n
\r\n
function removeArrayItem(index, array) {\n    array.splice(index, 1);\n    return array;\n}\n\nlet array = [1,2,3,4];\nlet index = 2;\narray = removeArrayItem(index, array);\nconsole.log(array);
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffff", + "creator": "Tijo John", + "createdAt": 1606314456000, + "text": "\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffed", + "creator": "Kamil Kiełczewski", + "createdAt": 1566966278000, + "text": "

Non in-place solution

\n
arr.slice(0,i).concat(arr.slice(i+1));\n
\n

\r\n
\r\n
let arr = [10, 20, 30, 40, 50]\n\nlet i = 2 ; // position to remove (starting from 0)\nlet r = arr.slice(0,i).concat(arr.slice(i+1));\n\nconsole.log(r);
\r\n
\r\n
\r\n

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329ba082fcc3049e92dbf", + "creator": "Peter Mortensen", + "createdAt": 1586654414000, + "text": "What is the advantage of that?", + "upvotes": 92, + "upvoterUsernames": [], + "downvotes": 92, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bc082fcc3049e8fff0", + "creator": "Yohannes Kristiawan", + "createdAt": 1568804277000, + "text": "

I would like to suggest to remove one array item using delete and filter:

\n

\r\n
\r\n
var arr = [1,2,3,4,5,5,6,7,8,9];\ndelete arr[5];\narr = arr.filter(function(item){ return item != undefined; });\n//result: [1,2,3,4,5,6,7,8,9]\n\nconsole.log(arr)
\r\n
\r\n
\r\n

\n

So, we can remove only one specific array item instead of all items with the same value.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bc082fcc3049e8ffef", + "creator": "user8331407", + "createdAt": 1568760599000, + "text": "

You can create an index with an all accessors example:

\n\n
<div >\n</div>\n
\n\n

\r\n
\r\n
function getIndex($id){\r\n  return (\r\n    this.removeIndex($id)\r\n    alert(\"This element was removed\")\r\n  )\r\n}\r\n\r\n\r\nfunction removeIndex(){\r\n   const index = $id;\r\n   this.accesor.id.splice(index.id) // You can use splice for slice index on\r\n                                    // accessor id and return with message\r\n}
\r\n
<div>\r\n    <fromList>\r\n        <ul>\r\n            {...this.array.map( accesors => {\r\n                <li type=\"hidden\"></li>\r\n                <li>{...accesors}</li>\r\n            })\r\n\r\n            }\r\n        </ul>\r\n    </fromList>\r\n\r\n    <form id=\"form\" method=\"post\">\r\n        <input  id=\"{this.accesors.id}\">\r\n        <input type=\"submit\" callbackforApplySend...getIndex({this.accesors.id}) name=\"sendendform\" value=\"removeIndex\" >\r\n    </form>\r\n</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f93", + "creator": "Ran Turner", + "createdAt": 1613747876000, + "text": "

Using filter is an elegant way to achieve this requirement

\n

\r\n
\r\n
const num = 3;\nlet arr = [1, 2, 3, 4];\narr = arr.filter(x => x !== num);\nconsole.log(arr); // [1, 2, 4]
\r\n
\r\n
\r\n

\n

By the way, filter will remove all of the occurrences matched in the condition (not just the first occurrence) like you can see in the following example

\n

\r\n
\r\n
const num = 3;\nlet arr = [1, 2, 3, 3, 3, 4];\narr = arr.filter(x => x !== num);\nconsole.log(arr); // [1, 2, 4]
\r\n
\r\n
\r\n

\n

In case, you just want to remove the first occurrence, you can use the splice method

\n

\r\n
\r\n
const num = 3;\nlet arr = [1, 2, 3, 3, 3, 4];\narr.splice(arr.indexOf(num), 1);\nconsole.log(arr); // [1, 2, 3, 3, 4]
\r\n
\r\n
\r\n

\n", + "upvotes": 130, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f94", + "creator": "Dishant Chanchad", + "createdAt": 1616750777000, + "text": "

If you're using a modern browser, you can use .filter.

\n

\r\n
\r\n
Array.prototype.remove = function(x){\n    return this.filter(function(v){\n        return v !== x;\n    });\n};\n\nvar a = [\"a\",\"b\",\"c\"];\nvar b = a.remove('a');
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f96", + "creator": "Asker", + "createdAt": 1617749746000, + "text": "

I tested splice and filter to see which is faster:

\n

\r\n
\r\n
let someArr = [...Array(99999).keys()] \n\nconsole.time('filter')\nsomeArr.filter(x => x !== 6666)\nconsole.timeEnd('filter')\n\nconsole.time('splice by indexOf')\nsomeArr.splice(someArr.indexOf(6666), 1)\nconsole.timeEnd('splice by indexOf')
\r\n
\r\n
\r\n

\n

On my machine, splice is faster. This makes sense, as splice merely edits an existing array, whereas filter creates a new array.

\n

That said, filter is logically cleaner (easier to read) and fits better into a coding style that uses immutable state. So it's up to you whether you want to make that trade-off.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4a082fcc3049e9304f", + "creator": "Asker", + "createdAt": 1623918077000, + "text": "@andras I never claimed they were the same. Were you trying to respond to another answer?", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f32a4a082fcc3049e93051", + "creator": "Asker", + "createdAt": 1623918358000, + "text": "@andras Also, for future reference, putting your comment in bold and capitals for emphasis makes it come across as very aggressive.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a0b082fcc3049e92f95", + "creator": "oriadam", + "createdAt": 1617639385000, + "text": "

I like this one-liner:

\n
arr.includes(val) && arr.splice(arr.indexOf(val), 1)\n
\n\n

As a prototype

\n
// remove by value. return true if value found and removed, false otherwise\nArray.prototype.remove = function(val)\n{\n    return this.includes(val) && !!this.splice(this.indexOf(val), 1);\n}\n
\n

(Yes, I read all other answers and couldn't find one that combines includes and splice in the same line.)

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4a082fcc3049e93052", + "creator": "Victor Gavro", + "createdAt": 1619626665000, + "text": ""immutable" means "not mutated" (i.e. returned value is not changing original value), in your case array is actually mutated.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a0b082fcc3049e92f98", + "creator": "shweta ghanate", + "createdAt": 1619291028000, + "text": "

Using the array filter method:

\n
let array = [1, 2, 3, 4, 511, 34, 511, 78, 88];\n\nlet value = 511;\narray = array.filter(element => element !== value);\nconsole.log(array)\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4a082fcc3049e93055", + "creator": "Larphoid", + "createdAt": 1619908269000, + "text": "Very elegant solution! Even works if array elements are objects with properties, so you can do element.property !== value", + "upvotes": 1295, + "upvoterUsernames": [], + "downvotes": 1295, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a0b082fcc3049e92f97", + "creator": "Force Bolt", + "createdAt": 1618819804000, + "text": "

Try this code using the filter method and you can remove any specific item from an array.

\n
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];\nfunction removeItem(arr, value) {\n  return arr.filter(function (ele) {\n    return ele !== value;\n  });\n}\nconsole.log(removeItem(arr, 6));\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f99", + "creator": "Julio Vinachi", + "createdAt": 1624595943000, + "text": "

You could use the standard __proto__ of JavaScript and define this function. For example,

\n

\r\n
\r\n
let data = [];\ndata.__proto__.remove = (n) => { data = data.flatMap((v) => { return v !== n ? v : []; }) };\n\ndata = [1, 2, 3];\ndata.remove(2);\nconsole.log(data); // [1,3]\n\ndata = ['a','b','c'];\ndata.remove('b');\nconsole.log(data); // [a,c]
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9a", + "creator": "jnbm", + "createdAt": 1632148361000, + "text": "

You can add a prototype function to "remove" the element from the array.

\n

The following example shows how to simply remove an element from an array, when we know the index of the element. We use it in the Array.filter method.

\n

\r\n
\r\n
Array.prototype.removeByIndex = function(i) {\n    if(!Number.isInteger(i) || i < 0) {\n        // i must be an integer\n        return this;\n    }\n\n    return this.filter((f, indx) => indx !== i)\n}\nvar a = [5, -89, (2 * 2), \"some string\", null, false, undefined, 20, null, 5];\n\nvar b = a.removeByIndex(2);\nconsole.log(a);\nconsole.log(b);
\r\n
\r\n
\r\n

\n

Sometimes we don't know the index of the element.

\n

\r\n
\r\n
Array.prototype.remove = function(i) {\n    return this.filter(f => f !== i)\n}\nvar a = [5, -89, (2 * 2), \"some string\", null, false, undefined, 20, null, 5];\n\nvar b = a.remove(5).remove(null);\nconsole.log(a);\nconsole.log(b);\n\n// It removes all occurrences of searched value
\r\n
\r\n
\r\n

\n

But, when we want to remove only the first occurrence of the searched value, we can use the Array.indexOf method in our function.

\n

\r\n
\r\n
Array.prototype.removeFirst = function(i) {\n    i = this.indexOf(i);\n\n    if(!Number.isInteger(i) || i < 0) {\n        return this;\n    }\n\n    return this.filter((f, indx) => indx !== i)\n}\nvar a = [5, -89, (2 * 2), \"some string\", null, false, undefined, 20, null, 5];\n\nvar b = a.removeFirst(5).removeFirst(null);\nconsole.log(a);\nconsole.log(b);
\r\n
\r\n
\r\n

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9b", + "creator": "Emmanuel Onah", + "createdAt": 1632472915000, + "text": "
 (function removeFromArrayPolyfill() {\n      if (window.Array.prototype.remove) return;\n    \n      Array.prototype.remove = function (value) {\n        if (!this.length || !value) return;\n    \n        const indexOfValue = this.indexOf(value);\n    \n        if (indexOfValue >= 0) {\n          this.splice(indexOfValue, 1);\n        }\n      };\n    })();\n    \n    // testing polyfill\n    const nums = [10, 20, 30];\n    nums.remove(20);\n    console.log(nums);//[10,30]\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9c", + "creator": "k26dr", + "createdAt": 1635995449000, + "text": "

You can use a Set instead and use the delete function:

\n
const s = Set;\ns.add('hello');\ns.add('goodbye');\ns.delete('hello');\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9d", + "creator": "user17401373", + "createdAt": 1636787080000, + "text": "
    \n
  1. Get the array and index
  2. \n
  3. From arraylist with array elements
  4. \n
  5. Remove the specific index element using remove() method
  6. \n
  7. From the new array of the array list using maptoint() and toarray() method
  8. \n
  9. Return the formatted array
  10. \n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9e", + "creator": "Enrico König", + "createdAt": 1639488980000, + "text": "

It depends on whether you want to keep an empty spot or not.

\n

If you do want an empty slot:

\n
array[index] = undefined;\n
\n

If you don't want an empty slot:

\n

To keep the original:

\n
oldArray = [...array];\n
\n

This modifies the array.

\n
array.splice(index, 1);\n
\n

And if you need the value of that item, you can just store the returned array's element:

\n
var value = array.splice(index, 1)[0];\n
\n

If you want to remove at either end of the array, you can use array.pop() for the last one or array.shift() for the first one (both return the value of the item as well).

\n

If you don't know the index of the item, you can use array.indexOf(item) to get it (in a if() to get one item or in a while() to get all of them).\narray.indexOf(item) returns either the index or -1 if not found.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa0", + "creator": "Erçin Dedeoğlu", + "createdAt": 1640611542000, + "text": "

Definition:

\n
function RemoveEmptyItems(arr) {\n  var result = [];\n  for (var i = 0; i < arr.length; i++) if (arr[i] != null && arr[i].length > 0) result.push(arr[i]);\n  return result;\n}\n
\n

Usage:

\n
var arr = [1,2,3, "", null, 444];\narr = RemoveEmptyItems(arr);\nconsole.log(arr);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa2", + "creator": "shekhar chander", + "createdAt": 1641970028000, + "text": "

The cleanest of all :

\n

\r\n
\r\n
var arr = ['1','2','3'];\narr = arr.filter(e => e !== '3');\nconsole.warn(arr);
\r\n
\r\n
\r\n

\n

This will also delete duplicates (if any).

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa4", + "creator": "Ali Raza", + "createdAt": 1643119807000, + "text": "

Best way to remove item from array is to user filter method.\n.filter() return new array without filtered item.

\n
items = items.filter(e => e.id !== item.id);\n
\n

This .filter() method map to complete array and when i return true condition it push that current item to filtered array.\nread more on filter HERE

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa1", + "creator": "Enrico König", + "createdAt": 1641397249000, + "text": "

The most basic solution would be:

\n
array.key = null;\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa5", + "creator": "Hangover Sound Sound", + "createdAt": 1643802720000, + "text": "

Beside all this solutions it can also be done with array.reduce...

\n
const removeItem = \n    idx => \n    arr => \n    arr.reduce((acc, a, i) =>  idx === i ? acc : acc.concat(a), [])\n\nconst array = [1, 2, 3]\nconst index = 1\n\nconst newArray = removeItem(index)(array) \n\nconsole.log(newArray) // logs the following array to the console : [1, 3]\n
\n

... or a recursive function (which is to be honest not that elegant...has maybe someone a better recursive solution ??)...

\n
const removeItemPrep = \n    acc => \n    i => \n    idx => \n    arr => \n\n    // If the index equals i, just feed in the unchanged accumulator(acc) else...\n    i === idx ? removeItemPrep(acc)(i + 1)(idx)(arr) :\n\n    // If the array length + 1 of the accumulator is smaller than the array length of the original array concatenate the array element at index i else... \n    acc.length + 1 < arr.length ? removeItemPrep(acc.concat(arr[i]))(i + 1)(idx)(arr) : \n\n    // return the accumulator\n    acc \n\nconst removeItem = removeItemPrep([])(0)\n\nconst array = [1, 2, 3]\nconst index = 1\n\nconst newArray = removeItem(index)(array) \n\nconsole.log(newArray) // logs the following array to the console : [1, 3]\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f9f", + "creator": "guogangj", + "createdAt": 1640163289000, + "text": "

Lodash:

\n
let a1 = {name:'a1'}\nlet a2 = {name:'a2'}\nlet a3 = {name:'a3'}\nlet list = [a1, a2, a3]\n_.remove(list, a2)\n//list now is [{name: "a1"}, {name: "a3"}]\n
\n

Check this for details: .remove(array, [predicate=.identity])

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa3", + "creator": "Rahul Daksh", + "createdAt": 1642787376000, + "text": "

Try to remove the array element using the delete operator

\n

For an instance:

\n
const arr = [10, 20, 30, 40, 50, 60];\ndelete arr[2]; // It will Delete element present at index 2\nconsole.log( arr ); // [10, 20, undefined , 40, 50, 60]\n
\n

Note: Using delete operator will leave empty spaces/ holes in an array. It will not alert the length of an array. To change the length of an array when the element is deleted from it, use splice method instead.

\n

Hope this could resolve all your problems.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa6", + "creator": "Mohammed Shabeer k", + "createdAt": 1644056563000, + "text": "
function array_remove(arr, index) {\n    for (let i = index; i < arr.length - 1; i++) {\n        arr[i] = arr[i + 1];\n    }\n    arr.length -= 1;\n    return arr;\n}\nmy_arr = ['A', 'B', 'C', 'D'];\nconsole.log(array_remove(my_arr, 0));\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4c082fcc3049e93061", + "creator": "radrow", + "createdAt": 1644509148000, + "text": "Could you provide some explanation?", + "upvotes": 49, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a0b082fcc3049e92fa8", + "creator": "Sahil Thummar", + "createdAt": 1646289223000, + "text": "
    \n
  1. Using indexOf, can find a specific index of number from array\n
      \n
    • using splice, can remove a specific index from array.
    • \n
    \n
  2. \n
\n

\r\n
\r\n
const array = [1,2,3,4,5,6,7,8,9,0];\nconst index = array.indexOf(5);\n// find Index of specific number\nif(index != -1){\n    array.splice(index, 1); // remove number using index\n}\nconsole.log(array);
\r\n
\r\n
\r\n

\n
    \n
  1. To remove all occurrences.
  2. \n
\n

\r\n
\r\n
let array = [1, 2, 3, 4, 5, 1, 7, 8, 9, 2, 3, 4, 5, 6];\narray = array.filter(number=> number !== 5);\nconsole.log(array);
\r\n
\r\n
\r\n

\n
    \n
  1. Using Join and split

    \n

    \r\n
    \r\n
    let array = [1, 2, 3, 4, 5, 1, 7, 8, 9, 2, 3, 4, 5, 6]\narray = Array.from(array.join(\"-\").split(\"-5-\").join(\"-\").split(\"-\"),Number)\nconsole.log(array)
    \r\n
    \r\n
    \r\n

    \n
  2. \n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa7", + "creator": "Aayush Bhattacharya", + "createdAt": 1645163841000, + "text": "

Filter with different types of array

\n
    **simple array**\n    const arr = ['1','2','3'];\n    const updatedArr = arr.filter(e => e !== '3');\n    console.warn(updatedArr);\n\n    **array of object**\n    const newArr = [{id:1,name:'a'},{id:2,name:'b'},{id:3,name:'c'}]\n    const updatedNewArr = newArr.filter(e => e.id !== 3);\n    console.warn(updatedNewArr);\n\n    **array of object with different parameter name**\n    const newArr = [{SINGLE_MDMC:{id:1,cout:10}},{BULK_MDMC:{id:1,cout:15}},{WPS_MDMC:{id:2,cout:10}},]\n    const newArray = newArr.filter((item) => !Object.keys(item).includes('SINGLE_MDMC'));\n    console.log(newArray)\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fa9", + "creator": "amir yeganeh", + "createdAt": 1647013486000, + "text": "

there are multiple ways to do it and its u to you how you want it to act

\n

one approach is to use the splice method and remove the item from the array

\n
let array = [1,2,3]\narray.splice(1,1);\nconsole.log(array)\n\n// return [1,3]\n
\n

but make sure you pass the second argument or you end up deleting the whole array after the index

\n

second approach is to use filter method and the benefit with it is that it is immutable which means your main array doesn't get manipulated

\n
const array = [1,2,3];\nconst newArray = array.filter(item => item !== 2)\nconsole.log(newArray)\n\n// return [1,3]\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92faa", + "creator": "Weilory", + "createdAt": 1652099289000, + "text": "

for removing only the first 34 from ages, not all the ages 34

\n
ages.splice(ages.indexOf(34), 1);\n
\n

or you can define a method globally:

\n
function remove(array, item){\n    let ind = array.indexOf(item);\n    if(ind !== -1)array.splice(ind, 1);\n}\n
\n

for removing all the ages 34

\n
ages = ages.filter(a => a !== 34);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32a4c082fcc3049e93066", + "creator": "JaeIL Ryu", + "createdAt": 1652947959000, + "text": "that doesn't matter. simple and ez answer.", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + }, + { + "_id": "62f32a4c082fcc3049e93068", + "creator": "Neil", + "createdAt": 1655561317000, + "text": "Why are you using != instead of !==?", + "upvotes": 742, + "upvoterUsernames": [], + "downvotes": 742, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f32a0b082fcc3049e92fab", + "creator": "Artur Müller Romanov", + "createdAt": 1652171787000, + "text": "

In case you want to remove several items, I found this to be the easiest:

\n
const oldArray = [1, 2, 3, 4, 5]\nconst removeItems = [1, 3, 5]\n\nconst newArray = oldArray.filter((value) => {\n    return !removeItems.includes(value)\n})\n\nconsole.log(newArray)\n
\n

output:

\n
[2, 4]\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fac", + "creator": "Jose Pedro Febian", + "createdAt": 1652198246000, + "text": "

this is my simple code to remove spesific data in array use splice method. the splice method will given 2 parameter, the first parameter is start number, and the second parameter is deleteCount. The second parameter use for remove some element start from the value of first parameter.

\n
let arr = [1,3,5,6,9];\n\narr.splice(0,2);\n\nconsole.log(arr);\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fae", + "creator": "user6689187", + "createdAt": 1655344620000, + "text": "
const newArray = oldArray.filter(item => item !== removeItem);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fad", + "creator": "Navin Singh rangar", + "createdAt": 1655172994000, + "text": "

To remove an item by passing its value-

\n
const remove=(value)=>{\n    myArray = myArray.filter(element=>element !=value);\n\n}\n
\n

To remove an item by passing its index number -

\n
  const removeFrom=(index)=>{\n    myArray = myArray.filter((_, i)=>{\n        return i!==index\n    })\n}\n
\n", + "upvotes": 504, + "upvoterUsernames": [], + "downvotes": 504, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92faf", + "creator": "Himanshu Jangid", + "createdAt": 1655355548000, + "text": "

You can do this task in many ways in javascript

\n
    \n
  1. If you know the index of the value: in\nthis case you can use splice
  2. \n
\n
var arr = [1,2,3,4]\n// let's say we have the index, comming from some api\nlet index = 2;\n// splice is a destructive method and modifies the original array\narr.splice(2, 1)\n
\n
    \n
  1. If you don't have the index and only have the value:\nin this case you can use filter
  2. \n
\n
// let's remove 2 for example \narr = arr.filter((value)=>{\n    return value !== 2;\n})\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fb0", + "creator": "Shavleg Kakulia", + "createdAt": 1657777059000, + "text": "

for example you have characters array and want to delete "A" from array.

\n

Array has filter method which can filter and return only that elements what do you want.

\n

let CharacterArray = ['N', 'B', 'A'];

\n

I want to return elements apart from 'A'.

\n

CharacterArray = CharacterArray.filter(character => character !== 'A');

\n

Then CharacterArray must be: ['N', 'B']

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92fb1", + "creator": "Aslam khan", + "createdAt": 1658145675000, + "text": "

Simple just do it:

\n
var arr = [1,2,4,5];\narr.splice(arr.indexOf(5), 1);\nconsole.log(arr); // [1,2,4];\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f91", + "creator": "Giacomo Casadei", + "createdAt": 1610834145000, + "text": "

This function removes an element from an array from a specific position.

\n

array.remove(position);

\n

\r\n
\r\n
Array.prototype.remove = function (pos) {\n    this.splice(pos, 1);\n}\n\nvar arr = [\"a\", \"b\", \"c\", \"d\", \"e\"];\narr.remove(2); // remove \"c\"\nconsole.log(arr);
\r\n
\r\n
\r\n

\n

If you don't know the location of the item to delete use this:

\n
array.erase(element);\n
\n

\r\n
\r\n
Array.prototype.erase = function(el) {\n    let p = this.indexOf(el); // indexOf use strict equality (===)\n    if(p != -1) {\n        this.splice(p, 1);\n    }\n}\n\nvar arr = [\"a\", \"b\", \"c\", \"d\", \"e\"];\narr.erase(\"c\");\nconsole.log(arr);
\r\n
\r\n
\r\n

\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f32a0b082fcc3049e92f92", + "creator": "Masoud Aghaei", + "createdAt": 1611778209000, + "text": "

You just need filter by element or index:

\n

\r\n
\r\n
var num = [5, 6, 5, 4, 5, 1, 5];\n\nvar result1 = num.filter((el, index) => el != 5) // for remove all 5\nvar result2 = num.filter((el, index) => index != 5) // for remove item with index == 5\n\nconsole.log(result1);\nconsole.log(result2);
\r\n
\r\n
\r\n

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321bb082fcc3049e8ff0d", + "creator": "Ikem Krueger", + "createdAt": 1634939838000, + "text": "array.filter((_, index) => index != number);", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f321bb082fcc3049e8ff0e", + "creator": "Vaulstein", + "createdAt": 1637914224000, + "text": "Anyone checking this question, do see the problems associated with using delete for arrays mentioned by Sasa", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 10288354, + "uvac": 10288480 + } + }, + { + "_id": "62f321bb082fcc3049e8fef9", + "title": ".prop() vs .attr()", + "title-lowercase": ".prop() vs .attr()", + "creator": "Naftali", + "createdAt": 1304451208000, + "status": "open", + "text": "

So jQuery 1.6 has the new function prop().

\n\n
$(selector).click(function(){\n    //instead of:\n    this.getAttribute('style');\n    //do i use:\n    $(this).prop('style');\n    //or:\n    $(this).attr('style');\n})\n
\n\n

or in this case do they do the same thing?

\n\n

And if I do have to switch to using prop(), all the old attr() calls will break if i switch to 1.6?

\n\n

UPDATE

\n\n

\r\n
\r\n
selector = '#id'\r\n\r\n$(selector).click(function() {\r\n    //instead of:\r\n    var getAtt = this.getAttribute('style');\r\n    //do i use:\r\n    var thisProp = $(this).prop('style');\r\n    //or:\r\n    var thisAttr = $(this).attr('style');\r\n\r\n    console.log(getAtt, thisProp, thisAttr);\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js\"></script>\r\n<div id='id' style=\"color: red;background: orange;\">test</div>
\r\n
\r\n
\r\n

\n\n

(see also this fiddle: http://jsfiddle.net/maniator/JpUF2/)

\n\n

The console logs the getAttribute as a string, and the attr as a string, but the prop as a CSSStyleDeclaration, Why? And how does that affect my coding in the future?

\n", + "upvotes": 3235, + "upvoterUsernames": [], + "downvotes": 786, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 693476, + "answers": 15, + "answerItems": [ + { + "_id": "62f321cb082fcc3049e90d99", + "creator": "Arnaud F.", + "createdAt": 1304451347000, + "text": "

All is in the doc:

\n\n
\n

The difference between attributes and properties can be important in specific situations. Before jQuery 1.6, the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior. As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() retrieves attributes.

\n
\n\n

So use prop!

\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9b", + "creator": "yunzen", + "createdAt": 1317142509000, + "text": "

A property is in the DOM; an attribute is in the HTML that is parsed into the DOM.

\n

Further detail

\n

If you change an attribute, the change will be reflected in the DOM (sometimes with a different name).

\n

Example: Changing the class attribute of a tag will change the className property of that tag in the DOM (That's because class is already used).\nIf you have no attribute on a tag, you still have the corresponding DOM property with an empty or a default value.

\n

Example: While your tag has no class attribute, the DOM property className does exist with a empty string value.

\n

edit

\n

If you change the one, the other will be changed by a controller, and vice versa.\nThis controller is not in jQuery, but in the browser's native code.

\n", + "upvotes": 264, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9a", + "creator": "Gary Green", + "createdAt": 1305800317000, + "text": "

It's just the distinction between HTML attributes and DOM objects that causes a confusion. For those that are comfortable acting on the DOM elements native properties such a this.src this.value this.checked etc, .prop is a very warm welcome to the family. For others, it's just an added layer of confusion. Let's clear that up.

\n

The easiest way to see the difference between .attr and .prop is the following example:

\n
<input blah="hello">\n
\n
    \n
  1. $('input').attr('blah'): returns 'hello' as expected. No suprises here.
  2. \n
  3. $('input').prop('blah'): returns undefined -- because it's trying to do [HTMLInputElement].blah -- and no such property on that DOM object exists. It only exists in the scope as an attribute of that element i.e. [HTMLInputElement].getAttribute('blah')
  4. \n
\n

Now we change a few things like so:

\n
$('input').attr('blah', 'apple');\n$('input').prop('blah', 'pear');\n
\n
    \n
  1. $('input').attr('blah'): returns 'apple' eh? Why not "pear" as this was set last on that element. Because the property was changed on the input attribute, not the DOM input element itself -- they basically almost work independently of each other.
  2. \n
  3. $('input').prop('blah'): returns 'pear'
  4. \n
\n

The thing you really need to be careful with is just do not mix the usage of these for the same property throughout your application for the above reason.

\n

See a fiddle demonstrating the difference: http://jsfiddle.net/garreh/uLQXc/

\n
\n

.attr vs .prop:

\n

Round 1: style

\n
<input style="font:arial;"/>\n
\n\n

Round 2: value

\n
<input value="hello" type="text"/>   \n\n$('input').prop('value', 'i changed the value');\n
\n\n

* Note: jQuery for this reason has a .val() method, which internally is equivalent to .prop('value')

\n", + "upvotes": 248, + "upvoterUsernames": [], + "downvotes": 104, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9d", + "creator": "Ciro Santilli Путлер Капут 六四事", + "createdAt": 1404647012000, + "text": "

Dirty checkedness

\n\n

This concept provides an example where the difference is observable: http://www.w3.org/TR/html5/forms.html#concept-input-checked-dirty

\n\n

Try it out:

\n\n\n\n

\r\n
\r\n
$('button').on('click', function() {\r\n  $('#attr').attr('checked', 'checked')\r\n  $('#prop').prop('checked', true)\r\n})
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<label>attr <input id=\"attr\" type=\"checkbox\"></label>\r\n<label>prop <input id=\"prop\" type=\"checkbox\"></label>\r\n<button type=\"button\">Set checked attr and prop.</button>
\r\n
\r\n
\r\n

\n\n

For some attributes like disabled on button, adding or removing the content attribute disabled=\"disabled\" always toggles the property (called IDL attribute in HTML5) because http://www.w3.org/TR/html5/forms.html#attr-fe-disabled says:

\n\n
\n

The disabled IDL attribute must reflect the disabled content attribute.

\n
\n\n

so you might get away with it, although it is ugly since it modifies HTML without need.

\n\n

For other attributes like checked=\"checked\" on input type=\"checkbox\", things break, because once you click on it, it becomes dirty, and then adding or removing the checked=\"checked\" content attribute does not toggle checkedness anymore.

\n\n

This is why you should use mostly .prop, as it affects the effective property directly, instead of relying on complex side-effects of modifying the HTML.

\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9c", + "creator": "naor", + "createdAt": 1391373411000, + "text": "

Usually you'll want to use properties.\nUse attributes only for:

\n\n
    \n
  1. Getting a custom HTML attribute (since it's not synced with a DOM property).
  2. \n
  3. Getting a HTML attribute that doesn't sync with a DOM property, e.g. get the \"original value\" of a standard HTML attribute, like <input value=\"abc\">.
  4. \n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9e", + "creator": "agjmills", + "createdAt": 1405503952000, + "text": "

TL;DR

\n\n

Use prop() over attr() in the majority of cases.

\n\n

A property is the current state of the input element. An attribute is the default value.

\n\n

A property can contain things of different types. An attribute can only contain strings

\n", + "upvotes": 107, + "upvoterUsernames": [], + "downvotes": 50, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da1", + "creator": "zawhtut", + "createdAt": 1444898336000, + "text": "

Gary Hole answer is very relevant to solve the problem if the code is written in such way

\n\n
obj.prop(\"style\",\"border:1px red solid;\")\n
\n\n

Since the prop function return CSSStyleDeclaration object, above code will not working properly in some browser(tested with IE8 with Chrome Frame Plugin in my case).

\n\n

Thus changing it into following code

\n\n
obj.prop(\"style\").cssText = \"border:1px red solid;\"\n
\n\n

solved the problem.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da0", + "creator": "Evgenia Karunus", + "createdAt": 1434088616000, + "text": "

attributes are in your HTML text document/file (== imagine this is the result of your html markup parsed), whereas
\nproperties are in HTML DOM tree (== basically an actual property of some object in JS sense).

\n\n

Importantly, many of them are synced (if you update class property, class attribute in html will also be updated; and otherwise). But some attributes may be synced to unexpected properties - eg, attribute checked corresponds to property defaultChecked, so that

\n\n\n\n
\n

Rule of thumb is: .prop() method should be used for boolean attributes/properties and for properties which do not exist in html\n (such as window.location). All other attributes (ones you can see in\n the html) can and should continue to be manipulated with the .attr()\n method. (http://blog.jquery.com/2011/05/10/jquery-1-6-1-rc-1-released/)

\n
\n\n

And here is a table that shows where .prop() is preferred (even though .attr() can still be used).

\n\n

\"table

\n\n
\n\n

Why would you sometimes want to use .prop() instead of .attr() where latter is officially adviced?

\n\n
    \n
  1. .prop() can return any type - string, integer, boolean; while .attr() always returns a string.
  2. \n
  3. .prop() is said to be about 2.5 times faster than .attr().
  4. \n
\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90d9f", + "creator": "NkS", + "createdAt": 1419600535000, + "text": "

attributes -> HTML

\n\n

properties -> DOM

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da2", + "creator": "user2657778", + "createdAt": 1445098372000, + "text": "

Gently reminder about using prop(), example:

\n\n
if ($(\"#checkbox1\").prop('checked')) {\n    isDelete = 1;\n} else {\n    isDelete = 0;\n}\n
\n\n

The function above is used to check if checkbox1 is checked or not, if checked: return 1; if not: return 0. Function prop() used here as a GET function.

\n\n
if ($(\"#checkbox1\").prop('checked', true)) {\n    isDelete = 1;\n} else {\n    isDelete = 0;\n}\n
\n\n

The function above is used to set checkbox1 to be checked and ALWAYS return 1. Now function prop() used as a SET function.

\n\n

Don't mess up.

\n\n

P/S: When I'm checking Image src property. If the src is empty, prop return the current URL of the page (wrong), and attr return empty string (right).

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da3", + "creator": "Peter Girnus", + "createdAt": 1445731540000, + "text": "

Before jQuery 1.6 , the attr() method sometimes took property values into account when retrieving attributes, this caused rather inconsistent behavior.

\n\n

The introduction of the prop() method provides a way to explicitly retrieve property values, while .attr() retrieves attributes.

\n\n

The Docs:

\n\n

jQuery.attr()\nGet the value of an attribute for the first element in the set of matched elements.

\n\n

jQuery.prop()\nGet the value of a property for the first element in the set of matched elements.

\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da5", + "creator": "Parth Trivedi", + "createdAt": 1450430907000, + "text": "
\n

1) A property is in the DOM; an attribute is in the HTML that is\n parsed into the DOM.

\n \n

2) $( elem ).attr( \"checked\" ) (1.6.1+) \"checked\" (String) Will\n change with checkbox state

\n \n

3) $( elem ).attr( \"checked\" ) (pre-1.6) true (Boolean) Changed\n with checkbox state

\n
\n\n\n\n

\r\n
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js\"></script>\r\n<!doctype html>\r\n<html lang=\"en\">\r\n\r\n<head>\r\n  <meta charset=\"utf-8\">\r\n  <title>prop demo</title>\r\n  <style>\r\n    p {\r\n      margin: 20px 0 0;\r\n    }\r\n    b {\r\n      color: blue;\r\n    }\r\n  </style>\r\n\r\n</head>\r\n\r\n<body>\r\n\r\n  <input id=\"check1\" type=\"checkbox\" checked=\"checked\">\r\n  <label for=\"check1\">Check me</label>\r\n  <p></p>\r\n\r\n  <script>\r\n    $(\"input\").change(function() {\r\n      var $input = $(this);\r\n      $(\"p\").html(\r\n        \".attr( \\\"checked\\\" ): <b>\" + $input.attr(\"checked\") + \"</b><br>\" +\r\n        \".prop( \\\"checked\\\" ): <b>\" + $input.prop(\"checked\") + \"</b><br>\" +\r\n        \".is( \\\":checked\\\" ): <b>\" + $input.is(\":checked\")) + \"</b>\";\r\n    }).change();\r\n  </script>\r\n\r\n</body>\r\n\r\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da4", + "creator": "Kgn-web", + "createdAt": 1449566089000, + "text": "

.attr():

\n\n\n\n

.prop():

\n\n\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321cb082fcc3049e90da7", + "creator": "Rishu Ranjan", + "createdAt": 1589475496000, + "text": "

There are few more considerations in prop() vs attr():

\n\n\n", + "upvotes": 1364, + "upvoterUsernames": [], + "downvotes": 1364, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3288d082fcc3049e92888", + "creator": "Naftali", + "createdAt": 1613505181000, + "text": "I am 49% sure that the attr method does not call prop internally", + "upvotes": 640, + "upvoterUsernames": [], + "downvotes": 640, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321cb082fcc3049e90da6", + "creator": "Bob Stein", + "createdAt": 1560285077000, + "text": "

One thing .attr() can do that .prop() can't: affect CSS selectors

\n\n

Here's an issue I didn't see in the other answers.

\n\n

CSS selector [name=value]

\n\n\n\n

.prop() affects only a few attribute-selectors

\n\n\n\n

.attr() affects all attribute-selectors

\n\n\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 8, + "commentItems": [ + { + "_id": "62f321ca082fcc3049e90d48", + "creator": "user1385191", + "createdAt": 1304451826000, + "text": "@Neal, it's because this change transcends jQuery. The difference between HTML attributes and DOM properties is massive.", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d49", + "creator": "Naftali", + "createdAt": 1304451865000, + "text": "@Matt but what was attr returning before 1.6? wasnt it always returning a string?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4a", + "creator": "user1385191", + "createdAt": 1305308159000, + "text": "It makes me sad to see jQuery's reverted the changes. They're heading in the wrong direction.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4b", + "creator": "user1385191", + "createdAt": 1305308259000, + "text": "@Neal. Yes, and it just complicates the problem further instead of separating the two methods.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4c", + "creator": "Mark Schultheiss", + "createdAt": 1360592698000, + "text": "Depricated attr in 1.9.0+ removed.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4d", + "creator": "Bergi", + "createdAt": 1473036187000, + "text": "see also Properties and Attributes in HTML", + "upvotes": 855, + "upvoterUsernames": [], + "downvotes": 855, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4e", + "creator": "Bergi", + "createdAt": 1473076889000, + "text": "@Neal Dunno, it doesn't focus as much on jQuery as this question here, so it might often be better dupe target.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f321ca082fcc3049e90d4f", + "creator": "Naftali", + "createdAt": 1473106184000, + "text": "@Bergi the answers here actually do not even reference jquery that much and is basically the canonical point for don properties vs attributes", + "upvotes": 345, + "upvoterUsernames": [], + "downvotes": 345, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 696719, + "uvac": 696734 + } + }, + { + "_id": "62f321bb082fcc3049e8feee", + "title": "Scroll to an element with jQuery", + "title-lowercase": "scroll to an element with jquery", + "creator": "DiegoP.", + "createdAt": 1310550584000, + "status": "open", + "text": "

I have this input element:

\n
  <input type="text" class="textfield" value="" id="subject" name="subject">\n
\n

Then I have some other elements, like other tag's & <textarea> tag's, etc...

\n

When the user clicks on the <input id="#subject">, the page should scroll to the page's last element, and it should do so with a nice animation (It should be a scroll to bottom and not to top).

\n

The last item of the page is a submit button with #submit:

\n
<input type="submit" class="submit" id="submit" name="submit" value="Ok, Done.">\n
\n

The animation should not be too fast and should be fluid.

\n

I am running the latest jQuery version. I prefer to not install any plugin but to use the default jQuery features to achieve this.

\n", + "upvotes": 4188, + "upvoterUsernames": [], + "downvotes": 1584, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 2981480, + "answers": 32, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90bb4", + "creator": "Timothy Perez", + "createdAt": 1349427048000, + "text": "

jQuery .scrollTo(): View - Demo, API, Source

\n

I wrote this lightweight plugin to make page/element scrolling much easier. It's flexible where you could pass in a target element or specified value. Perhaps this could be part of jQuery's next official release, what do you think?

\n
\n

Examples Usage:

\n
$('body').scrollTo('#target'); // Scroll screen to target element\n\n$('body').scrollTo(500); // Scroll screen 500 pixels down\n\n$('#scrollable').scrollTo(100); // Scroll individual element 100 pixels down\n
\n
\n

Options:

\n

scrollTarget: A element, string, or number which indicates desired scroll position.

\n

offsetTop: A number that defines additional spacing above scroll target.

\n

duration: A string or number determining how long the animation will run.

\n

easing: A string indicating which easing function to use for the transition.

\n

complete: A function to call once the animation is complete.

\n", + "upvotes": 846, + "upvoterUsernames": [], + "downvotes": 285, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ab082fcc3049e9251a", + "creator": "kiranvj", + "createdAt": 1373040499000, + "text": "$('body') didn't work in FF, so tried $('html, body') which worked.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bb5", + "creator": "Warface", + "createdAt": 1378382034000, + "text": "

Using this simple script

\n\n
if($(window.location.hash).length > 0){\n        $('html, body').animate({ scrollTop: $(window.location.hash).offset().top}, 1000);\n}\n
\n\n

Would make in sort that if a hash tag is found in the url, the scrollTo animate to the ID. If not hash tag found, then ignore the script.

\n", + "upvotes": 70, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bb7", + "creator": "Tejasvi Hegde", + "createdAt": 1391701149000, + "text": "

The solution by Steve and Peter works very well.

\n\n

But in some cases, you may have to convert the value to an integer. Strangely, the returned value from $(\"...\").offset().top is sometimes in float.
\nUse: parseInt($(\"....\").offset().top)

\n\n

For example:

\n\n
$(\"#button\").click(function() {\n    $('html, body').animate({\n        scrollTop: parseInt($(\"#elementtoScrollToID\").offset().top)\n    }, 2000);\n});\n
\n", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 15, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bb8", + "creator": "user669677", + "createdAt": 1392214948000, + "text": "
$('html, body').animate({scrollTop: \n  Math.min( \n    $(to).offset().top-margintop, //margintop is the margin above the target\n    $('body')[0].scrollHeight-$('body').height()) //if the target is at the bottom\n}, 2000);\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bb3", + "creator": "Steve", + "createdAt": 1310550756000, + "text": "

Assuming you have a button with the id button, try this example:

\n
$("#button").click(function() {\n    $([document.documentElement, document.body]).animate({\n        scrollTop: $("#elementtoScrollToID").offset().top\n    }, 2000);\n});\n
\n

I got the code from the article Smoothly scroll to an element without a jQuery plugin. And I have tested it on the example below.

\n

\r\n
\r\n
<html>\n    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js\"></script>\n    <script>\n        $(document).ready(function (){\n            $(\"#click\").click(function (){\n                $('html, body').animate({\n                    scrollTop: $(\"#div1\").offset().top\n                }, 2000);\n            });\n        });\n    </script>\n    <div id=\"div1\" style=\"height: 1000px; width 100px\">\n        Test\n    </div>\n    <br/>\n    <div id=\"div2\" style=\"height: 1000px; width 100px\">\n        Test 2\n    </div>\n    <button id=\"click\">Click me</button>\n</html>
\r\n
\r\n
\r\n

\n", + "upvotes": 4896, + "upvoterUsernames": [], + "downvotes": 482, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327ab082fcc3049e9251e", + "creator": "WASasquatch", + "createdAt": 1648941742000, + "text": "For some reason when I use this method, the scroll is locked in at the location. I can't scroll anywhere else.", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bb6", + "creator": "Atharva", + "createdAt": 1387884937000, + "text": "

If you are not much interested in the smooth scroll effect and just interested in scrolling to a particular element, you don't require some jQuery function for this. Javascript has got your case covered:

\n\n

https://developer.mozilla.org/en-US/docs/Web/API/element.scrollIntoView

\n\n

So all you need to do is: $(\"selector\").get(0).scrollIntoView();

\n\n

.get(0) is used because we want to retrieve the JavaScript's DOM element and not the JQuery's DOM element.

\n", + "upvotes": 825, + "upvoterUsernames": [], + "downvotes": 383, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ab082fcc3049e92520", + "creator": "RobW", + "createdAt": 1395082979000, + "text": "Could you also use $(selector)[0]?", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f327ab082fcc3049e92522", + "creator": "Brian", + "createdAt": 1411076406000, + "text": "@Gavin I'm sure you meant that to be: document.getElementById('elementID').scrollIntoView()", + "upvotes": 121, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [] + }, + { + "_id": "62f327ab082fcc3049e92524", + "creator": "Avatar", + "createdAt": 1653578883000, + "text": "Would be nice to be able to define an offset ... stackoverflow.com/q/24665602/1066234", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bb9", + "creator": "Roman Shamritskiy", + "createdAt": 1395706213000, + "text": "

To show the full element (if it's possible with the current window size):

\n\n
var element       = $(\"#some_element\");\nvar elementHeight = element.height();\nvar windowHeight  = $(window).height();\n\nvar offset = Math.min(elementHeight, windowHeight) + element.offset().top;\n$('html, body').animate({ scrollTop: offset }, 500);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bba", + "creator": "davidcondrey", + "createdAt": 1405706765000, + "text": "

\r\n
\r\n
jQuery(document).ready(function($) {\r\n  $('a[href^=\"#\"]').bind('click.smoothscroll',function (e) {\r\n    e.preventDefault();\r\n    var target = this.hash,\r\n        $target = $(target);\r\n\r\n    $('html, body').stop().animate( {\r\n      'scrollTop': $target.offset().top-40\r\n    }, 900, 'swing', function () {\r\n      window.location.hash = target;\r\n    } );\r\n  } );\r\n} );
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n\r\n<ul role=\"tablist\">\r\n  <li class=\"active\" id=\"p1\"><a href=\"#pane1\" role=\"tab\">Section 1</a></li>\r\n  <li id=\"p2\"><a href=\"#pane2\" role=\"tab\">Section 2</a></li>\r\n  <li id=\"p3\"><a href=\"#pane3\" role=\"tab\">Section 3</a></li>\r\n</ul>\r\n\r\n<div id=\"pane1\"></div>\r\n<div id=\"pane2\"></div>\r\n<div id=\"pane3\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bbb", + "creator": "Rezgar Cadro", + "createdAt": 1409311171000, + "text": "

A compact version of \"animate\" solution.

\n\n
$.fn.scrollTo = function (speed) {\n    if (typeof(speed) === 'undefined')\n        speed = 1000;\n\n    $('html, body').animate({\n        scrollTop: parseInt($(this).offset().top)\n    }, speed);\n};\n
\n\n

Basic usage: $('#your_element').scrollTo();

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bbc", + "creator": "Benjamin Oakes", + "createdAt": 1422464419000, + "text": "

If you are only handling scrolling to an input element, you can use focus(). For example, if you wanted to scroll to the first visible input:

\n\n
$(':input:visible').first().focus();\n
\n\n

Or the first visible input in an container with class .error:

\n\n
$('.error :input:visible').first().focus();\n
\n\n

Thanks to Tricia Ball for pointing this out!

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bbd", + "creator": "vascogaspar", + "createdAt": 1434905197000, + "text": "

This is my approach abstracting the ID's and href's, using a generic class selector

\n\n

\r\n
\r\n
$(function() {\r\n  // Generic selector to be used anywhere\r\n  $(\".js-scroll-to\").click(function(e) {\r\n\r\n    // Get the href dynamically\r\n    var destination = $(this).attr('href');\r\n\r\n    // Prevent href=“#” link from changing the URL hash (optional)\r\n    e.preventDefault();\r\n\r\n    // Animate scroll to destination\r\n    $('html, body').animate({\r\n      scrollTop: $(destination).offset().top\r\n    }, 500);\r\n  });\r\n});
\r\n
<!-- example of a fixed nav menu -->\r\n<ul class=\"nav\">\r\n  <li>\r\n    <a href=\"#section-1\" class=\"nav-item js-scroll-to\">Item 1</a>\r\n  </li>\r\n  <li>\r\n    <a href=\"#section-2\" class=\"nav-item js-scroll-to\">Item 2</a>\r\n  </li>\r\n  <li>\r\n    <a href=\"#section-3\" class=\"nav-item js-scroll-to\">Item 3</a>\r\n  </li>\r\n</ul>
\r\n
\r\n
\r\n

\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bbf", + "creator": "hashchange", + "createdAt": 1439803095000, + "text": "

In most cases, it would be best to use a plugin. Seriously. I'm going to tout mine here. Of course there are others, too. But please check if they really avoid the pitfalls for which you'd want a plugin in the first place - not all of them do.

\n\n

I have written about the reasons for using a plugin elsewhere. In a nutshell, the one liner underpinning most answers here

\n\n
$('html, body').animate( { scrollTop: $target.offset().top }, duration );\n
\n\n

is bad UX.

\n\n\n\n

To handle these issues (and a bunch of others), you can use a plugin of mine, jQuery.scrollable. The call then becomes

\n\n
$( window ).scrollTo( targetPosition );\n
\n\n

and that's it. Of course, there are more options.

\n\n

With regard to the target position, $target.offset().top does the job in most cases. But please be aware that the returned value doesn't take a border on the html element into account (see this demo). If you need the target position to be accurate under any circumstances, it is better to use

\n\n
targetPosition = $( window ).scrollTop() + $target[0].getBoundingClientRect().top;\n
\n\n

That works even if a border on the html element is set.

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc0", + "creator": "isapir", + "createdAt": 1447875973000, + "text": "

I wrote a general purpose function that scrolls to either a jQuery object, a CSS selector, or a numeric value.

\n\n

Example usage:

\n\n
// scroll to \"#target-element\":\n$.scrollTo(\"#target-element\");\n\n// scroll to 80 pixels above first element with class \".invalid\":\n$.scrollTo(\".invalid\", -80);\n\n// scroll a container with id \"#my-container\" to 300 pixels from its top:\n$.scrollTo(300, 0, \"slow\", \"#my-container\");\n
\n\n

The function's code:

\n\n
/**\n* Scrolls the container to the target position minus the offset\n*\n* @param target    - the destination to scroll to, can be a jQuery object\n*                    jQuery selector, or numeric position\n* @param offset    - the offset in pixels from the target position, e.g.\n*                    pass -80 to scroll to 80 pixels above the target\n* @param speed     - the scroll speed in milliseconds, or one of the\n*                    strings \"fast\" or \"slow\". default: 500\n* @param container - a jQuery object or selector for the container to\n*                    be scrolled. default: \"html, body\"\n*/\njQuery.scrollTo = function (target, offset, speed, container) {\n\n    if (isNaN(target)) {\n\n        if (!(target instanceof jQuery))\n            target = $(target);\n\n        target = parseInt(target.offset().top);\n    }\n\n    container = container || \"html, body\";\n    if (!(container instanceof jQuery))\n        container = $(container);\n\n    speed = speed || 500;\n    offset = offset || 0;\n\n    container.animate({\n        scrollTop: target + offset\n    }, speed);\n};\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc2", + "creator": "DevWL", + "createdAt": 1452486731000, + "text": "

Very simple and easy to use custom jQuery plugin. Just add the attribute scroll= to your clickable element and set its value to the selector you want to scroll to.

\n\n

Like so: <a scroll=\"#product\">Click me</a>. It can be used on any element.

\n\n
(function($){\n    $.fn.animateScroll = function(){\n        console.log($('[scroll]'));\n        $('[scroll]').click(function(){\n            selector = $($(this).attr('scroll'));\n            console.log(selector);\n            console.log(selector.offset().top);\n            $('html body').animate(\n                {scrollTop: (selector.offset().top)}, //- $(window).scrollTop()\n                1000\n            );\n        });\n    }\n})(jQuery);\n\n// RUN\njQuery(document).ready(function($) {\n    $().animateScroll();\n});\n\n// IN HTML EXAMPLE\n// RUN ONCLICK ON OBJECT WITH ATTRIBUTE SCROLL=\".SELECTOR\"\n// <a scroll=\"#product\">Click To Scroll</a>\n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bbe", + "creator": "kayz1", + "createdAt": 1435904856000, + "text": "
var scrollTo = function($parent, $element) {\n    var topDiff = $element.position().top - $parent.position().top;\n\n    $parent.animate({\n        scrollTop : topDiff\n    }, 100);\n};\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc1", + "creator": "Khaled.K", + "createdAt": 1448714095000, + "text": "
\n

When the user clicks on that input with #subject, the page should\n scroll to the last element of the page with a nice animation. It\n should be a scroll to bottom and not to top.

\n \n

The last item of the page is a submit button with #submit

\n
\n\n
$('#subject').click(function()\n{\n    $('#submit').focus();\n    $('#subject').focus();\n});\n
\n\n

This will first scroll down to #submit then restore the cursor back to the input that was clicked, which mimics a scroll down, and works on most browsers. It also doesn't require jQuery as it can be written in pure JavaScript.

\n\n

Can this fashion of using focus function mimic animation in a better way, through chaining focus calls. I haven't tested this theory, but it would look something like this:

\n\n
<style>\n  #F > *\n  {\n    width: 100%;\n  }\n</style>\n\n<form id=\"F\" >\n  <div id=\"child_1\"> .. </div>\n  <div id=\"child_2\"> .. </div>\n  ..\n  <div id=\"child_K\"> .. </div>\n</form>\n\n<script>\n  $('#child_N').click(function()\n  {\n    $('#child_N').focus();\n    $('#child_N+1').focus();\n    ..\n    $('#child_K').focus();\n\n    $('#child_N').focus();\n  });\n</script>\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc4", + "creator": "Jonathan", + "createdAt": 1470179354000, + "text": "

With this solution you do not need any plugin and there's no setup required besides placing the script before your closing </body> tag.

\n
$("a[href^='#']").on("click", function(e) {\n  $("html, body").animate({\n    scrollTop: $($(this).attr("href")).offset().top\n  }, 1000);\n  return false;\n});\n\nif ($(window.location.hash).length > 1) {\n  $("html, body").animate({\n    scrollTop: $(window.location.hash).offset().top\n  }, 1000);\n}\n
\n

On load, if there is a hash in the address, we scroll to it.

\n

And - whenever you click an a link with an href hash e.g. #top, we scroll to it.

\n

##Edit 2020

\n

If you want a pure JavaScript solution: you could perhaps instead use something like:

\n
var _scrollToElement = function (selector) {\n  try {\n    document.querySelector(selector).scrollIntoView({ behavior: 'smooth' });\n  } catch (e) {\n    console.warn(e);\n  }\n}\n\nvar _scrollToHashesInHrefs = function () {\n  document.querySelectorAll("a[href^='#']").forEach(function (el) {\n    el.addEventListener('click', function (e) {\n      _scrollToElement(el.getAttribute('href'));\n      return false;\n    })\n  })\n  if (window.location.hash) {\n    _scrollToElement(window.location.hash);\n  }\n}\n\n_scrollToHashesInHrefs();\n
\n", + "upvotes": 28, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc5", + "creator": "svnm", + "createdAt": 1479904387000, + "text": "

I set up a module scroll-element npm install scroll-element. It works like this:

\n\n
import { scrollToElement, scrollWindowToElement } from 'scroll-element'\n\n/* scroll the window to your target element, duration and offset optional */\nlet targetElement = document.getElementById('my-item')\nscrollWindowToElement(targetElement)\n\n/* scroll the overflow container element to your target element, duration and offset optional */\nlet containerElement = document.getElementById('my-container')\nlet targetElement = document.getElementById('my-item')\nscrollToElement(containerElement, targetElement)\n
\n\n

Written with help from the following SO posts:

\n\n\n\n

Here is the code:

\n\n
export const scrollToElement = function(containerElement, targetElement, duration, offset) {\n  if (duration == null) { duration = 1000 }\n  if (offset == null) { offset = 0 }\n\n  let targetOffsetTop = getElementOffset(targetElement).top\n  let containerOffsetTop = getElementOffset(containerElement).top\n  let scrollTarget = targetOffsetTop + ( containerElement.scrollTop - containerOffsetTop)\n  scrollTarget += offset\n  scroll(containerElement, scrollTarget, duration)\n}\n\nexport const scrollWindowToElement = function(targetElement, duration, offset) {\n  if (duration == null) { duration = 1000 }\n  if (offset == null) { offset = 0 }\n\n  let scrollTarget = getElementOffset(targetElement).top\n  scrollTarget += offset\n  scrollWindow(scrollTarget, duration)\n}\n\nfunction scroll(containerElement, scrollTarget, duration) {\n  let scrollStep = scrollTarget / (duration / 15)\n  let interval = setInterval(() => {\n    if ( containerElement.scrollTop < scrollTarget ) {\n      containerElement.scrollTop += scrollStep\n    } else {\n      clearInterval(interval)\n    }\n  },15)\n}\n\nfunction scrollWindow(scrollTarget, duration) {\n  let scrollStep = scrollTarget / (duration / 15)\n  let interval = setInterval(() => {\n    if ( window.scrollY < scrollTarget ) {\n      window.scrollBy( 0, scrollStep )\n    } else {\n      clearInterval(interval)\n    }\n  },15)\n}\n\nfunction getElementOffset(element) {\n  let de = document.documentElement\n  let box = element.getBoundingClientRect()\n  let top = box.top + window.pageYOffset - de.clientTop\n  let left = box.left + window.pageXOffset - de.clientLeft\n  return { top: top, left: left }\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc6", + "creator": "Shahdat", + "createdAt": 1483542709000, + "text": "

$('html, body').animate(...) does not work for me in the iPhone, Android, Chrome, or Safari browsers.

\n

I had to target the root content element of the page.

\n
\n

$('#cotnent').animate(...)

\n
\n

Here is what I have ended up with:

\n
if (navigator.userAgent.match(/(iPod|iPhone|iPad|Android)/)) {\n    $('#content').animate({\n    scrollTop: $("#elementtoScrollToID").offset().top\n   }, 'slow');\n}\nelse{\n    $('html, body').animate({\n    scrollTop: $("#elementtoScrollToID").offset().top\n    }, 'slow');\n}\n
\n

All body content wired up with a #content div

\n
<html>\n    ....\n    <body>\n        <div id="content">\n        ...\n        </div>\n    </body>\n</html>\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc3", + "creator": "martinh_kentico", + "createdAt": 1456486812000, + "text": "

For what it's worth, this is how I managed to achieve such behavior for a general element which can be inside a DIV with scrolling. In our case we don't scroll the full body, but just particular elements with overflow: auto; within a larger layout.

\n\n

It creates a fake input of the height of the target element, and then puts a focus to it, and the browser will take care about the rest no matter how deep within the scrollable hierarchy you are. Works like a charm.

\n\n
var $scrollTo = $('#someId'),\ninputElem = $('<input type=\"text\"></input>');\n\n$scrollTo.prepend(inputElem);\ninputElem.css({\n  position: 'absolute',\n  width: '1px',\n  height: $scrollTo.height()\n});\ninputElem.focus();\ninputElem.remove();\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc8", + "creator": "a11r", + "createdAt": 1506125505000, + "text": "

If you want to scroll within an overflow container (instead of $('html, body') answered above), working also with absolute positioning, this is the way to do :

\n\n
var elem = $('#myElement'),\n    container = $('#myScrollableContainer'),\n    pos = elem.position().top + container.scrollTop() - container.position().top;\n\ncontainer.animate({\n  scrollTop: pos\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc7", + "creator": "user7601056", + "createdAt": 1488190152000, + "text": "

Animations:

\n\n
// slide to top of the page\n$('.up').click(function () {\n    $(\"html, body\").animate({\n        scrollTop: 0\n    }, 600);\n    return false;\n});\n\n// slide page to anchor\n$('.menutop b').click(function(){\n    //event.preventDefault();\n    $('html, body').animate({\n        scrollTop: $( $(this).attr('href') ).offset().top\n    }, 600);\n    return false;\n});\n\n// Scroll to class, div\n$(\"#button\").click(function() {\n    $('html, body').animate({\n        scrollTop: $(\"#target-element\").offset().top\n    }, 1000);\n});\n\n// div background animate\n$(window).scroll(function () {\n\n    var x = $(this).scrollTop();\n\n    // freezze div background\n    $('.banner0').css('background-position', '0px ' + x +'px');\n\n    // from left to right\n    $('.banner0').css('background-position', x+'px ' +'0px');\n\n    // from right to left\n    $('.banner0').css('background-position', -x+'px ' +'0px');\n\n    // from bottom to top\n    $('#skills').css('background-position', '0px ' + -x + 'px');\n\n    // move background from top to bottom\n    $('.skills1').css('background-position', '0% ' + parseInt(-x / 1) + 'px' + ', 0% ' + parseInt(-x / 1) + 'px, center top');\n\n    // Show hide mtop menu  \n    if ( x > 100 ) {\n    $( \".menu\" ).addClass( 'menushow' );\n    $( \".menu\" ).fadeIn(\"slow\");\n    $( \".menu\" ).animate({opacity: 0.75}, 500);\n    } else {\n    $( \".menu\" ).removeClass( 'menushow' );\n    $( \".menu\" ).animate({opacity: 1}, 500);\n    }\n\n});\n\n// progres bar animation simple\n$('.bar1').each(function(i) {\n  var width = $(this).data('width');  \n  $(this).animate({'width' : width + '%' }, 900, function(){\n    // Animation complete\n  });  \n});\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bc9", + "creator": "Irshad Khan", + "createdAt": 1507640586000, + "text": "

Easy way to achieve the scroll of page to target div id

\n\n
var targetOffset = $('#divID').offset().top;\n$('html, body').animate({scrollTop: targetOffset}, 1000);\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bca", + "creator": "cynya", + "createdAt": 1537590331000, + "text": "

This is Atharva's answer from: https://developer.mozilla.org/en-US/docs/Web/API/element.scrollIntoView.\nJust wanted to add if your document is in an iframe, you can choose an element in the parent frame to scroll into view:

\n\n
 $('#element-in-parent-frame', window.parent.document).get(0).scrollIntoView();\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bcb", + "creator": "Polaris", + "createdAt": 1541580672000, + "text": "

This worked for me:

\n\n
var targetOffset = $('#elementToScrollTo').offset().top;\n$('#DivParent').animate({scrollTop: targetOffset}, 2500);\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bcc", + "creator": "object-Object", + "createdAt": 1545326532000, + "text": "

This is achievable without jQuery:

\n
document.getElementById("element-id").scrollIntoView();\n
\n", + "upvotes": 161, + "upvoterUsernames": [], + "downvotes": 71, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bce", + "creator": "Minguocode", + "createdAt": 1553009141000, + "text": "

\r\n
\r\n
jQuery(document).ready(function($) {\r\n  $('a[href^=\"#\"]').bind('click.smoothscroll',function (e) {\r\n    e.preventDefault();\r\n    var target = this.hash,\r\n        $target = $(target);\r\n\r\n    $('html, body').stop().animate( {\r\n      'scrollTop': $target.offset().top-40\r\n    }, 900, 'swing', function () {\r\n      window.location.hash = target;\r\n    } );\r\n  } );\r\n} );
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n\r\n\r\n<ul role=\"tablist\">\r\n  <li class=\"active\" id=\"p1\"><a href=\"#pane1\" role=\"tab\">Section 1</a></li>\r\n  <li id=\"p2\"><a href=\"#pane2\" role=\"tab\">Section 2</a></li>\r\n  <li id=\"p3\"><a href=\"#pane3\" role=\"tab\">Section 3</a></li>\r\n</ul>\r\n\r\n<div id=\"pane1\"></div>\r\n<div id=\"pane2\"></div>\r\n<div id=\"pane3\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bcd", + "creator": "stardust4891", + "createdAt": 1547755135000, + "text": "

Updated answer as of 2019:

\n\n
$('body').animate({\n    scrollTop: $('#subject').offset().top - $('body').offset().top + $('body').scrollTop()\n}, 'fast');\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bd0", + "creator": "Kamil Kiełczewski", + "createdAt": 1578482189000, + "text": "

ONELINER

\n\n
subject.onclick = e=> window.scroll({ top: submit.offsetTop, behavior: 'smooth'});\n
\n\n

\r\n
\r\n
subject.onclick = e=> window.scroll({top: submit.offsetTop, behavior: 'smooth'});
\r\n
.box,.foot{display: flex;background:#fdf;padding:500px 0} .foot{padding:250px}
\r\n
<input type=\"text\" class=\"textfield\" value=\"click here\" id=\"subject\" name=\"subject\">\r\n\r\n<div class=\"box\">\r\n  Some content\r\n  <textarea></textarea>\r\n</div>\r\n\r\n<input type=\"submit\" class=\"submit\" id=\"submit\" name=\"submit\" value=\"Ok, Done.\">\r\n\r\n<div class=\"foot\">Some footer</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bcf", + "creator": "Edvard Åkerberg", + "createdAt": 1561039196000, + "text": "

This is the way I do it.

\n\n
document.querySelector('scrollHere').scrollIntoView({ behavior: 'smooth' })\n
\n\n

Works in any browser.

\n\n

It can easily be wrapped into a function

\n\n
function scrollTo(selector) {\n    document.querySelector(selector).scrollIntoView({ behavior: 'smooth' })\n}\n
\n\n

Here is a working example\n

\r\n
\r\n
$(\".btn\").click(function() {\r\n  document.getElementById(\"scrollHere\").scrollIntoView( {behavior: \"smooth\" })\r\n})
\r\n
.btn {margin-bottom: 500px;}\r\n.middle {display: block; margin-bottom: 500px; color: red;}
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\r\n\r\n<button class=\"btn\">Scroll down</button>\r\n\r\n<h1 class=\"middle\">You see?</h1>\r\n\r\n<div id=\"scrollHere\">Arrived at your destination</div>
\r\n
\r\n
\r\n

\n\n

Docs

\n", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bd1", + "creator": "Jaber Alshami", + "createdAt": 1597313993000, + "text": "

You just need:

\n
$("selector").get(0).scrollTo(0, 0)\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ae082fcc3049e92539", + "creator": "Nico Haase", + "createdAt": 1597322756000, + "text": "Please add some explanation to your answer such that others can learn from it.", + "upvotes": 694, + "upvoterUsernames": [], + "downvotes": 694, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bd2", + "creator": "Vu Viet Hung", + "createdAt": 1598424131000, + "text": "

After finding the way to get my code work, I think I should make thing a bit clear:\nFor using:

\n
$('html, body').animate({\n   scrollTop: $("#div1").offset().top\n}, 2000);\n
\n

you need to be on top of the page since $("#div1").offset().top will return different numbers for different positions you scroll to. If you already scrolled out of the top, you need to specify the exact pageY value (see pageY definition here: https://javascript.info/coordinates).

\n

So now, the problem is to calculate the pageY value of one element. Below is an example in case the scroll container is the body:

\n
function getPageY(id) {\n    let elem = document.getElementById(id);\n    let box = elem.getBoundingClientRect();\n    var body = document.getElementsByTagName("BODY")[0];\n    return box.top + body.scrollTop; // for window scroll: box.top + window.scrollY;\n}\n
\n

The above function returns the same number even if you scrolled somewhere. Now, to scroll back to that element:

\n
$("html, body").animate({ scrollTop: getPageY('div1') }, "slow");\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90baf", + "creator": "Jacksonkr", + "createdAt": 1564163581000, + "text": "scrollTo was disabled for me because of a chrome plugin, not sure which.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 2985669, + "uvac": 2985701 + } + }, + { + "_id": "62f321bb082fcc3049e8ff06", + "title": "Iterate through object properties", + "title-lowercase": "iterate through object properties", + "creator": "Rafay", + "createdAt": 1322577026000, + "status": "open", + "text": "

\r\n
\r\n
var obj = {\r\n    name: \"Simon\",\r\n    age: \"20\",\r\n    clothing: {\r\n        style: \"simple\",\r\n        hipster: false\r\n    }\r\n}\r\n\r\nfor(var propt in obj){\r\n    console.log(propt + ': ' + obj[propt]);\r\n}
\r\n
\r\n
\r\n

\n\n

How does the variable propt represent the properties of the object? It's not a built-in method or property. Why does it come up with every property in the object?

\n", + "upvotes": 3343, + "upvoterUsernames": [], + "downvotes": 1059, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1522997, + "answers": 31, + "answerItems": [ + { + "_id": "62f3220c082fcc3049e90fba", + "creator": "Matt Ball", + "createdAt": 1322577191000, + "text": "

It's just a for...in loop. Check out the documentation at Mozilla.

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 29, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c43", + "creator": "Burak", + "createdAt": 1613593599000, + "text": "The link is broken :(.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c45", + "creator": "Matt Ball", + "createdAt": 1613685784000, + "text": "@Burak thanks for letting me know - fixed. In the future, feel free to suggest an edit :)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fbb", + "creator": "arb", + "createdAt": 1322577226000, + "text": "

Your for loop is iterating over all of the properties of the object obj. propt is defined in the first line of your for loop. It is a string that is a name of a property of the obj object. In the first iteration of the loop, propt would be \"name\".

\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fbc", + "creator": "user920041", + "createdAt": 1322577278000, + "text": "

Objects in JavaScript are collections of properties and can therefore be looped in a for each statement.

\n\n

You should think of obj as an key value collection.

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c48", + "creator": "Qqwy", + "createdAt": 1322577500000, + "text": "! with the important difference that these 'lists of properties' can have names as keys, while normal JS arrays can only have numbers as keys.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fb9", + "creator": "Marc B", + "createdAt": 1322577155000, + "text": "

It's the for...in statement (MDN, ECMAScript spec).

\n\n

You can read it as \"FOR every property IN the obj object, assign each property to the PROPT variable in turn\".

\n", + "upvotes": 431, + "upvoterUsernames": [], + "downvotes": 205, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c4b", + "creator": "Rafay", + "createdAt": 1322577790000, + "text": "Thanks a lot, I understand it now. I was banging my head, going through books and Google.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c4d", + "creator": "Doug Molineux", + "createdAt": 1377208008000, + "text": "Odd this answer has so many up votes, especially since these popular comments seem to contradict it.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c4f", + "creator": "computrius", + "createdAt": 1386870495000, + "text": "Why is this marked as the answer? It is quite possibly the least helpful one in this thread..", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c51", + "creator": "Dan Dascalescu", + "createdAt": 1397722158000, + "text": "@PeteHerbertPenito, odd that nobody bothered to edit the answer until I did.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c52", + "creator": "David Callanan", + "createdAt": 1535825313000, + "text": "This is the first answer I've found that actually answers the question.", + "upvotes": 5826, + "upvoterUsernames": [], + "downvotes": 5826, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c54", + "creator": "Tom Russell", + "createdAt": 1543435550000, + "text": ""For each property in the object". One of my pet peeves is messing up plurality. It's so confusing to beginners.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c56", + "creator": "Ian Steffy", + "createdAt": 1548250271000, + "text": "This answer does not iterate through the list of objects, rather they key names of each object and the return value is a string.", + "upvotes": 2367, + "upvoterUsernames": [], + "downvotes": 2367, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fbe", + "creator": "Danny R", + "createdAt": 1376378348000, + "text": "

As of JavaScript 1.8.5 you can use Object.keys(obj) to get an Array of properties defined on the object itself (the ones that return true for obj.hasOwnProperty(key)).

\n\n
Object.keys(obj).forEach(function(key,index) {\n    // key: the name of the object key\n    // index: the ordinal position of the key within the object \n});\n
\n\n

This is better (and more readable) than using a for-in loop.

\n\n

Its supported on these browsers:

\n\n\n\n

See the Mozilla Developer Network Object.keys()'s reference for futher information.

\n", + "upvotes": 1316, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c59", + "creator": "Victor Grazi", + "createdAt": 1417033601000, + "text": "Tried this in MSIE 9.0 but it doesn't recognize Object.keys()", + "upvotes": 960, + "upvoterUsernames": [], + "downvotes": 960, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c5b", + "creator": "AJ_83", + "createdAt": 1485332386000, + "text": "Good solution. But how to break the iteration?!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c5d", + "creator": "Daniel Z.", + "createdAt": 1490266062000, + "text": "@AJ_83 There's no good way to break out of a forEach(). Use some() in this case, and return true to break", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c5f", + "creator": "Umut Çağdaş Coşkun", + "createdAt": 1493043277000, + "text": "Thank you. I guess it's more clear than accepted answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c61", + "creator": "Jona", + "createdAt": 1532951309000, + "text": "why is this more readable than for-in? for candidate in candidateStatus... seems readable to me", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c63", + "creator": "corsiKa", + "createdAt": 1610428435000, + "text": "@Duncan Because of the atrocious "hasOwnProperty" malarkey.", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fbd", + "creator": "user2417527", + "createdAt": 1369399090000, + "text": "

Iterating over properties requires this additional hasOwnProperty check:

\n\n
for (var prop in obj) {\n    if (Object.prototype.hasOwnProperty.call(obj, prop)) {\n        // do stuff\n    }\n}\n
\n\n

It's necessary because an object's prototype contains additional properties for the object which are technically part of the object. These additional properties are inherited from the base object class, but are still properties of obj.

\n\n

hasOwnProperty simply checks to see if this is a property specific to this class, and not one inherited from the base class.

\n\n
\n\n

It's also possible to call hasOwnProperty through the object itself:

\n\n
if (obj.hasOwnProperty(prop)) {\n    // do stuff\n}\n
\n\n

But this will fail if the object has an unrelated field with the same name:

\n\n
var obj = { foo: 42, hasOwnProperty: 'lol' };\nobj.hasOwnProperty('foo');  // TypeError: hasOwnProperty is not a function\n
\n\n

That's why it's safer to call it through Object.prototype instead:

\n\n
var obj = { foo: 42, hasOwnProperty: 'lol' };\nObject.prototype.hasOwnProperty.call(obj, 'foo');  // true\n
\n", + "upvotes": 4504, + "upvoterUsernames": [], + "downvotes": 1952, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c66", + "creator": "Adria", + "createdAt": 1413000357000, + "text": "If you like performance, you won't use hasOwnProperty. You'll add non-enumerable properties the proper way using Object.defineProperty", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c68", + "creator": "user1228", + "createdAt": 1453990249000, + "text": "if('javascript'.hasOwnProperty(0)) console.log('Well, you've really just screwed me over, didn't you, javascript?');", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c69", + "creator": "user1228", + "createdAt": 1454011751000, + "text": "@JadieldeArmas strings are just a corner case you have to watch out for. Like everything else in javascript :/", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c6a", + "creator": "Jadiel de Armas", + "createdAt": 1454012585000, + "text": "I think that the confusion is mainly introduced here because a Javascript object and a JSON object are not the same thing.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c6c", + "creator": "user663031", + "createdAt": 1473757638000, + "text": "This answer needs to be updated for "ES7", with methods such as Object.entries and Object.values.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c6d", + "creator": "developerbmw", + "createdAt": 1506736266000, + "text": "this doesn't answer the question at all. why 1700 people upvote it?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3296f082fcc3049e92c6f", + "creator": "Meghan", + "createdAt": 1520777985000, + "text": "These are the same values returned from Object.keys(obj)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fc0", + "creator": "Vappor Washmade", + "createdAt": 1453607830000, + "text": "

The for...in loop represents each property in an object because it is just like a for loop. You defined propt in the for...in loop by doing:

\n\n
    for(var propt in obj){\nalert(propt + ': ' + obj[propt]);\n}\n
\n\n

A for...in loop iterates through the enumerable properties of an object. Whichever variable you define, or put in the for...in loop, changes each time it goes to the next property it iterates. The variable in the for...in loop iterates through the keys, but the value of it is the key's value. For example:

\n\n
    for(var propt in obj) {\n      console.log(propt);//logs name\n      console.log(obj[propt]);//logs \"Simon\"\n    }\n
\n\n

You can see how the variable differs from the variable's value. In contrast, a for...of loop does the opposite.

\n\n

I hope this helps.

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fbf", + "creator": "Raman Sohi", + "createdAt": 1427618887000, + "text": "

What for..in loop does is that it creates a new variable (var someVariable) and then stores each property of the given object in this new variable(someVariable) one by one. Therefore if you use block {}, you can iterate. Consider the following example.

\n\n
var obj = {\n     name:'raman',\n     hobby:'coding',\n     planet:'earth'\n     };\n\nfor(var someVariable in obj) {\n  //do nothing..\n}\n\nconsole.log(someVariable); // outputs planet\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fc1", + "creator": "Jadiel de Armas", + "createdAt": 1454013670000, + "text": "

I want to add to the answers above, because you might have different intentions from Javascript. A JSON object and a Javascript object are different things, and you might want to iterate through the properties of a JSON object using the solutions proposed above, and then be surprised.

\n\n

Suppose that you have a JSON object like:

\n\n
var example = {\n    \"prop1\": \"value1\",\n    \"prop2\": [ \"value2_0\", value2_1\"],\n    \"prop3\": {\n         \"prop3_1\": \"value3_1\"\n    }\n}\n
\n\n

The wrong way to iterate through its 'properties':

\n\n
function recursivelyIterateProperties(jsonObject) {\n    for (var prop in Object.keys(example)) {\n        console.log(prop);\n        recursivelyIterateProperties(jsonObject[prop]);\n    }\n}\n
\n\n

You might be surprised of seeing the console logging 0, 1, etc. when iterating through the properties of prop1 and prop2 and of prop3_1. Those objects are sequences, and the indexes of a sequence are properties of that object in Javascript.

\n\n

A better way to recursively iterate through a JSON object properties would be to first check if that object is a sequence or not:

\n\n
function recursivelyIterateProperties(jsonObject) {\n    for (var prop in Object.keys(example)) {\n        console.log(prop);\n        if (!(typeof(jsonObject[prop]) === 'string')\n            && !(jsonObject[prop] instanceof Array)) {\n                recursivelyIterateProperties(jsonObject[prop]);\n\n            }\n\n     }\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fc2", + "creator": "Ondrej Svejdar", + "createdAt": 1455203399000, + "text": "

Also adding the recursive way:

\n\n
function iterate(obj) {\n    // watch for objects we've already iterated so we won't end in endless cycle\n    // for cases like var foo = {}; foo.bar = foo; iterate(foo);\n    var walked = [];\n    var stack = [{obj: obj, stack: ''}];\n    while(stack.length > 0)\n    {\n        var item = stack.pop();\n        var obj = item.obj;\n        for (var property in obj) {\n            if (obj.hasOwnProperty(property)) {\n                if (typeof obj[property] == \"object\") {\n                  // check if we haven't iterated through the reference yet\n                  var alreadyFound = false;\n                  for(var i = 0; i < walked.length; i++)\n                  {\n                    if (walked[i] === obj[property])\n                    {\n                      alreadyFound = true;\n                      break;\n                    }\n                  }\n                  // new object reference\n                  if (!alreadyFound)\n                  {\n                    walked.push(obj[property]);\n                    stack.push({obj: obj[property], stack: item.stack + '.' + property});\n                  }\n                }\n                else\n                {\n                    console.log(item.stack + '.' + property + \"=\" + obj[property]);\n                }\n            }\n        }\n    }\n}\n
\n\n

Usage:

\n\n
iterate({ foo: \"foo\", bar: { foo: \"foo\"} }); \n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3296f082fcc3049e92c74", + "creator": "Ondrej Svejdar", + "createdAt": 1456130178000, + "text": "@faiz - see my comments, it is safeguard against being stuck in endless loop when you recurrently walk trough object that has cyclic references", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fc4", + "creator": "Rob Sedgwick", + "createdAt": 1459264765000, + "text": "

jquery allows you to do this now:

\n\n
$.each( obj, function( key, value ) {\n  alert( key + \": \" + value );\n});\n
\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32970082fcc3049e92c76", + "creator": "Bob Stein", + "createdAt": 1522852989000, + "text": "Details why I think this is a bug-inviting approach.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fc3", + "creator": "Faiz Mohamed Haneef", + "createdAt": 1456072817000, + "text": "

Here I am iterating each node and creating meaningful node names. If you notice, instanceOf Array and instanceOf Object pretty much does the same thing (in my application, i am giving different logic though)

\n\n
function iterate(obj,parent_node) {\n    parent_node = parent_node || '';\n    for (var property in obj) {\n        if (obj.hasOwnProperty(property)) {\n            var node = parent_node + \"/\" + property;\n            if(obj[property] instanceof Array) {\n                //console.log('array: ' + node + \":\" + obj[property]);\n                iterate(obj[property],node)\n            } else if(obj[property] instanceof Object){\n                //console.log('Object: ' + node + \":\" + obj[property]);\n                iterate(obj[property],node)\n            }\n            else {\n                console.log(node + \":\" + obj[property]);\n            }\n        }\n    }\n}\n
\n\n

note - I am inspired by Ondrej Svejdar's answer. But this solution has better performance and less ambiguous

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fc6", + "creator": "Redu", + "createdAt": 1470739418000, + "text": "

Nowadays you can convert a standard JS object into an iterable object just by adding a Symbol.iterator method. Then you can use a for of loop and acceess its values directly or even can use a spread operator on the object too. Cool. Let's see how we can make it:

\n\n

\r\n
\r\n
var o = {a:1,b:2,c:3},\r\n    a = [];\r\no[Symbol.iterator] = function*(){\r\n                       var ok = Object.keys(this);\r\n                            i = 0;\r\n                       while (i < ok.length) yield this[ok[i++]];\r\n                     };\r\nfor (var value of o) console.log(value);\r\n// or you can even do like\r\na = [...o];\r\nconsole.log(a);
\r\n
\r\n
\r\n

\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32970082fcc3049e92c78", + "creator": "Benj", + "createdAt": 1485512546000, + "text": "Interesting way to do that. Thanks for the function* discovery!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32970082fcc3049e92c79", + "creator": "Dmitry", + "createdAt": 1602837807000, + "text": "Nice one, you can even use methods, if properties are objects too!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fc5", + "creator": "HovyTech", + "createdAt": 1466711604000, + "text": "

You basically want to loop through each property in the object.

\n\n

JSFiddle

\n\n
var Dictionary = {\n  If: {\n    you: {\n      can: '',\n      make: ''\n    },\n    sense: ''\n  },\n  of: {\n    the: {\n      sentence: {\n        it: '',\n        worked: ''\n      }\n    }\n  }\n};\n\nfunction Iterate(obj) {\n  for (prop in obj) {\n    if (obj.hasOwnProperty(prop) && isNaN(prop)) {\n      console.log(prop + ': ' + obj[prop]);\n      Iterate(obj[prop]);\n    }\n  }\n}\nIterate(Dictionary);\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fc7", + "creator": "user663031", + "createdAt": 1473748899000, + "text": "

In up-to-date implementations of ES, you can use Object.entries:

\n\n
for (const [key, value] of Object.entries(obj)) { }\n
\n\n

or

\n\n
Object.entries(obj).forEach(([key, value]) => ...)\n
\n\n

If you just want to iterate over the values, then use Object.values:

\n\n
for (const value of Object.values(obj)) { }\n
\n\n

or

\n\n
Object.values(obj).forEach(value => ...)\n
\n", + "upvotes": 322, + "upvoterUsernames": [], + "downvotes": 98, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32970082fcc3049e92c7b", + "creator": "gnzg", + "createdAt": 1524585737000, + "text": "The third suggestion is great if you only the properties' values. Awesome!", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32970082fcc3049e92c7d", + "creator": "OzzyCzech", + "createdAt": 1627458102000, + "text": "this is best answer, you should use for (const [key, value] of Object.entries(obj)) { }", + "upvotes": 68, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fc8", + "creator": "viktarpunko", + "createdAt": 1478278273000, + "text": "

You can use Lodash. The documentation

\n\n
var obj = {a: 1, b: 2, c: 3};\n_.keys(obj).forEach(function (key) {\n    ...\n});\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fca", + "creator": "Philll_t", + "createdAt": 1494009044000, + "text": "
let obj = {"a": 3, "b": 2, "6": "a"}\n\nObject.keys(obj).forEach((item) => {console.log("item", obj[item])})\n\n// a\n// 3\n// 2\n
\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fc9", + "creator": "Frank Roth", + "createdAt": 1479804427000, + "text": "

Girls and guys we are in 2019 and we do not have that much time for typing... So lets do this cool new fancy ECMAScript 2016:

\n\n
Object.keys(obj).forEach(e => console.log(`key=${e}  value=${obj[e]}`));\n
\n", + "upvotes": 714, + "upvoterUsernames": [], + "downvotes": 350, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32970082fcc3049e92c81", + "creator": "krillgar", + "createdAt": 1484149038000, + "text": "How is this any different than Danny R's answer?", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32970082fcc3049e92c83", + "creator": "Frank Roth", + "createdAt": 1484237323000, + "text": "It is a oneliner and uses map instead of forEach. And also the console.log satement is maybe interesting for some people.", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fcb", + "creator": "Justin", + "createdAt": 1502426227000, + "text": "

If running Node I'd recommend:

\n\n
Object.keys(obj).forEach((key, index) => {\n    console.log(key);\n});\n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fcc", + "creator": "Konrad Kiss", + "createdAt": 1503992177000, + "text": "

To further refine the accepted answer it's worth noting that if you instantiate the object with a var object = Object.create(null) then object.hasOwnProperty(property) will trigger a TypeError. So to be on the safe side, you'd need to call it from the prototype like this:

\n\n
for (var property in object) {\n    if (Object.prototype.hasOwnProperty.call(object, property)) {\n        // do stuff\n    }\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fcd", + "creator": "JSON C11", + "createdAt": 1507411629000, + "text": "

If your environment supports ES2017 then I would recommend Object.entries:

\n\n
Object.entries(obj).forEach(([key, value]) => {\n  console.log(`${key} ${value}`);\n});\n
\n\n

As shown in Mozillas Object.entries() documentation:

\n\n
\n

The Object.entries() method returns an array of a given object's own\n enumerable property [key, value] pairs, in the same order as that\n provided by a for...in loop (the difference being that a for-in loop\n enumerates properties in the prototype chain as well).

\n
\n\n

Basically with Object.entries we can forgo the following extra step that is required with the older for...in loop:

\n\n
// This step is not necessary with Object.entries\nif (object.hasOwnProperty(property)) {\n  // do stuff\n}\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fd0", + "creator": "Fellow Stranger", + "createdAt": 1524654179000, + "text": "
Object.keys(obj).forEach(key =>\n  console.log(`key=${key} value=${obj[key]}`)\n);\n
\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fd2", + "creator": "iRohitBhatia", + "createdAt": 1560164118000, + "text": "

While the top-rated answer is correct, here is an alternate use case i.e if you are iterating over an object and want to create an array in the end. Use .map instead of forEach

\n\n
const newObj = Object.keys(obj).map(el => {\n    //ell will hold keys \n   // Getting the value of the keys should be as simple as obj[el]\n})\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fcf", + "creator": "Cyril N.", + "createdAt": 1516359812000, + "text": "

Dominik's answer is perfect, I just prefer to do it that way, as it's cleaner to read:

\n
for (var property in obj) {\n    if (!obj.hasOwnProperty(property)) continue;\n\n    // Do stuff...\n}\n
\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32971082fcc3049e92c87", + "creator": "Jonathan", + "createdAt": 1578408527000, + "text": "Should be Object with uppercase o though, no?", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [] + }, + { + "_id": "62f32971082fcc3049e92c88", + "creator": "shrekuu", + "createdAt": 1611805073000, + "text": "@Jonathan Note it is the object variable in the first line.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32971082fcc3049e92c8a", + "creator": "Cyril N.", + "createdAt": 1611822525000, + "text": "I have updated the code to avoid confusion ;)", + "upvotes": 2455, + "upvoterUsernames": [], + "downvotes": 2455, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fd1", + "creator": "Dimitar Nikovski", + "createdAt": 1530190168000, + "text": "

To add ES2015's usage of Reflect.ownKeys(obj) and also iterating over the properties via an iterator.

\n\n

For example:

\n\n
let obj = { a: 'Carrot', b: 'Potato', Car: { doors: 4 } };\n
\n\n

can be iterated over by

\n\n
// logs each key\nReflect.ownKeys(obj).forEach(key => console.log(key));\n
\n\n

If you would like to iterate directly over the values of the keys of an object, you can define an iterator, just like JavaScipts's default iterators for strings, arrays, typed arrays, Map and Set.

\n\n

JS will attempt to iterate via the default iterator property, which must be defined as Symbol.iterator.

\n\n

If you want to be able to iterate over all objects you can add it as a prototype of Object:

\n\n
Object.prototype[Symbol.iterator] = function*() { \n    for(p of Reflect.ownKeys(this)){ yield this[p]; }\n}\n
\n\n

This would enable you to iterate over the values of an object with a for...of loop, for example:

\n\n
for(val of obj) { console.log('Value is:' + val ) }\n
\n\n

Caution: As of writing this answer (June 2018) all other browsers, but IE, support generators and for...of iteration via Symbol.iterator

\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32971082fcc3049e92c8c", + "creator": "Michiel", + "createdAt": 1559122895000, + "text": "Although you are not actually answering the OP's question, this was very helpful for me, I did not know about Reflect yet.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fce", + "creator": "dylanh724", + "createdAt": 1509169165000, + "text": "

The above answers are a bit annoying because they don't explain what you do inside the for loop after you ensure it's an object: YOU DON'T ACCESS IT DIRECTLY! You are actually only delivered the KEY that you need to apply to the OBJ:

\n\n
var obj = {\n  a: \"foo\",\n  b: \"bar\",\n  c: \"foobar\"\n};\n\n// We need to iterate the string keys (not the objects)\nfor(var someKey in obj)\n{\n  // We check if this key exists in the obj\n  if (obj.hasOwnProperty(someKey))\n  {\n    // someKey is only the KEY (string)! Use it to get the obj:\n    var myActualPropFromObj = obj[someKey]; // Since dynamic, use [] since the key isn't literally named \"someKey\"\n\n    // NOW you can treat it like an obj\n    var shouldBeBar = myActualPropFromObj.b;\n  }\n}\n
\n\n

This is all ECMA5 safe. Even works in the lame JS versions like Rhino ;)

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32971082fcc3049e92c8f", + "creator": "Antony Hatchkins", + "createdAt": 1594837077000, + "text": "shouldBeBar is undefined for all three iterations.", + "upvotes": 697, + "upvoterUsernames": [], + "downvotes": 697, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fd6", + "creator": "danday74", + "createdAt": 1627600574000, + "text": "

If you just want to iterate to map property values then lodash has _.mapValues

\n

\r\n
\r\n
const obj = {\n  a: 2,\n  b: 3\n}\nconst res = _.mapValues(obj, v => v * 2)\nconsole.log(res)
\r\n
<script src=\"https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 414, + "upvoterUsernames": [], + "downvotes": 414, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fd4", + "creator": "Kamil Kiełczewski", + "createdAt": 1593195859000, + "text": "

Check type

\n

You can check how propt represent object propertis by

\n
typeof propt\n
\n

to discover that it's just a string (name of property). It come up with every property in the object due the way of how for-in js "build-in" loop works.

\n

\r\n
\r\n
var obj = {\n    name: \"Simon\",\n    age: \"20\",\n    clothing: {\n        style: \"simple\",\n        hipster: false\n    }\n}\n\nfor(var propt in obj){\n    console.log(typeof propt,  propt + ': ' + obj[propt]);\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3671, + "upvoterUsernames": [], + "downvotes": 3671, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fd5", + "creator": "Sunny", + "createdAt": 1601962046000, + "text": "

You can access the nested properties of the object using the for...in and forEach loop.

\n

for...in:

\n
for (const key in info) {\n    console.log(info[key]);\n}\n
\n

forEach:

\n
Object.keys(info).forEach(function(prop) {\n    console.log(info[prop]);\n    // cities: Array[3], continent: "North America", images: Array[3], name: "Canada"\n    // "prop" is the property name\n    // "data[prop]" is the property value\n});\n
\n", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f3220c082fcc3049e90fd3", + "creator": "Fouad Boukredine", + "createdAt": 1573577038000, + "text": "
if (typeof obj === 'object' && obj !== null) {\n    Object.keys(obj).forEach(key => {\n        console.log("\\n" + key + ": " + obj[key]);\n    });\n}\n\n// *** Explanation line by line ***\n\n// Explaining the bellow line\n// It checks if obj is neither null nor undefined, which means it's safe to get its keys. \n// Otherwise it will give you a "TypeError: Cannot convert undefined or null to object" if obj is null or undefined.\n// NOTE 1: You can use Object.hasOwnProperty() instead of Object.keys(obj).length\n// NOTE 2: No need to check if obj is an array because it will work just fine.\n// NOTE 3: No need to check if obj is a string because it will not pass the if typeof obj is Object statement.\n// NOTE 4: No need to check if Obj is undefined because it will not pass the if type obj is Object statement either.\nif (typeof obj === 'object' && obj !== null) {\n\n    // Explaining the bellow line\n    // Just like in the previous line, this returns an array with\n    // all keys in obj (because if code execution got here, it means \n    // obj has keys.) \n    // Then just invoke built-in javascript forEach() to loop\n    // over each key in returned array and calls a call back function \n    // on each array element (key), using ES6 arrow function (=>)\n    // Or you can just use a normal function ((key) { blah blah }).\n    Object.keys(obj).forEach(key => {\n\n        // The bellow line prints out all keys with their \n        // respective value in obj.\n        // key comes from the returned array in Object.keys(obj)\n        // obj[key] returns the value of key in obj\n        console.log("\\n" + key + ": " + obj[key]);\n    });\n}\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32971082fcc3049e92c93", + "creator": "Nicolas", + "createdAt": 1573589978000, + "text": "Hi, could you add more information about your answer, providing only code does not help.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32971082fcc3049e92c95", + "creator": "Fouad Boukredine", + "createdAt": 1573600734000, + "text": "Hi @Nicolas I've added a line by line explanation to the code. Let me know if it's still not clear", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f3220c082fcc3049e90fd7", + "creator": "user17344058", + "createdAt": 1636213682000, + "text": "

Simple and clear way to reach this, im modern JS without iterate the prototypes, is like this:

\n
Object.prototype.iterateProperties = ((callback) => {\n   Object.keys(obj).filter(key => obj.hasOwnProperty(key)).forEach((key) => {\n      callback(key, obj[key]);\n   });\n});\n
\n

Explain

\n

This code creates a function in the prototype of all object - means function that is accessible in every Object instance.\nThe function iterate all own properties of the object and run a callback function which gets (key, value) for each propery in the object.

\n

Example for use

\n
obj.iterateProperties((key, value) => console.log(key + ': ' + value));\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f32209082fcc3049e90f53", + "creator": "noob", + "createdAt": 1322577582000, + "text": "if (typeof(obj[propt]) === 'object') {/* Do it again */ }", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1526341, + "uvac": 1526372 + } + }, + { + "_id": "62f321bb082fcc3049e8feb8", + "title": "For-each over an array in JavaScript", + "title-lowercase": "for-each over an array in javascript", + "creator": "Dante1986", + "createdAt": 1329486708000, + "status": "open", + "text": "

How can I loop through all the entries in an array using JavaScript?

\n", + "upvotes": 8343, + "upvoterUsernames": [], + "downvotes": 2927, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 4988088, + "answers": 32, + "answerItems": [ + { + "_id": "62f321be082fcc3049e901d9", + "creator": "PatrikAkerstrand", + "createdAt": 1329486911000, + "text": "

Note: This answer is hopelessly out-of-date. For a more modern approach, look at the methods available on an array. Methods of interest might be:

\n\n\n\n
\n\n

The standard way to iterate an array in JavaScript is a vanilla for-loop:

\n\n
var length = arr.length,\n    element = null;\nfor (var i = 0; i < length; i++) {\n  element = arr[i];\n  // Do something with element\n}\n
\n\n

Note, however, that this approach is only good if you have a dense array, and each index is occupied by an element. If the array is sparse, then you can run into performance problems with this approach, since you will iterate over a lot of indices that do not really exist in the array. In this case, a for .. in-loop might be a better idea. However, you must use the appropriate safeguards to ensure that only the desired properties of the array (that is, the array elements) are acted upon, since the for..in-loop will also be enumerated in legacy browsers, or if the additional properties are defined as enumerable.

\n\n

In ECMAScript 5 there will be a forEach method on the array prototype, but it is not supported in legacy browsers. So to be able to use it consistently you must either have an environment that supports it (for example, Node.js for server side JavaScript), or use a \"Polyfill\". The Polyfill for this functionality is, however, trivial and since it makes the code easier to read, it is a good polyfill to include.

\n", + "upvotes": 877, + "upvoterUsernames": [], + "downvotes": 319, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3233b082fcc3049e913fa", + "creator": "PatrikAkerstrand", + "createdAt": 1600064681000, + "text": "@stevec: Array.from(document.querySelectorAll('video')).forEach(video => video.playbackRate = 2.2);", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321be082fcc3049e901da", + "creator": "Quentin", + "createdAt": 1329486946000, + "text": "

If you want to loop over an array, use the standard three-part for loop.

\n\n
for (var i = 0; i < myArray.length; i++) {\n    var arrayItem = myArray[i];\n}\n
\n\n

You can get some performance optimisations by caching myArray.length or iterating over it backwards.

\n", + "upvotes": 106, + "upvoterUsernames": [], + "downvotes": 52, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901db", + "creator": "joidegn", + "createdAt": 1329487109000, + "text": "

There isn't any for each loop in native JavaScript. You can either use libraries to get this functionality (I recommend Underscore.js), use a simple for in loop.

\n\n
for (var instance in objects) {\n   ...\n}\n
\n\n

However, note that there may be reasons to use an even simpler for loop (see Stack Overflow question Why is using “for…in” with array iteration such a bad idea?)

\n\n
var instance;\nfor (var i=0; i < objects.length; i++) {\n    var instance = objects[i];\n    ...\n}\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901dc", + "creator": "zzzzBov", + "createdAt": 1329487225000, + "text": "

Some C-style languages use foreach to loop through enumerations. In JavaScript this is done with the for..in loop structure:

\n\n
var index,\n    value;\nfor (index in obj) {\n    value = obj[index];\n}\n
\n\n

There is a catch. for..in will loop through each of the object's enumerable members, and the members on its prototype. To avoid reading values that are inherited through the object's prototype, simply check if the property belongs to the object:

\n\n
for (i in obj) {\n    if (obj.hasOwnProperty(i)) {\n        //do stuff\n    }\n}\n
\n\n

Additionally, ECMAScript 5 has added a forEach method to Array.prototype which can be used to enumerate over an array using a calback (the polyfill is in the docs so you can still use it for older browsers):

\n\n
arr.forEach(function (val, index, theArray) {\n    //do stuff\n});\n
\n\n

It's important to note that Array.prototype.forEach doesn't break when the callback returns false. jQuery and Underscore.js provide their own variations on each to provide loops that can be short-circuited.

\n", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901dd", + "creator": "Poonam", + "createdAt": 1329487267000, + "text": "

If you’re using the jQuery library, you can use jQuery.each:

\n\n\n\n
$.each(yourArray, function(index, value) {\n  // do your stuff here\n});\n
\n\n

EDIT :

\n\n

As per question, user want code in javascript instead of jquery so the edit is

\n\n
var length = yourArray.length;   \nfor (var i = 0; i < length; i++) {\n  // Do something with yourArray[i].\n}\n
\n", + "upvotes": 366, + "upvoterUsernames": [], + "downvotes": 99, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e0", + "creator": "Micka", + "createdAt": 1374052024000, + "text": "

An easy solution now would be to use the underscore.js library. It's providing many useful tools, such as each and will automatically delegate the job to the native forEach if available.

\n\n

A CodePen example of how it works is:

\n\n
var arr = [\"elemA\", \"elemB\", \"elemC\"];\n_.each(arr, function(elem, index, ar)\n{\n...\n});\n
\n\n

See also

\n\n\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901df", + "creator": "nmoliveira", + "createdAt": 1365553596000, + "text": "

A forEach implementation (see in jsFiddle):

\n\n
function forEach(list,callback) {\n  var length = list.length;\n  for (var n = 0; n < length; n++) {\n    callback.call(list[n]);\n  }\n}\n\nvar myArray = ['hello','world'];\n\nforEach(\n  myArray,\n  function(){\n    alert(this); // do something\n  }\n);\n
\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e1", + "creator": "Federico Piragua", + "createdAt": 1383359012000, + "text": "

Probably the for(i = 0; i < array.length; i++) loop is not the best choice. Why? If you have this:

\n\n
var array = new Array();\narray[1] = \"Hello\";\narray[7] = \"World\";\narray[11] = \"!\";\n
\n\n

The method will call from array[0] to array[2]. First, this will first reference variables you don't even have, second you would not have the variables in the array, and third this will make the code bolder. Look here, it's what I use:

\n\n
for(var i in array){\n    var el = array[i];\n    //If you want 'i' to be INT just put parseInt(i)\n    //Do something with el\n}\n
\n\n

And if you want it to be a function, you can do this:

\n\n
function foreach(array, call){\n    for(var i in array){\n        call(array[i]);\n    }\n}\n
\n\n

If you want to break, a little more logic:

\n\n
function foreach(array, call){\n    for(var i in array){\n        if(call(array[i]) == false){\n            break;\n        }\n    }\n}\n
\n\n

Example:

\n\n
foreach(array, function(el){\n    if(el != \"!\"){\n        console.log(el);\n    } else {\n        console.log(el+\"!!\");\n    }\n});\n
\n\n

It returns:

\n\n
//Hello\n//World\n//!!!\n
\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 25, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e2", + "creator": "Rajesh Paul", + "createdAt": 1387988071000, + "text": "

There are three implementations of foreach in jQuery as follows.

\n\n
var a = [3,2];\n\n$(a).each(function(){console.log(this.valueOf())}); //Method 1\n$.each(a, function(){console.log(this.valueOf())}); //Method 2\n$.each($(a), function(){console.log(this.valueOf())}); //Method 3\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e3", + "creator": "Tim", + "createdAt": 1391095553000, + "text": "

This is an iterator for NON-sparse list where the index starts at 0, which is the typical scenario when dealing with document.getElementsByTagName or document.querySelectorAll)

\n\n
function each( fn, data ) {\n\n    if(typeof fn == 'string')\n        eval('fn = function(data, i){' + fn + '}');\n\n    for(var i=0, L=this.length; i < L; i++) \n        fn.call( this[i], data, i );   \n\n    return this;\n}\n\nArray.prototype.each = each;  \n
\n\n

Examples of usage:

\n\n

Example #1

\n\n
var arr = [];\n[1, 2, 3].each( function(a){ a.push( this * this}, arr);\narr = [1, 4, 9]\n
\n\n

Example #2

\n\n
each.call(document.getElementsByTagName('p'), \"this.className = data;\",'blue');\n
\n\n

Each p tag gets class=\"blue\"

\n\n

Example #3

\n\n
each.call(document.getElementsByTagName('p'), \n    \"if( i % 2 == 0) this.className = data;\",\n    'red'\n);\n
\n\n

Every other p tag gets class=\"red\">

\n\n

Example #4

\n\n
each.call(document.querySelectorAll('p.blue'), \n    function(newClass, i) {\n        if( i < 20 )\n            this.className = newClass;\n    }, 'green'\n);\n
\n\n

And finally the first 20 blue p tags are changed to green

\n\n

Caution when using string as function: the function is created out-of-context and ought to be used only where you are certain of variable scoping. Otherwise, better to pass functions where scoping is more intuitive.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e4", + "creator": "Daniel W.", + "createdAt": 1396350926000, + "text": "

jQuery way using $.map:

\n\n
var data = [1, 2, 3, 4, 5, 6, 7];\n\nvar newData = $.map(data, function(element) {\n    if (element % 2 == 0) {\n        return element;\n    }\n});\n\n// newData = [2, 4, 6];\n
\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e5", + "creator": "Priyanshu Chauhan", + "createdAt": 1437980847000, + "text": "

There's no inbuilt ability to break in forEach. To interrupt execution use the Array#some like below:

\n\n
[1,2,3].some(function(number) {\n    return number === 1;\n});\n
\n\n

This works because some returns true as soon as any of the callbacks, executed in array order, returns true, short-circuiting the execution of the rest. \nOriginal Answer\nsee Array prototype for some

\n", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e6", + "creator": "Volkan Seçkin Akbayır", + "createdAt": 1440746841000, + "text": "

I also would like to add this as a composition of a reverse loop and an answer above for someone that would like this syntax too.

\n\n
var foo = [object,object,object];\nfor (var i = foo.length, item; item = foo[--i];) {\n    console.log(item);\n}\n
\n\n

Pros:

\n\n

The benefit for this: You have the reference already in the first like that won't need to be declared later with another line. It is handy when looping trough the object array.

\n\n

Cons:

\n\n

This will break whenever the reference is false - falsey (undefined, etc.). It can be used as an advantage though. However, it would make it a little bit harder to read. And also depending on the browser it can be \"not\" optimized to work faster than the original one.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901de", + "creator": "gaby de wilde", + "createdAt": 1362883071000, + "text": "

If you don't mind emptying the array:

\n\n
var x;\n\nwhile(x = y.pop()){ \n\n    alert(x); //do something \n\n}\n
\n\n

x will contain the last value of y and it will be removed from the array. You can also use shift() which will give and remove the first item from y.

\n", + "upvotes": 81, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e8", + "creator": "Anil Arya", + "createdAt": 1491756711000, + "text": "

ECMAScript 5 (the version on JavaScript) to work with Arrays:

\n\n

forEach - Iterates through every item in the array and do whatever you need with each item.

\n\n
['C', 'D', 'E'].forEach(function(element, index) {\n  console.log(element + \" is #\" + (index+1) + \" in the musical scale\");\n});\n\n// Output\n// C is the #1 in musical scale\n// D is the #2 in musical scale\n// E is the #3 in musical scale\n
\n\n

In case, more interested on operation on array using some inbuilt feature.

\n\n

map - It creates a new array with the result of the callback function. This method is good to be used when you need to format the elements of your array.

\n\n
// Let's upper case the items in the array\n['bob', 'joe', 'jen'].map(function(elem) {\n  return elem.toUpperCase();\n});\n\n// Output: ['BOB', 'JOE', 'JEN']\n
\n\n

reduce - As the name says, it reduces the array to a single value by calling the given function passing in the current element and the result of the previous execution.

\n\n
[1,2,3,4].reduce(function(previous, current) {\n  return previous + current;\n});\n// Output: 10\n// 1st iteration: previous=1, current=2 => result=3\n// 2nd iteration: previous=3, current=3 => result=6\n// 3rd iteration: previous=6, current=4 => result=10\n
\n\n

every - Returns true or false if all the elements in the array pass the test in the callback function.

\n\n
// Check if everybody has 18 years old of more.\nvar ages = [30, 43, 18, 5];\nages.every(function(elem) {\n  return elem >= 18;\n});\n\n// Output: false\n
\n\n

filter - Very similar to every except that filter returns an array with the elements that return true to the given function.

\n\n
// Finding the even numbers\n[1,2,3,4,5,6].filter(function(elem){\n  return (elem % 2 == 0)\n});\n\n// Output: [2,4,6]\n
\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e7", + "creator": "Zaz", + "createdAt": 1464279284000, + "text": "

As of ECMAScript 6:

\n\n

\r\n
\r\n
list = [0, 1, 2, 3]\r\nfor (let obj of list) {\r\n    console.log(obj)\r\n}
\r\n
\r\n
\r\n

\n\n

Where of avoids the oddities associated with in and makes it work like the for loop of any other language, and let binds i within the loop as opposed to within the function.

\n\n

The braces ({}) can be omitted when there is only one command (e.g. in the example above).

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901e9", + "creator": "anteAdamovic", + "createdAt": 1510241504000, + "text": "

A way closest to your idea would be to use Array.forEach() which accepts a closure function which will be executed for each element of the array.

\n\n
myArray.forEach(\n  (item) => {\n    // Do something\n    console.log(item);\n  }\n);\n
\n\n

Another viable way would be to use Array.map() which works in the same way, but it also takes all values that you return and returns them in a new array (essentially mapping each element to a new one), like this:

\n\n
var myArray = [1, 2, 3];\nmyArray = myArray.map(\n  (item) => {\n    return item + 1;\n  }\n);\n\nconsole.log(myArray); // [2, 3, 4]\n
\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ea", + "creator": "Murtuza Husain", + "createdAt": 1510380987000, + "text": "

The lambda syntax doesn't usually work in Internet Explorer 10 or below.

\n\n

I usually use the

\n\n
[].forEach.call(arrayName,function(value,index){\n    console.log(\"value of the looped element\" + value);\n    console.log(\"index of the looped element\" + index);\n});\n
\n\n

If you are a jQuery fan and already have a jQuery file running, you should reverse the positions of the index and value parameters

\n\n
$(\"#ul>li\").each(function(**index, value**){\n    console.log(\"value of the looped element\" + value);\n    console.log(\"index of the looped element\" + index);\n});\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901eb", + "creator": "John", + "createdAt": 1515693817000, + "text": "
var a = [\"car\", \"bus\", \"truck\"]\na.forEach(function(item, index) {\n    console.log(\"Index\" + index);\n    console.log(\"Element\" + item);\n})\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ec", + "creator": "Harun Or Rashid", + "createdAt": 1527671130000, + "text": "

If you want to use forEach(), it will look like -

\n\n
theArray.forEach ( element => {\n    console.log(element);\n});\n
\n\n

If you want to use for(), it will look like -

\n\n
for(let idx = 0; idx < theArray.length; idx++){\n    let element = theArray[idx];\n    console.log(element);\n}\n
\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ed", + "creator": "Nouman Dilshad", + "createdAt": 1531830651000, + "text": "

You can call forEach like this:

\n\n

forEach will iterate over the array you provide and for each iteration it will have element which holds the value of that iteration. If you need index you can get the current index by passing the i as the second parameter in the callback function for forEach.

\n\n

Foreach is basically a High Order Function, Which takes another function as its parameter.

\n\n
let theArray= [1,3,2];\n\ntheArray.forEach((element) => {\n  // Use the element of the array\n  console.log(element)\n}\n
\n\n

Output:

\n\n
1\n3\n2\n
\n\n

You can also iterate over an array like this:

\n\n
for (let i=0; i<theArray.length; i++) {\n  console.log(i); // i will have the value of each index\n}\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ee", + "creator": "Peko Chan", + "createdAt": 1544612789000, + "text": "

I come from Python, and I found this way much clearer.

\n\n

theArray being the array, and instance being the elements of the array:

\n\n
for (let instance of theArray)\n{\n    console.log(\"The instance\", instance);\n}\n
\n\n

or

\n\n
for (instance in theArray)\n{\n    console.log(\"The instance\", instance);\n}\n
\n\n

compare to:

\n\n
theArray.forEach(function(instance) {\n    console.log(instance);\n});\n
\n\n

But at the end of the day both are doing the same thing.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901ef", + "creator": "arul prince", + "createdAt": 1548076099000, + "text": "

\r\n
\r\n
// Looping through arrays using the foreach ECMAScript 6 way\r\n\r\nvar data = new Array(1, 2, 3, 4, 5);\r\ndata.forEach((val,index) => {\r\n    console.log(\"index: \", index); // Index\r\n    console.log(\"value: \", val); // Value\r\n});
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f0", + "creator": "alejoko", + "createdAt": 1549120380000, + "text": "

If you want to keep your code in the functional way, use map:

\n\n
theArray.map(instance => do_something);\n
\n\n

In this way you will generate a new array to future operation and will skip any not desirable side effect.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f1", + "creator": "subhashish negi", + "createdAt": 1552053849000, + "text": "

If you want to loop through an array of objects with the arrow function:

\n\n

\r\n
\r\n
let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}];\r\n\r\narr.forEach((person)=>{\r\n  console.log('I am ' + person.name + ' and I am ' + person.age + ' old');\r\n})
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f2", + "creator": "antelove", + "createdAt": 1576319090000, + "text": "

Mozilla documentation

\n\n

\r\n
\r\n
/* Get all forms */\r\ndocument.querySelectorAll( \"form\" ).forEach( form => {\r\n\r\n  /* For each form, add the onsubmit event */\r\n  form.addEventListener( \"submit\", event => {\r\n    event.preventDefault(); // Return false\r\n\r\n    /* Display it */\r\n    alert(event.target.action);\r\n    console.log(event.target);\r\n  } );\r\n\r\n} );
\r\n
<form action=\"form1.php\" >\r\n  <input type=\"submit\" value=\"Submit\" />\r\n</form>\r\n<form action=\"form2.php\" >\r\n  <input type=\"submit\" value=\"Submit\" />\r\n</form>\r\n<form action=\"form3.php\" >\r\n  <input type=\"submit\" value=\"Submit\" />\r\n</form>
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f3", + "creator": "Mustafa Kunwa", + "createdAt": 1576758361000, + "text": "

You can use:

\n\n
    \n
  1. ForEach

    \n\n
    theArray.forEach(function (array, index) {\n    console.log(index);\n    console.log(array);\n});\n
  2. \n
  3. for

    \n\n
    for(var i=0; i<theArray.length; i++) {\n    console.log(i)\n}\n
  4. \n
  5. map

    \n\n
    theArray.map(x => console.log(x));\n
  6. \n
  7. map

    \n\n
    theArray.filter(x => console.log(x));\n
  8. \n
\n\n

And there are many others for iteration.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f4", + "creator": "vkarpov15", + "createdAt": 1577129479000, + "text": "

I'd argue that for/of is the way to go:

\n\n

\r\n
\r\n
const arr = ['a', 'b', 'c'];\r\n\r\nfor (const v of arr) {\r\n  console.log(v); // Prints \"a\", \"b\", \"c\"\r\n}
\r\n
\r\n
\r\n

\n\n\n\n

You can read more in this blog post on for/of vs forEach().

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f5", + "creator": "ankitkanojia", + "createdAt": 1579680844000, + "text": "

As per the new updated feature ECMAScript 6 (ES6) and ECMAScript 2015, you can use the following options with loops:

\n\n
\n

for loops

\n
\n\n
for(var i = 0; i < 5; i++){\n  console.log(i);\n}\n\n// Output: 0,1,2,3,4\n
\n\n
\n

for...in loops

\n
\n\n
let obj = {\"a\":1, \"b\":2}\n\nfor(let k in obj){\n  console.log(k)\n}\n\n// Output: a,b\n
\n\n
\n

Array.forEach()

\n
\n\n
let array = [1,2,3,4]\n\narray.forEach((x) => {\n  console.log(x);\n})\n\n// Output: 1,2,3,4\n
\n\n
\n

for...of loops

\n
\n\n
let array = [1,2,3,4]\n\nfor(let x of array){\n  console.log(x);\n}\n\n// Output: 1,2,3,4\n
\n\n
\n

while loops

\n
\n\n
let x = 0\n\nwhile(x < 5){\n  console.log(x)\n  x++\n}\n\n// Output: 1,2,3,4\n
\n\n
\n

do...while loops

\n
\n\n
let x = 0\n\ndo{\n  console.log(x)\n  x++\n}while(x < 5)\n\n// Output: 1,2,3,4\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f6", + "creator": "Muhammad Waqas", + "createdAt": 1585436000000, + "text": "

Suppose we have an array of subjects:

\n
let ddl = new Array();\nif (subjects) {\n    subjects.forEach(function (s) {ddl.push({"id": s.id, "label": s.name});});\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f8", + "creator": "mdmundo", + "createdAt": 1657706880000, + "text": "

Use for...of where possible.

\n

\"Table\"

\n

As one can see in the image above, for...of should be used wherever it fits. Since it supports async functions, skips non-numeric properties and is able to prevent messing up the loop by accidentally modifying the loop index.

\n

Syntax

\n
const nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\nfor (const num of nums) {\n  /* Do something with num */\n}\n
\n

See for...of reference for more examples, link to specification and difference between for...of and for...in.

\n

Also see this tutorial and source of the image.

\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321be082fcc3049e901f7", + "creator": "Felipe Chernicharo", + "createdAt": 1630350912000, + "text": "

for...of   |   forEach   |   map

\n

Using modern JavaScript syntax to iterate through arrays

\n
const fruits = ['🍎', '🍋', '🍌' ]\n
\n

👉🏽   for...of

\n
for (const fruit of fruits) {\n    console.log(fruit)  // '🍎', '🍋', '🍌'\n}\n
\n

👉🏽   forEach

\n
fruits.forEach(fruit => {\n    console.log(fruit)  // '🍎', '🍋', '🍌'\n})\n
\n

👉🏽   map

\n

*Different from the two above, map() creates a new array and expects you to return something after each iteration.

\n
fruits.map(fruit => fruit)   // ['🍎', '🍋', '🍌' ]\n
\n

🛑  Important: As map() is meant to return a value at each iteration, it is an ideal method for transforming elements in arrays:

\n
fruits.map(fruit => 'cool ' + fruit)   // ['cool 🍎', 'cool 🍋', 'cool 🍌' ]\n
\n

On the other hand, for...of and forEach( ) don't need to return anything and that's why we typically use them to perform logic tasks that manipulate stuff outside.

\n

So to speak, you're going to find if () statements, side effects, and logging activities in these two.

\n

👌🏾  TIP: you can also have the index (as well as the whole array) in each iteration in your .map() or .forEach() functions.

\n

Just pass additional arguments to them:

\n
fruits.map((fruit, i) =>  i + '  ' + fruit)\n\n// ['0 🍎', '1 🍋', '2 🍌' ]\n\nfruits.forEach((f, i, arr) => {\n    console.log( f + ' ' + i + ' ' +  arr )\n})\n\n// 🍎  0  🍎, 🍋, 🍌,\n// 🍋  1  🍎, 🍋, 🍌,\n// 🍌  2  🍎, 🍋, 🍌,\n
\n", + "upvotes": 97, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 4996431, + "uvac": 4996463 + } + }, + { + "_id": "62f321bb082fcc3049e8fecb", + "title": "How to round to at most 2 decimal places, if necessary", + "title-lowercase": "how to round to at most 2 decimal places, if necessary", + "creator": "stinkycheeseman", + "createdAt": 1344273468000, + "status": "open", + "text": "

I'd like to round at most two decimal places, but only if necessary.

\n

Input:

\n
10\n1.7777777\n9.1\n
\n

Output:

\n
10\n1.78\n9.1\n
\n

How can I do this in JavaScript?

\n", + "upvotes": 4599, + "upvoterUsernames": [], + "downvotes": 883, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 3668309, + "answers": 85, + "answerItems": [ + { + "_id": "62f321c1082fcc3049e905ea", + "creator": "totten", + "createdAt": 1344273679000, + "text": "

This may help you:

\n
var result = Math.round(input*100)/100;\n
\n

For more information, you can have a look at Math.round(num) vs num.toFixed(0) and browser inconsistencies

\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91ad5", + "creator": "Kamlesh", + "createdAt": 1651666177000, + "text": "Math.round(1.965 * 100) / 100 will be 1.96 . it's wrong.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ec", + "creator": "Shreedhar", + "createdAt": 1344273700000, + "text": "

It may work for you,

\n\n
Math.round(num * 100)/100;\n
\n\n

to know the difference between toFixed and round. You can have a look at Math.round(num) vs num.toFixed(0) and browser inconsistencies.

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91ad8", + "creator": "Kamlesh", + "createdAt": 1651666205000, + "text": "Math.round(1.965 * 100) / 100 will be 1.96 . it's wrong.", + "upvotes": 191, + "upvoterUsernames": [], + "downvotes": 191, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905eb", + "creator": "AceCorban", + "createdAt": 1344273683000, + "text": "

Consider .toFixed() and .toPrecision():

\n\n

http://www.javascriptkit.com/javatutors/formatnumber.shtml

\n", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91ada", + "creator": "Yeheshuah", + "createdAt": 1624345408000, + "text": "A Kunin has told a bit about this at below answer.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32502082fcc3049e91adc", + "creator": "Manny Alvarado", + "createdAt": 1643999204000, + "text": "toFixed() sometimes doesn't round correctly. I've seen it myself. Math.round is better", + "upvotes": 1169, + "upvoterUsernames": [], + "downvotes": 1169, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905e9", + "creator": "Brian Ustas", + "createdAt": 1344273631000, + "text": "

Use Math.round() :

\n
Math.round(num * 100) / 100\n
\n

Or to be more specific and to ensure things like 1.005 round correctly, use Number.EPSILON :

\n
Math.round((num + Number.EPSILON) * 100) / 100\n
\n", + "upvotes": 8609, + "upvoterUsernames": [], + "downvotes": 3736, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91adf", + "creator": "Satish Patro", + "createdAt": 1612512214000, + "text": "Math.round((224.98499999 + Number.EPSILON) * 100) / 100 224.98 where as it should have ben 224.95 right?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32502082fcc3049e91ae1", + "creator": "Sun", + "createdAt": 1616474997000, + "text": "Math.round(1.255 * 100) / 100 will be 1.25 . it's wrong", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32502082fcc3049e91ae3", + "creator": "JokerMartini", + "createdAt": 1622117603000, + "text": "Not sure why this is marked as the correct answer. It does in fact not work correctly", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32502082fcc3049e91ae5", + "creator": "Nauraushaun", + "createdAt": 1630617957000, + "text": "I find that it rounds wrong for 10.075. Gives 10.07 rather than 10.08, even with the epsilon fix.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32502082fcc3049e91ae7", + "creator": "yuyicman", + "createdAt": 1630637715000, + "text": "Math.round((519.805+ Number.EPSILON) * 100) / 100, it rounds to 519.8", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ed", + "creator": "JayDM", + "createdAt": 1344274022000, + "text": "

Here is a simple way to do it:

\n\n
Math.round(value * 100) / 100\n
\n\n

You might want to go ahead and make a separate function to do it for you though:

\n\n
function roundToTwo(value) {\n    return(Math.round(value * 100) / 100);\n}\n
\n\n

Then you would simply pass in the value.

\n\n

You could enhance it to round to any arbitrary number of decimals by adding a second parameter.

\n\n
function myRound(value, places) {\n    var multiplier = Math.pow(10, places);\n\n    return (Math.round(value * multiplier) / multiplier);\n}\n
\n", + "upvotes": 95, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905ee", + "creator": "A Kunin", + "createdAt": 1349915244000, + "text": "

If the value is a text type:

\n
parseFloat("123.456").toFixed(2);\n
\n

If the value is a number:

\n
var numb = 123.23454;\nnumb = numb.toFixed(2);\n
\n

There is a downside that values like 1.5 will give "1.50" as the output. A fix suggested by @minitech:

\n
var numb = 1.5;\nnumb = +numb.toFixed(2);\n// Note the plus sign that drops any "extra" zeroes at the end.\n// It changes the result (which is a string) into a number again (think "0 + foo"),\n// which means that it uses only as many digits as necessary.\n
\n

It seems like Math.round is a better solution. But it is not! In some cases it will not round correctly:

\n
Math.round(1.005 * 100)/100 // Returns 1 instead of expected 1.01!\n
\n

toFixed() will also not round correctly in some cases (tested in Chrome v.55.0.2883.87)!

\n

Examples:

\n
parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.\nparseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.\n// However, it will return correct result if you round 1.5551.\nparseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.\n\n1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.\n// However, it will return correct result if you round 1.35551.\n1.35551.toFixed(2); // Returns 1.36 as expected.\n
\n

I guess, this is because 1.555 is actually something like float 1.55499994 behind the scenes.

\n

Solution 1 is to use a script with required rounding algorithm, for example:

\n
function roundNumber(num, scale) {\n  if(!("" + num).includes("e")) {\n    return +(Math.round(num + "e+" + scale)  + "e-" + scale);\n  } else {\n    var arr = ("" + num).split("e");\n    var sig = ""\n    if(+arr[1] + scale > 0) {\n      sig = "+";\n    }\n    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);\n  }\n}\n
\n

It is also at Plunker.

\n

Note: This is not a universal solution for everyone. There are several different rounding algorithms. Your implementation can be different, and it depends on your requirements. See also Rounding.

\n

Solution 2 is to avoid front end calculations and pull rounded values from the backend server.

\n

Another possible solution, which is not a bulletproof either.

\n
Math.round((num + Number.EPSILON) * 100) / 100\n
\n

In some cases, when you round a number like 1.3549999999999998, it will return an incorrect result. It should be 1.35, but the result is 1.36.

\n", + "upvotes": 4013, + "upvoterUsernames": [], + "downvotes": 103, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32502082fcc3049e91aea", + "creator": "Nando", + "createdAt": 1652885860000, + "text": "same problem as Ustas' suggestion. 10.075 input = 10.07 output. No good.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ef", + "creator": "machineaddict", + "createdAt": 1371116139000, + "text": "

None of the answers found here is correct. stinkycheeseman asked to round up, but you all rounded the number.

\n

To round up, use this:

\n
Math.ceil(num * 100)/100;\n
\n", + "upvotes": 101, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91aed", + "creator": "A Kunin", + "createdAt": 1643655160000, + "text": "1.3549999999999998 will return incorrect result. Should be 1.35 but result is 1.36.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91aee", + "creator": "user207421", + "createdAt": 1653193550000, + "text": "Most values will return an incorrect result. Try it.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f0", + "creator": "Andrei", + "createdAt": 1371451733000, + "text": "

Here is a function I came up with to do \"round up\". I used double Math.round to compensate for JavaScript's inaccurate multiplying, so 1.005 will be correctly rounded as 1.01.

\n\n
function myRound(number, decimalplaces){\n    if(decimalplaces > 0){\n        var multiply1 = Math.pow(10,(decimalplaces + 4));\n        var divide1 = Math.pow(10, decimalplaces);\n        return Math.round(Math.round(number * multiply1)/10000 )/divide1;\n    }\n    if(decimalplaces < 0){\n        var divide2 = Math.pow(10, Math.abs(decimalplaces));\n        var multiply2 = Math.pow(10, Math.abs(decimalplaces));\n        return Math.round(Math.round(number / divide2) * multiply2);\n    }\n    return Math.round(number);\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91af1", + "creator": "Ashutosh", + "createdAt": 1401101527000, + "text": "An upvote as this maybe a long winded way of doing it but it works.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f1", + "creator": "vsvasya", + "createdAt": 1372812074000, + "text": "

I wrote the following set of functions for myself. Maybe it will help you too.

\n\n
function float_exponent(number) {\n    exponent = 1;\n    while (number < 1.0) {\n        exponent += 1\n        number *= 10\n    }\n    return exponent;\n}\nfunction format_float(number, extra_precision) {\n    precision = float_exponent(number) + (extra_precision || 0)\n    return number.toFixed(precision).split(/\\.?0+$/)[0]\n}\n
\n\n

Usage:

\n\n
format_float(1.01); // 1\nformat_float(1.06); // 1.1\nformat_float(0.126); // 0.13\nformat_float(0.000189); // 0.00019\n
\n\n

For you case:

\n\n
format_float(10, 1); // 10\nformat_float(9.1, 1); // 9.1\nformat_float(1.77777, 1); // 1.78\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905f4", + "creator": "MarkG", + "createdAt": 1377089788000, + "text": "

You can use

\n
function roundToTwo(num) {\n    return +(Math.round(num + "e+2")  + "e-2");\n}\n
\n

I found this on MDN. Their way avoids the problem with 1.005 that was mentioned.

\n
roundToTwo(1.005)\n1.01\nroundToTwo(10)\n10\nroundToTwo(1.7777777)\n1.78\nroundToTwo(9.1)\n9.1\nroundToTwo(1234.5678)\n1234.57\n
\n", + "upvotes": 900, + "upvoterUsernames": [], + "downvotes": 306, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91af5", + "creator": "Antony Ng", + "createdAt": 1610100166000, + "text": "Pass a number with e and it returns NaN e.g. 1.19e-7", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91af7", + "creator": "Nabi K.A.Z.", + "createdAt": 1650642586000, + "text": "This does not work well for negative numbers.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f2", + "creator": "coreyavis", + "createdAt": 1375002091000, + "text": "

I still don't think anyone gave him the answer to how to only do the rounding if needed. The easiest way I see to do it is to check if there is even a decimal in the number, like so:

\n\n
var num = 3.21;\nif ( (num+\"\").indexOf('.') >= 0 ) { //at least assert to string first...\n    // whatever code you decide to use to round\n}\n
\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91afa", + "creator": "Jason Foglia", + "createdAt": 1381775644000, + "text": "The Question asked how to round numbers not just check if they need to be rounded.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91afc", + "creator": "Peter Mortensen", + "createdAt": 1651765027000, + "text": "What language? This question is tagged with JavaScript. IndexOf (uppercase "I") would work in C#.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f3", + "creator": "arielf", + "createdAt": 1376332228000, + "text": "

Here is a prototype method:

\n\n
Number.prototype.round = function(places){\n    places = Math.pow(10, places); \n    return Math.round(this * places)/places;\n}\n\nvar yournum = 10.55555;\nyournum = yournum.round(2);\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905f5", + "creator": "Daniel De León", + "createdAt": 1381504899000, + "text": "

To not deal with many 0s, use this variant:

\n\n
Math.round(num * 1e2) / 1e2\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91aff", + "creator": "Daniel De León", + "createdAt": 1651948794000, + "text": "Oh... "To not deal with many 0s" I wrote it just to emphasize the e-notation (elevation notation) B-)", + "upvotes": 180, + "upvoterUsernames": [], + "downvotes": 180, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f6", + "creator": "Lavamantis", + "createdAt": 1383291659000, + "text": "

MarkG's answer is the correct one. Here's a generic extension for any number of decimal places.

\n
Number.prototype.round = function(places) {\n  return +(Math.round(this + "e+" + places)  + "e-" + places);\n}\n
\n

Usage:

\n
var n = 1.7777;    \nn.round(2); // 1.78\n
\n

Unit test:

\n
it.only('should round floats to 2 places', function() {\n    \n  var cases = [\n    { n: 10,      e: 10,    p:2 },\n    { n: 1.7777,  e: 1.78,  p:2 },\n    { n: 1.005,   e: 1.01,  p:2 },\n    { n: 1.005,   e: 1,     p:0 },\n    { n: 1.77777, e: 1.8,   p:1 }\n  ]\n    \n  cases.forEach(function(testCase) {\n    var r = testCase.n.round(testCase.p);\n    assert.equal(r, testCase.e, 'didn\\'t get right number');\n  });\n})\n
\n", + "upvotes": 237, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91b01", + "creator": "Learner", + "createdAt": 1614930620000, + "text": "What if the input number is already in exponential form? You will get NaN", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91b03", + "creator": "Delight", + "createdAt": 1639603634000, + "text": "oh come on dont modify prototypes", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f8", + "creator": "Scott Stafford", + "createdAt": 1403098504000, + "text": "

If you happen to already be using the D3.js library, they have a powerful number formatting library.

\n

Rounding specifically is at D3 round.

\n

In your case, the answer is:

\n
> d3.round(1.777777, 2)\n1.78\n\n> d3.round(1.7, 2)\n1.7\n\n> d3.round(1, 2)\n1\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91b06", + "creator": "Scott Stafford", + "createdAt": 1403201480000, + "text": "But documented, and, being in a library, I don't have the same need to check browser compatibility BS.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91b08", + "creator": "daviestar", + "createdAt": 1407230753000, + "text": "using Math.pow(n) allows for d3.round(12, -1) == 10", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905f7", + "creator": "astorije", + "createdAt": 1390531713000, + "text": "

MarkG and Lavamantis offered a much better solution than the one that has been accepted. It's a shame they don't get more upvotes!

\n

Here is the function I use to solve the floating point decimals issues also based on MDN. It is even more generic (but less concise) than Lavamantis's solution:

\n
function round(value, exp) {\n  if (typeof exp === 'undefined' || +exp === 0)\n    return Math.round(value);\n\n  value = +value;\n  exp  = +exp;\n\n  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))\n    return NaN;\n\n  // Shift\n  value = value.toString().split('e');\n  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));\n\n  // Shift back\n  value = value.toString().split('e');\n  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));\n}\n
\n

Use it with:

\n
round(10.8034, 2);      // Returns 10.8\nround(1.275, 2);        // Returns 1.28\nround(1.27499, 2);      // Returns 1.27\nround(1.2345678e+2, 2); // Returns 123.46\n
\n

Compared to Lavamantis's solution, we can do...

\n
round(1234.5678, -2); // Returns 1200\nround("123.45");      // Returns 123\n
\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32503082fcc3049e91b0a", + "creator": "astorije", + "createdAt": 1436132575000, + "text": "Your solution does not cover some cases as opposed to MDN's solution. While it may be shorter, it is not accurate...", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32503082fcc3049e91b0c", + "creator": "Jorge Sampayo", + "createdAt": 1469756315000, + "text": "round(-1835.665,2) => -1835.66", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905fa", + "creator": "user3447070", + "createdAt": 1405080269000, + "text": "

Try to use the jQuery .number plug-in:

\n\n
var number = 19.8000000007;\nvar res = 1 * $.number(number, 2);\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905f9", + "creator": "user3711536", + "createdAt": 1403692027000, + "text": "
+(10).toFixed(2); // = 10\n+(10.12345).toFixed(2); // = 10.12\n\n(10).toFixed(2); // = 10.00\n(10.12345).toFixed(2); // = 10.12\n
\n", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b0f", + "creator": "Peter Mortensen", + "createdAt": 1651767323000, + "text": "OK, the OP has left the building. Perhaps someone else can chime in?", + "upvotes": 1190, + "upvoterUsernames": [], + "downvotes": 1190, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905fb", + "creator": "user", + "createdAt": 1406880136000, + "text": "

A precise rounding method. Source: Mozilla

\n\n
(function(){\n\n    /**\n     * Decimal adjustment of a number.\n     *\n     * @param   {String}    type    The type of adjustment.\n     * @param   {Number}    value   The number.\n     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).\n     * @returns {Number}            The adjusted value.\n     */\n    function decimalAdjust(type, value, exp) {\n        // If the exp is undefined or zero...\n        if (typeof exp === 'undefined' || +exp === 0) {\n            return Math[type](value);\n        }\n        value = +value;\n        exp = +exp;\n        // If the value is not a number or the exp is not an integer...\n        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {\n            return NaN;\n        }\n        // Shift\n        value = value.toString().split('e');\n        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));\n        // Shift back\n        value = value.toString().split('e');\n        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));\n    }\n\n    // Decimal round\n    if (!Math.round10) {\n        Math.round10 = function(value, exp) {\n            return decimalAdjust('round', value, exp);\n        };\n    }\n    // Decimal floor\n    if (!Math.floor10) {\n        Math.floor10 = function(value, exp) {\n            return decimalAdjust('floor', value, exp);\n        };\n    }\n    // Decimal ceil\n    if (!Math.ceil10) {\n        Math.ceil10 = function(value, exp) {\n            return decimalAdjust('ceil', value, exp);\n        };\n    }\n})();\n
\n\n

Examples:

\n\n
// Round\nMath.round10(55.55, -1); // 55.6\nMath.round10(55.549, -1); // 55.5\nMath.round10(55, 1); // 60\nMath.round10(54.9, 1); // 50\nMath.round10(-55.55, -1); // -55.5\nMath.round10(-55.551, -1); // -55.6\nMath.round10(-55, 1); // -50\nMath.round10(-55.1, 1); // -60\nMath.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above\n// Floor\nMath.floor10(55.59, -1); // 55.5\nMath.floor10(59, 1); // 50\nMath.floor10(-55.51, -1); // -55.6\nMath.floor10(-51, 1); // -60\n// Ceil\nMath.ceil10(55.51, -1); // 55.6\nMath.ceil10(51, 1); // 60\nMath.ceil10(-55.59, -1); // -55.5\nMath.ceil10(-59, 1); // -50\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e905fc", + "creator": "Deele", + "createdAt": 1412587789000, + "text": "

I just wanted to share my approach, based on previously mentioned answers:

\n\n

Let's create a function that rounds any given numeric value to a given amount of decimal places:

\n\n
function roundWDecimals(n, decimals) {\n    if (!isNaN(parseFloat(n)) && isFinite(n)) {\n        if (typeof(decimals) == typeof(undefined)) {\n            decimals = 0;\n        }\n        var decimalPower = Math.pow(10, decimals);\n        return Math.round(parseFloat(n) * decimalPower) / decimalPower;\n    }\n    return NaN;\n}\n
\n\n

And introduce a new \"round\" method for numbers prototype:

\n\n
Object.defineProperty(Number.prototype, 'round', {\n    enumerable: false,\n    value: function(decimals) {\n        return roundWDecimals(this, decimals);\n    }\n});\n
\n\n

And you can test it:

\n\n

\r\n
\r\n
function roundWDecimals(n, decimals) {\r\n    if (!isNaN(parseFloat(n)) && isFinite(n)) {\r\n        if (typeof(decimals) == typeof(undefined)) {\r\n            decimals = 0;\r\n        }\r\n        var decimalPower = Math.pow(10, decimals);\r\n        return Math.round(parseFloat(n) * decimalPower) / decimalPower;\r\n    }\r\n    return NaN;\r\n}\r\nObject.defineProperty(Number.prototype, 'round', {\r\n    enumerable: false,\r\n    value: function(decimals) {\r\n        return roundWDecimals(this, decimals);\r\n    }\r\n});\r\n\r\nvar roundables = [\r\n    {num: 10, decimals: 2},\r\n    {num: 1.7777777, decimals: 2},\r\n    {num: 9.1, decimals: 2},\r\n    {num: 55.55, decimals: 1},\r\n    {num: 55.549, decimals: 1},\r\n    {num: 55, decimals: 0},\r\n    {num: 54.9, decimals: 0},\r\n    {num: -55.55, decimals: 1},\r\n    {num: -55.551, decimals: 1},\r\n    {num: -55, decimals: 0},\r\n    {num: 1.005, decimals: 2},\r\n    {num: 1.005, decimals: 2},\r\n    {num: 19.8000000007, decimals: 2},\r\n  ],\r\n  table = '<table border=\"1\"><tr><th>Num</th><th>Decimals</th><th>Result</th></tr>';\r\n$.each(roundables, function() {\r\n  table +=\r\n    '<tr>'+\r\n      '<td>'+this.num+'</td>'+\r\n      '<td>'+this.decimals+'</td>'+\r\n      '<td>'+this.num.round(this.decimals)+'</td>'+\r\n    '</tr>'\r\n  ;\r\n});\r\ntable += '</table>';\r\n$('.results').append(table);
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\r\n<div class=\"results\"></div>
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b12", + "creator": "Soldeplata Saketos", + "createdAt": 1480063936000, + "text": "or do the same with only 3 lines of code: stackoverflow.com/a/40800717/4537906", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b13", + "creator": "Peter Mortensen", + "createdAt": 1651767683000, + "text": "(The syntax highlighting is weird for NaN (the first return statement). It is probably due to the syntax highlighter, not this post.)", + "upvotes": 123, + "upvoterUsernames": [], + "downvotes": 123, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905fd", + "creator": "Harish.bazee", + "createdAt": 1412765474000, + "text": "

Use this function Number(x).toFixed(2);

\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b15", + "creator": "user993683", + "createdAt": 1442035042000, + "text": "Wrap it all in Number again, if you don't want it returned as a string: Number(Number(x).toFixed(2));", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b17", + "creator": "bgusach", + "createdAt": 1544803776000, + "text": "The Number call is not necessary, x.toFixed(2) works.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b19", + "creator": "Mohan Ram", + "createdAt": 1552631219000, + "text": "@bgusach Number call needed, since the statement x.toFixed(2) return string and not a number. To convert again to number we need to wrap with Number", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b1a", + "creator": "Eugene Mala", + "createdAt": 1559940040000, + "text": "When using this method (1).toFixed(2) returns 1.00, but questioner needed 1 in this case.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b1c", + "creator": "Adam Jagosz", + "createdAt": 1567000807000, + "text": "This doesn't work, 1.005.toFixed(2) yields "1" when it should be "1.01".", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905fe", + "creator": "petermeissner", + "createdAt": 1413800472000, + "text": "

Try this lightweight solution:

\n
function round(x, digits){\n  return parseFloat(x.toFixed(digits))\n}\n\n round(1.222,  2);\n // 1.22\n round(1.222, 10);\n // 1.222\n
\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b1f", + "creator": "user993683", + "createdAt": 1442034968000, + "text": "Anyone know if there's any difference between this and return Number(x.toFixed(digits))?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b20", + "creator": "petermeissner", + "createdAt": 1442052985000, + "text": "@JoeRocc ... should make no difference as far a I can see since .toFixed() allows only for numbers anyways .", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32504082fcc3049e91b22", + "creator": "racribeiro", + "createdAt": 1640781224000, + "text": "This works, but it adds unnecessary complexity to the system as converts a float into a string and then parses the string back to a float.", + "upvotes": 268, + "upvoterUsernames": [], + "downvotes": 268, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e905ff", + "creator": "Gourav Singla", + "createdAt": 1413910941000, + "text": "

One can use .toFixed(NumberOfDecimalPlaces).

\n\n
var str = 10.234.toFixed(2); // => '10.23'\nvar number = Number(str); // => 10.23\n
\n", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 66, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b24", + "creator": "serge", + "createdAt": 1658915600000, + "text": "does not trim zeros", + "upvotes": 341, + "upvoterUsernames": [], + "downvotes": 341, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90600", + "creator": "bigpotato", + "createdAt": 1424894970000, + "text": "

Easiest way:

\n\n

+num.toFixed(2)

\n\n

It converts it to a string, and then back into an integer / float.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32504082fcc3049e91b27", + "creator": "mjs", + "createdAt": 1430853636000, + "text": "@Edmund It's supposed to return 1.01, not 1.00", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90601", + "creator": "Arne H. Bitubekk", + "createdAt": 1426604260000, + "text": "

You could also override the Math.round function to do the rounding correct and add a parameter for decimals and use it like: Math.round(Number, Decimals). Keep in mind that this overrides the built in component Math.round and giving it another property then it original is.

\n\n
var round = Math.round;\nMath.round = function (value, decimals) {\n  decimals = decimals || 0;\n  return Number(round(value + 'e' + decimals) + 'e-' + decimals);\n}\n
\n\n

Then you can simply use it like this:

\n\n
Math.round(1.005, 2);\n
\n\n

https://jsfiddle.net/k5tpq3pd/3/

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90602", + "creator": "mjs", + "createdAt": 1430236682000, + "text": "

Here is the shortest and complete answer:

\n
function round(num, decimals) {\n        var n = Math.pow(10, decimals);\n        return Math.round( (n * num).toFixed(decimals) )  / n;\n};\n
\n

This also takes care of the example case 1.005 which will return 1.01.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90604", + "creator": "Jérôme Beau", + "createdAt": 1432410228000, + "text": "

One way to achieve such a rounding only if necessary is to use Number.prototype.toLocaleString():

\n\n
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})\n
\n\n

This will provide exactly the output you expect, but as strings. You can still convert those back to numbers if that's not the data type you expect.

\n", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90605", + "creator": "stanleyxu2005", + "createdAt": 1456810568000, + "text": "

There are a couple of ways to do that. For people like me, Lodash's variant

\n
function round(number, precision) {\n    var pair = (number + 'e').split('e')\n    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))\n    pair = (value + 'e').split('e')\n    return +(pair[0] + 'e' + (+pair[1] - precision))\n}\n
\n

Usage:

\n
round(0.015, 2) // 0.02\nround(1.005, 2) // 1.01\n
\n\n

If your project uses jQuery or Lodash, you can also find the proper round method in the libraries.

\n", + "upvotes": 45, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b2b", + "creator": "Marcos Lima", + "createdAt": 1467727914000, + "text": "On Firefox, alert((+1234).toFixed(2)) shows "1234.00".", + "upvotes": 32, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90603", + "creator": "Ritesh Dhuri", + "createdAt": 1431674285000, + "text": "
var roundUpto = function(number, upto){\n    return Number(number.toFixed(upto));\n}\nroundUpto(0.1464676, 2);\n
\n

toFixed(2): Here 2 is the number of digits up to which we want to round this number.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b2e", + "creator": "Ritesh Dhuri", + "createdAt": 1431674360000, + "text": "this .toFixed() is more simple to implement. just go through it once.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90606", + "creator": "loretoparisi", + "createdAt": 1460845571000, + "text": "

To round at decimal positions pos (including no decimals) do Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos)

\n\n

\r\n
\r\n
var console = {\r\n log: function(s) {\r\n  document.getElementById(\"console\").innerHTML += s + \"<br/>\"\r\n }\r\n}\r\nvar roundDecimals=function(num,pos) {\r\n return (Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos) );\r\n}\r\n//https://en.wikipedia.org/wiki/Pi\r\nvar pi=3.14159265358979323846264338327950288419716939937510;\r\nfor(var i=2;i<15;i++) console.log(\"pi=\"+roundDecimals(pi,i));\r\nfor(var i=15;i>=0;--i) console.log(\"pi=\"+roundDecimals(pi,i));
\r\n
<div id=\"console\" />
\r\n
\r\n
\r\n

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b30", + "creator": "Marco Marsala", + "createdAt": 1568813982000, + "text": "Wrong. Try 1.015", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90608", + "creator": "Marcos Lima", + "createdAt": 1467725658000, + "text": "

Another approach to this:

\n
number = 16.6666666;\nconsole.log(parseFloat(number.toFixed(2)));\n"16.67"\n\nnumber = 16.6;\nconsole.log(parseFloat(number.toFixed(2)));\n"16.6"\n\nnumber = 16;\nconsole.log(parseFloat(number.toFixed(2)));\n"16"\n
\n

.toFixed(2) returns a string with exactly two decimal points, that may or may not be trailing zeros. Doing a parseFloat() will eliminate those trailing zeros.

\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90607", + "creator": "Nimesh", + "createdAt": 1461058397000, + "text": "

Please use the below function if you don't want to round off.

\n\n
function ConvertToDecimal(num) {\n  num = num.toString(); // If it's not already a String\n  num = num.slice(0, (num.indexOf(\".\")) + 3); // With 3 exposing the hundredths place    \nalert('M : ' + Number(num)); // If you need it back as a Number     \n}\n
\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b33", + "creator": "Joshua Pinter", + "createdAt": 1466634986000, + "text": "You should have the desired decimal places be a second parameter of the function that "defaults" to 2 or 3 decimal places.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90609", + "creator": "Soldeplata Saketos", + "createdAt": 1480063058000, + "text": "

This is the simplest, more elegant solution (and I am the best of the world;):

\n
function roundToX(num, X) {    \n    return +(Math.round(num + "e+"+X)  + "e-"+X);\n}\n//roundToX(66.66666666,2) => 66.67\n//roundToX(10,2) => 10\n//roundToX(10.904,2) => 10.9\n
\n

Modern syntax alternative with fallback values

\n
const roundToX = (num = 0, X = 20) => +(Math.round(num + `e${X}`)  + `e-${X}`)\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b36", + "creator": "AxelH", + "createdAt": 1480063682000, + "text": "That's a nice way to rewrite the accepted answer to accept an argument using E notation.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b38", + "creator": "Lonnie Best", + "createdAt": 1548140318000, + "text": "To be even more concise "e+" can just be "e" instead.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9060b", + "creator": "Martin", + "createdAt": 1486574011000, + "text": "

Just for the record, the scaling method could theoretically return Infinity if the number and the digits you want to round to are big enough. In JavaScript that shouldn't be a problem since the maximum number is 1.7976931348623157e+308, but if you're working with really big numbers or a lot of decimal places you could try this function instead:

\n\n

\r\n
\r\n
Number.prototype.roundTo = function(digits)\r\n{\r\n    var str = this.toString();\r\n    var split = this.toString().split('e');\r\n    var scientific = split.length > 1;\r\n    var index;\r\n    if (scientific)\r\n    {\r\n        str = split[0];\r\n        var decimal = str.split('.');\r\n        if (decimal.length < 2)\r\n            return this;\r\n        index = decimal[0].length + 1 + digits;\r\n    }\r\n    else\r\n        index = Math.floor(this).toString().length + 1 + digits;\r\n    if (str.length <= index)\r\n        return this;\r\n    var digit = str[index + 1];\r\n    var num = Number.parseFloat(str.substring(0, index));\r\n    if (digit >= 5)\r\n    {\r\n        var extra = Math.pow(10, -digits);\r\n        return this < 0 ? num - extra : num + extra;\r\n    }\r\n    if (scientific)\r\n        num += \"e\" + split[1];\r\n    return num;\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b3a", + "creator": "Brendan.H", + "createdAt": 1656312964000, + "text": "Didn't work for 1.555, -2.9e-7, 224.9849, 10.075, 519.805", + "upvotes": 2663, + "upvoterUsernames": [], + "downvotes": 2663, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9060c", + "creator": "RBZ", + "createdAt": 1496245428000, + "text": "

A generic answer for all browsers and precisions:

\n
function round(num, places) {\n    if(!places) {\n        return Math.round(num);\n    }\n\n    var val = Math.pow(10, places);\n    return Math.round(num * val) / val;\n}\n\nround(num, 2);\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9060a", + "creator": "cronvel", + "createdAt": 1484734770000, + "text": "

You should use:

\n\n
Math.round( num * 100 + Number.EPSILON ) / 100\n
\n\n

No one seems to be aware of Number.EPSILON.

\n\n

Also it's worth noting that this is not a JavaScript weirdness like some people stated.

\n\n

That is simply the way floating point numbers works in a computer. Like 99% of programming languages, JavaScript doesn't have home made floating point numbers; it relies on the CPU/FPU for that. A computer uses binary, and in binary, there isn't any numbers like 0.1, but a mere binary approximation for that. Why? For the same reason than 1/3 cannot be written in decimal: its value is 0.33333333... with an infinity of threes.

\n\n

Here come Number.EPSILON. That number is the difference between 1 and the next number existing in the double precision floating point numbers. That's it: There is no number between 1 and 1 + Number.EPSILON.

\n\n

EDIT:

\n\n

As asked in the comments, let's clarify one thing: adding Number.EPSILON is relevant only when the value to round is the result of an arithmetic operation, as it can swallow some floating point error delta.

\n\n

It's not useful when the value comes from a direct source (e.g.: literal, user input or sensor).

\n\n

EDIT (2019):

\n\n

Like @maganap and some peoples have pointed out, it's best to add Number.EPSILON before multiplying:

\n\n
Math.round( ( num + Number.EPSILON ) * 100 ) / 100\n
\n\n

EDIT (december 2019):

\n\n

Lately, I use a function similar to this one for comparing numbers epsilon-aware:

\n\n
const ESPILON_RATE = 1 + Number.EPSILON ;\nconst ESPILON_ZERO = Number.MIN_VALUE ;\n\nfunction epsilonEquals( a , b ) {\n  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {\n    return false ;\n  }\n  if ( a === 0 || b === 0 ) {\n    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;\n  }\n  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;\n}\n
\n\n

My use-case is an assertion + data validation lib I'm developing for many years.

\n\n

In fact, in the code I'm using ESPILON_RATE = 1 + 4 * Number.EPSILON and EPSILON_ZERO = 4 * Number.MIN_VALUE (four times the epsilon), because I want an equality checker loose enough for cumulating floating point error.

\n\n

So far, it looks perfect for me.\nI hope it will help.

\n", + "upvotes": 319, + "upvoterUsernames": [], + "downvotes": 140, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32505082fcc3049e91b3d", + "creator": "AliN11", + "createdAt": 1612115713000, + "text": "Should I use 1000 instead of 100 if I want to round to 3 decimal numbers?", + "upvotes": 3219, + "upvoterUsernames": [], + "downvotes": 3219, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b3f", + "creator": "Satish Patro", + "createdAt": 1612512321000, + "text": "Math.round((224.98499999 * 100 + Number.EPSILON)) / 100 224.98 Instead of 224.99", + "upvotes": 109, + "upvoterUsernames": [], + "downvotes": 109, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b40", + "creator": "Random Elephant", + "createdAt": 1620728355000, + "text": "@PSatishPatro That is correct. .849 is closer to .8 than it is to .9, thus, it's rounded down to .8.", + "upvotes": 80, + "upvoterUsernames": [], + "downvotes": 80, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b42", + "creator": "Satish Patro", + "createdAt": 1620990323000, + "text": "you are right, scale 2 with rounding half up, gave 224.98, but when number is 224.985, it gave 224.99. I was doing it wrong it seems. Thank you", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b43", + "creator": "Andre Figueiredo", + "createdAt": 1638839891000, + "text": "I don't any comment about putting EPSILON before multiplication. Why is that? It fails for 1.3549999999999998.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32505082fcc3049e91b45", + "creator": "Nando", + "createdAt": 1652886015000, + "text": "10.075 input returns 10.07 output. No good.", + "upvotes": 405, + "upvoterUsernames": [], + "downvotes": 405, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9060e", + "creator": "Suganth G", + "createdAt": 1505592222000, + "text": "

I tried my very own code. Try this:

\n
function AmountDispalyFormat(value) {\n    value = value.toFixed(3);\n    var amount = value.toString().split('.');\n    var result = 0;\n    if (amount.length > 1) {\n        var secondValue = parseInt(amount[1].toString().slice(0, 2));\n        if (amount[1].toString().length > 2) {\n            if (parseInt(amount[1].toString().slice(2, 3)) > 4) {\n                secondValue++;\n                if (secondValue == 100) {\n                    amount[0] = parseInt(amount[0]) + 1;\n                    secondValue = 0;\n                }\n            }\n        }\n\n        if (secondValue.toString().length == 1) {\n            secondValue = "0" + secondValue;\n        }\n        result = parseFloat(amount[0] + "." + secondValue);\n    } else {\n        result = parseFloat(amount);\n    }\n    return result;\n}\n
\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32506082fcc3049e91b47", + "creator": "Peter Mortensen", + "createdAt": 1651769716000, + "text": "Does it work for 1.015?", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9060d", + "creator": "Madura Pradeep", + "createdAt": 1501225159000, + "text": "

If you are using the Lodash library, you can use the round method of Lodash like following.

\n
_.round(number, precision)\n
\n

For example:

\n
_.round(1.7777777, 2) = 1.78\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9060f", + "creator": "Adam A.", + "createdAt": 1508522376000, + "text": "

A simpler ES6 way is

\n\n
const round = (x, n) => \n  Number(parseFloat(Math.round(x * Math.pow(10, n)) / Math.pow(10, n)).toFixed(n));\n
\n\n

This pattern also returns the precision asked for.

\n\n

ex:

\n\n
round(44.7826456, 4)  // yields 44.7826\nround(78.12, 4)       // yields 78.12\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32506082fcc3049e91b4a", + "creator": "Adam A.", + "createdAt": 1588125976000, + "text": "@MarcinWanago good catch, I overlooked that in the OP's question. I've updated my example. Thank you.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90610", + "creator": "pery mimon", + "createdAt": 1509552625000, + "text": "

2017
\nJust use native code .toFixed()

\n\n
number = 1.2345;\nnumber.toFixed(2) // \"1.23\"\n
\n\n

If you need to be strict and add digits just if needed it can use replace

\n\n
number = 1; // \"1\"\nnumber.toFixed(5).replace(/\\.?0*$/g,'');\n
\n", + "upvotes": 61, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32506082fcc3049e91b4c", + "creator": "Zambonilli", + "createdAt": 1510085064000, + "text": "The toFixed method returns a string. If you want a number result you'll need to send the result of toFixed to parseFloat.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b4e", + "creator": "pery mimon", + "createdAt": 1513271128000, + "text": "Ok got it. I add some solution also for that case", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b50", + "creator": "pery mimon", + "createdAt": 1533207140000, + "text": "But 1.005 should be rounded to 1.01. that is the rule of roundes", + "upvotes": 2479, + "upvoterUsernames": [], + "downvotes": 2479, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90612", + "creator": "Elid Garazlic", + "createdAt": 1513693385000, + "text": "
number=(parseInt((number +0.005)*100))/100;     \n
\n\n

add 0.005 if you want to normal round (2 decimals)

\n\n
8.123 +0.005=> 8.128*100=>812/100=>8.12   \n\n8.126 +0.005=> 8.131*100=>813/100=>8.13   \n
\n", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90611", + "creator": "Nicolo", + "createdAt": 1512715201000, + "text": "

I know there are many answers, but most of them have side effect in some specific cases.

\n\n

Easiest and shortest solution without any side effects is following:

\n\n
Number((2.3456789).toFixed(2)) // 2.35\n
\n\n

It rounds properly and returns number instead of string

\n\n
console.log(Number((2.345).toFixed(2)))  // 2.35\nconsole.log(Number((2.344).toFixed(2)))  // 2.34\nconsole.log(Number((2).toFixed(2)))      // 2\nconsole.log(Number((-2).toFixed(2)))     // -2\nconsole.log(Number((-2.345).toFixed(2))) // -2.35\n\nconsole.log(Number((2.345678).toFixed(3))) // 2.346\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32506082fcc3049e91b52", + "creator": "Eagle", + "createdAt": 1523390743000, + "text": "console.log(Number((1.005).toFixed(2))) still gives "1.00" instead of "1.01"", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90614", + "creator": "Marco Barbero", + "createdAt": 1518508103000, + "text": "

Starting from the example proposed over the precisionRound that I found on MDN (that event for 1.005 returns 1 and not 1.01), I write a custom precisionRound that manage a random precision number and for 1.005 returns 1.01.

\n

This is the function:

\n
function precisionRound(number, precision)\n{\n  if(precision < 0)\n  {\n    var factor = Math.pow(10, precision);\n    return Math.round(number * factor) / factor;\n  }\n  else\n    return +(Math.round(number + "e+"+precision)  + "e-"+precision);\n}\n\nconsole.log(precisionRound(1234.5678, 1));  // output: 1234.6\nconsole.log(precisionRound(1234.5678, -1)); // output: 1230\nconsole.log(precisionRound(1.005, 2));      // output: 1.01\nconsole.log(precisionRound(1.0005, 2));     // output: 1\nconsole.log(precisionRound(1.0005, 3));     // output: 1.001\nconsole.log(precisionRound(1.0005, 4));     // output: 1.0005\n
\n

For TypeScript:

\n
public static precisionRound(number: number, precision: number)\n{\n  if (precision < 0)\n  {\n    let factor = Math.pow(10, precision);\n    return Math.round(number * factor) / factor;\n  }\n  else\n    return +(Math.round(Number(number + "e+" + precision)) +\n      "e-" + precision);\n}\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90613", + "creator": "Matas Vaitkevicius", + "createdAt": 1517891640000, + "text": "

Since ES6 there is a 'proper' way (without overriding statics and creating workarounds) to do this by using toPrecision

\n\n

\r\n
\r\n
var x = 1.49999999999;\r\nconsole.log(x.toPrecision(4));\r\nconsole.log(x.toPrecision(3));\r\nconsole.log(x.toPrecision(2));\r\n\r\nvar y = Math.PI;\r\nconsole.log(y.toPrecision(6));\r\nconsole.log(y.toPrecision(5));\r\nconsole.log(y.toPrecision(4));\r\n\r\nvar z = 222.987654\r\nconsole.log(z.toPrecision(6));\r\nconsole.log(z.toPrecision(5));\r\nconsole.log(z.toPrecision(4));
\r\n
\r\n
\r\n

\n\n

then you can just parseFloat and zeroes will 'go away'.

\n\n

\r\n
\r\n
console.log(parseFloat((1.4999).toPrecision(3)));\r\nconsole.log(parseFloat((1.005).toPrecision(3)));\r\nconsole.log(parseFloat((1.0051).toPrecision(3)));
\r\n
\r\n
\r\n

\n\n

It doesn't solve the '1.005 rounding problem' though - since it is intrinsic to how float fractions are being processed.

\n\n

\r\n
\r\n
console.log(1.005 - 0.005);
\r\n
\r\n
\r\n

\n\n

If you are open to libraries you can use bignumber.js

\n\n

\r\n
\r\n
console.log(1.005 - 0.005);\r\nconsole.log(new BigNumber(1.005).minus(0.005));\r\n\r\nconsole.log(new BigNumber(1.005).round(4));\r\nconsole.log(new BigNumber(1.005).round(3));\r\nconsole.log(new BigNumber(1.005).round(2));\r\nconsole.log(new BigNumber(1.005).round(1));
\r\n
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 50, + "upvoterUsernames": [], + "downvotes": 23, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32506082fcc3049e91b56", + "creator": "Giacomo", + "createdAt": 1519424467000, + "text": "(1.005).toPrecision(3) still returns 1.00 instead of 1.01 actually.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b58", + "creator": "adamduren", + "createdAt": 1542409189000, + "text": "toPrecision returns a string which changes the desired output type.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b5a", + "creator": "Eugene Mala", + "createdAt": 1559939971000, + "text": "(1).toPrecision(3) returns '1.00', but questioner wanted to have 1 in this case.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b5b", + "creator": "Zach Saucier", + "createdAt": 1561742354000, + "text": "This also likely doesn't do what most people want with larger numbers without decimals - say 144. It will be rounded to 140.", + "upvotes": 2702, + "upvoterUsernames": [], + "downvotes": 2702, + "downvoterUsernames": [] + }, + { + "_id": "62f32506082fcc3049e91b5d", + "creator": "shinzou", + "createdAt": 1580461083000, + "text": "Returns scientific notations, useless.", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 62, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90615", + "creator": "Simon Prickett", + "createdAt": 1518554690000, + "text": "

In the Node.js environment I just use the roundTo module:

\n
const roundTo = require('round-to');\n...\nroundTo(123.4567, 2);\n\n// 123.46\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90616", + "creator": "Arulraj", + "createdAt": 1522052816000, + "text": "

Use something like this \n\"parseFloat(parseFloat(value).toFixed(2))\"

\n\n
parseFloat(parseFloat(\"1.7777777\").toFixed(2))-->1.78 \nparseFloat(parseFloat(\"10\").toFixed(2))-->10 \nparseFloat(parseFloat(\"9.1\").toFixed(2))-->9.1\n
\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90618", + "creator": "OZZIE", + "createdAt": 1527056510000, + "text": "

From the existing answers I found another solution which seems to work great, which also works with sending in a string and eliminates trailing zeros.

\n\n
function roundToDecimal(string, decimals) {\n    return parseFloat(parseFloat(string).toFixed(decimals));\n}\n
\n\n

It doesn't take in to account if you send in some bull.. like \"apa\" though. Or it will probably throw an error which I think is the proper way anyway, it's never good to hide errors that should be fixed (by the calling function).

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90617", + "creator": "Vikasdeep Singh", + "createdAt": 1524810752000, + "text": "

For me Math.round() was not giving correct answer. I found toFixed(2) works better. \nBelow are examples of both:

\n\n

\r\n
\r\n
console.log(Math.round(43000 / 80000) * 100); // wrong answer\r\n\r\nconsole.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
\r\n
\r\n
\r\n

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90619", + "creator": "Ross", + "createdAt": 1529366619000, + "text": "

This did the trick for me (TypeScript):

\n
round(decimal: number, decimalPoints: number): number{\n    let roundedValue = Math.round(decimal * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints);\n\n    console.log(`Rounded ${decimal} to ${roundedValue}`);\n    return roundedValue;\n}\n
\n

Sample output

\n
Rounded 18.339840000000436 to 18.34\nRounded 52.48283999999984 to 52.48\nRounded 57.24612000000036 to 57.25\nRounded 23.068320000000142 to 23.07\nRounded 7.792980000000398 to 7.79\nRounded 31.54157999999981 to 31.54\nRounded 36.79686000000004 to 36.8\nRounded 34.723080000000124 to 34.72\nRounded 8.4375 to 8.44\nRounded 15.666960000000074 to 15.67\nRounded 29.531279999999924 to 29.53\nRounded 8.277420000000006 to 8.28\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9061a", + "creator": "Denis S Dujota", + "createdAt": 1531772769000, + "text": "

I was building a simple tipCalculator and there was a lot of answers here that seemed to overcomplicate the issue. So I found summarizing the issue to be the best way to truly answer this question.

\n

If you want to create a rounded decimal number, first you call toFixed(# of decimal places you want to keep) and then wrap that in a Number().

\n

So the end result:

\n
let amountDue = 286.44;\ntip = Number((amountDue * 0.2).toFixed(2));\nconsole.log(tip)  // 57.29 instead of 57.288\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9061b", + "creator": "Pavithran Ravichandiran", + "createdAt": 1531897164000, + "text": "

A simple solution would be use Lodash's ceil function if you want to round up...

\n
_.round(6.001, 2)\n
\n

gives 6

\n
_.ceil(6.001, 2);\n
\n

gives 6.01

\n
_.ceil(37.4929, 2);\n
\n

gives 37.5

\n
_.round(37.4929, 2);\n
\n

gives 37.49

\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32507082fcc3049e91b65", + "creator": "Dmytro Medvid", + "createdAt": 1533211453000, + "text": "There is no sense in importing new dependency, just to make rounding.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32507082fcc3049e91b67", + "creator": "Pavithran Ravichandiran", + "createdAt": 1533216834000, + "text": "It is very funny that you don't use lodash already in your javascript project considering the kind of features lodash provides...", + "upvotes": 312, + "upvoterUsernames": [], + "downvotes": 312, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9061c", + "creator": "Dmytro Medvid", + "createdAt": 1533211592000, + "text": "

The rounding problem can be avoided by using numbers represented in exponential notation.

\n
public roundFinancial(amount: number, decimals: number) {\n    return Number(Math.round(Number(`${amount}e${decimals}`)) + `e-${decimals}`);\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32507082fcc3049e91b69", + "creator": "hanshenrik", + "createdAt": 1539705815000, + "text": "this doesn't look like javascript to me, and chrome doesn't accept it - VM82:1 Uncaught SyntaxError: Unexpected identifier", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9061d", + "creator": "aleha", + "createdAt": 1534845529000, + "text": "

This answer is more about speed.

\n\n
var precalculatedPrecisions = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10];\n\nfunction round(num, _prec) {\n    _precision = precalculatedPrecisions[_prec]\n    return Math.round(num * _precision + 1e-14) / _precision ;\n}\n
\n\n

jsPerf about this.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9061e", + "creator": "thecoolestname36", + "createdAt": 1536255303000, + "text": "

This worked pretty well for me when wanting to always round up to a certain decimal. The key here is that we will always be rounding up with the Math.ceil function.

\n\n

You could conditionally select ceil or floor if needed.

\n\n

\r\n
\r\n
     /**\r\n     * Possibility to lose precision at large numbers\r\n     * @param number\r\n     * @returns Number number\r\n     */\r\n    var roundUpToNearestHundredth = function(number) {\r\n\r\n        // Ensure that we use high precision Number\r\n        number = Number(number);\r\n\r\n        // Save the original number so when we extract the Hundredth decimal place we don't bit switch or lose precision\r\n        var numberSave = Number(number.toFixed(0));\r\n\r\n        // Remove the \"integer\" values off the top of the number\r\n        number = number - numberSave;\r\n\r\n        // Get the Hundredth decimal places\r\n        number *= 100;\r\n\r\n        // Ceil the decimals.  Therefore .15000001 will equal .151, etc.\r\n        number = Math.ceil(number);\r\n\r\n        // Put the decimals back into their correct spot\r\n        number /= 100;\r\n\r\n        // Add the \"integer\" back onto the number\r\n        return number + numberSave;\r\n\r\n    };\r\n\r\nconsole.log(roundUpToNearestHundredth(6132423.1200000000001))
\r\n
\r\n
\r\n

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9061f", + "creator": "Fellow Stranger", + "createdAt": 1540402996000, + "text": "

Based on the chosen answer and the upvoted comment on the same question:

\n
Math.round((num + 0.00001) * 100) / 100\n
\n

This works for both these examples:

\n
Math.round((1.005 + 0.00001) * 100) / 100\n\nMath.round((1.0049 + 0.00001) * 100) / 100\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32507082fcc3049e91b6d", + "creator": "Wiimm", + "createdAt": 1550826636000, + "text": "This function fails for 1.004999", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90620", + "creator": "Wiimm", + "createdAt": 1548495968000, + "text": "
\n

parseFloat(\"1.555\").toFixed(2); // Returns 1.55 instead of 1.56.

\n
\n\n

1.55 is the absolute correct result, because there exists no exact representation of 1.555 in the computer. If reading 1.555 it is rounded to the nearest possible value = 1.55499999999999994 (64 bit float). And rounding this number by toFixed(2) results in 1.55.

\n\n

All other functions provided here give fault result, if the input is 1.55499999999999.

\n\n

Solution: Append the digit \"5\" before scanning to rounding up (more exact: rounding away from 0) the number. Do this only, if the number is really a float (has a decimal point).

\n\n
parseFloat(\"1.555\"+\"5\").toFixed(2); // Returns 1.56\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90621", + "creator": "Carlos Lombardi", + "createdAt": 1551293942000, + "text": "

A different approach is to use a library. Use Lodash:

\n
const _ = require("lodash")\nconst roundedNumber = _.round(originalNumber, 2)\n
\n", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32508082fcc3049e91b70", + "creator": "Peter Mortensen", + "createdAt": 1651771318000, + "text": "At least five previous answers have used Lodash.", + "upvotes": 318, + "upvoterUsernames": [], + "downvotes": 318, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90622", + "creator": "malamut", + "createdAt": 1552769609000, + "text": "

The big challenge on this seemingly simple task is that we want it to yield psychologically expected results even if the input contains minimal rounding errors to start with (not mentioning the errors which will happen within our calculation). If we know that the real result is exactly 1.005, we expect that rounding to two digits yields 1.01, even if the 1.005 is the result of a large computation with loads of rounding errors on the way.

\n\n

The problem becomes even more obvious when dealing with floor() instead of round(). For example, when cutting everything away after the last two digits behind the dot of 33.3, we would certainly not expect to get 33.29 as a result, but that is what happens:

\n\n

\r\n
\r\n
console.log(Math.floor(33.3 * 100) / 100)
\r\n
\r\n
\r\n

\n\n

In simple cases, the solution is to perform calculation on strings instead of floating point numbers, and thus avoid rounding errors completely. However, this option fails at the first non-trivial mathematical operation (including most divsions), and it is slow.

\n\n

When operating on floating point numbers, the solution is to introduce a parameter which names the amount by which we are willing to deviate from the actual computation result, in order to output the psychologically expected result.

\n\n

\r\n
\r\n
var round = function(num, digits = 2, compensateErrors = 2) {\r\n  if (num < 0) {\r\n    return -this.round(-num, digits, compensateErrors);\r\n  }\r\n  const pow = Math.pow(10, digits);\r\n  return (Math.round(num * pow * (1 + compensateErrors * Number.EPSILON)) / pow);\r\n}\r\n\r\n/* --- testing --- */\r\n\r\nconsole.log(\"Edge cases mentioned in this thread:\")\r\nvar values = [ 0.015, 1.005, 5.555, 156893.145, 362.42499999999995, 1.275, 1.27499, 1.2345678e+2, 2.175, 5.015, 58.9 * 0.15 ];\r\nvalues.forEach((n) => {\r\n  console.log(n + \" -> \" + round(n));\r\n  console.log(-n + \" -> \" + round(-n));\r\n});\r\n\r\nconsole.log(\"\\nFor numbers which are so large that rounding cannot be performed anyway within computation precision, only string-based computation can help.\")\r\nconsole.log(\"Standard: \" + round(1e+19));\r\nconsole.log(\"Compensation = 1: \" + round(1e+19, 2, 1));\r\nconsole.log(\"Effectively no compensation: \" + round(1e+19, 2, 0.4));
\r\n
\r\n
\r\n

\n\n

Note: Internet Explorer does not know Number.EPSILON. If you are in the unhappy position of still having to support it, you can use a shim, or just define the constant yourself for that specific browser family.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90624", + "creator": "caliche2000", + "createdAt": 1556131850000, + "text": "

Here's my solution to this problem:

\n\n
function roundNumber(number, precision = 0) {\nvar num = number.toString().replace(\",\", \"\");\nvar integer, decimal, significantDigit;\n\nif (num.indexOf(\".\") > 0 && num.substring(num.indexOf(\".\") + 1).length > precision && precision > 0) {\n    integer = parseInt(num).toString();\n    decimal = num.substring(num.indexOf(\".\") + 1);\n    significantDigit = Number(decimal.substr(precision, 1));\n\n    if (significantDigit >= 5) {\n        decimal = (Number(decimal.substr(0, precision)) + 1).toString();\n        return integer + \".\" + decimal;\n    } else {\n        decimal = (Number(decimal.substr(0, precision)) + 1).toString();\n        return integer + \".\" + decimal;\n    }\n}\nelse if (num.indexOf(\".\") > 0) {\n    integer = parseInt(num).toString();\n    decimal = num.substring(num.indexOf(\".\") + 1);\n    significantDigit = num.substring(num.length - 1, 1);\n\n    if (significantDigit >= 5) {\n        decimal = (Number(decimal) + 1).toString();\n        return integer + \".\" + decimal;\n    } else {            \n        return integer + \".\" + decimal;\n    }\n} \n\nreturn number;\n}\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32508082fcc3049e91b73", + "creator": "Brendan.H", + "createdAt": 1656312536000, + "text": "Doesn't work for -2.9e-7 or 224.9849", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [] + }, + { + "_id": "62f32508082fcc3049e91b74", + "creator": "General Grievance", + "createdAt": 1658345642000, + "text": "There's way too much redundant code here.", + "upvotes": 655, + "upvoterUsernames": [], + "downvotes": 655, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90623", + "creator": "KFish", + "createdAt": 1554397969000, + "text": "

See @AmrAli's answer for a more thorough run through and performance breakdown of all the various adaptations of this solution.

\n

\r\n
\r\n
var DecimalPrecision = (function(){\n        if (Number.EPSILON === undefined) {\n            Number.EPSILON = Math.pow(2, -52);\n        }\n        if(Number.isInteger === undefined){\n            Number.isInteger = function(value) {\n                return typeof value === 'number' && \n                isFinite(value) && \n                Math.floor(value) === value;\n            };\n        }\n        this.isRound = function(n,p){\n            let l = n.toString().split('.')[1].length;\n            return (p >= l);\n        }\n        this.round = function(n, p=2){\n            if(Number.isInteger(n) || this.isRound(n,p))\n                return n;\n            let r = 0.5 * Number.EPSILON * n;\n            let o = 1; while(p-- > 0) o *= 10;\n            if(n<0)\n                o *= -1;\n            return Math.round((n + r) * o) / o;\n        }\n        this.ceil = function(n, p=2){\n            if(Number.isInteger(n) || this.isRound(n,p))\n                return n;\n            let r = 0.5 * Number.EPSILON * n;\n            let o = 1; while(p-- > 0) o *= 10;\n            \n            return Math.ceil((n + r) * o) / o;\n        }\n        this.floor = function(n, p=2){\n            if(Number.isInteger(n) || this.isRound(n,p))\n                return n;\n            let r = 0.5 * Number.EPSILON * n;\n            let o = 1; while(p-- > 0) o *= 10;\n            \n            return Math.floor((n + r) * o) / o;\n        }\n        return this;\n    })();\n    console.log(DecimalPrecision.round(1.005));\n    console.log(DecimalPrecision.ceil(1.005));\n    console.log(DecimalPrecision.floor(1.005));\n    console.log(DecimalPrecision.round(1.0049999));\n    console.log(DecimalPrecision.ceil(1.0049999));\n    console.log(DecimalPrecision.floor(1.0049999));\n    console.log(DecimalPrecision.round(2.175495134384,7));\n    console.log(DecimalPrecision.round(2.1753543549,8));\n    console.log(DecimalPrecision.round(2.1755465135353,4));\n    console.log(DecimalPrecision.ceil(17,4));\n    console.log(DecimalPrecision.ceil(17.1,4));\n    console.log(DecimalPrecision.ceil(17.1,15));
\r\n
\r\n
\r\n

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32508082fcc3049e91b77", + "creator": "Sergii", + "createdAt": 1589294548000, + "text": "(DecimalPrecision.round(0.014999999999999999, 2)) // returns 0.02", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32508082fcc3049e91b79", + "creator": "Amr Ali", + "createdAt": 1596322511000, + "text": "Update the post with edge numbers: [1.005, 1.275, 2.675, 16.235]", + "upvotes": 1124, + "upvoterUsernames": [], + "downvotes": 1124, + "downvoterUsernames": [] + }, + { + "_id": "62f32508082fcc3049e91b7b", + "creator": "Amr Ali", + "createdAt": 1597694195000, + "text": "@KFish DecimalPrecision.ceil(17,0); // 18 and DecimalPrecision.ceil(17,1); // 17.1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32508082fcc3049e91b7c", + "creator": "Amr Ali", + "createdAt": 1597959869000, + "text": "@KFish DecimalPrecision.ceil(-5.12, 1); // -5.2 and DecimalPrecision.floor(-5.12, 1); // -5.1", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32508082fcc3049e91b7d", + "creator": "Amr Ali", + "createdAt": 1598129775000, + "text": "One more issue: DecimalPrecision.ceil(0.0000001, 2) gives me Error: Cannot read property 'length' of undefined", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90626", + "creator": "profimedica", + "createdAt": 1559906519000, + "text": "

Keep type as integer for later sorting or other math operations:

\n\n
Math.round(1.7777777 * 100)/100\n
\n\n
\n

1.78

\n
\n\n
// Round up!\nMath.ceil(1.7777777 * 100)/100 \n
\n\n
\n

1.78

\n
\n\n
// Round down!\nMath.floor(1.7777777 * 100)/100\n
\n\n
\n

1.77

\n
\n\n

Or convert to string:

\n\n
(1.7777777).toFixed(2)\n
\n\n
\n

\"1.77\"

\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90625", + "creator": "Rob Evans", + "createdAt": 1558646854000, + "text": "

I have found this works for all my use cases:

\n
const round = (value, decimalPlaces = 0) => {\n    const multiplier = Math.pow(10, decimalPlaces);\n    return Math.round(value * multiplier + Number.EPSILON) / multiplier;\n};\n
\n

Keep in mind that is ES6. An ES5 equivalent would be very easy to code though, so I'm not is going to add it.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32508082fcc3049e91b80", + "creator": "Rob Evans", + "createdAt": 1594300511000, + "text": "@RomanVottner Thanks Roman, that does indeed indicate a flaw.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90627", + "creator": "Salman", + "createdAt": 1563871127000, + "text": "

The question is to round to two decimals.

\n

Let’s not make this complicated, modifying prototype chain, etc.

\n

Here is one-line solution

\n

\r\n
\r\n
let round2dec = num => Math.round(num * 100) / 100;\n\nconsole.log(round2dec(1.77));\nconsole.log(round2dec(1.774));\nconsole.log(round2dec(1.777));\nconsole.log(round2dec(10));
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90628", + "creator": "chriscrossweb", + "createdAt": 1564127010000, + "text": "

I created this function, for rounding a number. The value can be a string (ex. '1.005') or a number 1.005 that will be 1 by default and if you specify the decimal to be 2, the result will be 1.01

\n\n
round(value: string | number, decimals: number | string = \"0\"): number | null {\n    return +( Math.round(Number(value + \"e+\"+decimals)) + \"e-\" + decimals);\n}\n
\n\n

Usage: round(1.005, 2) // 1.01\nor \nUsage: round('1.005', 2) //1.01

\n", + "upvotes": 7572, + "upvoterUsernames": [], + "downvotes": 7572, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32508082fcc3049e91b83", + "creator": "Adam Jagosz", + "createdAt": 1566989166000, + "text": "is this a TypeScript question?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9062a", + "creator": "Ajsti.pl - Maciej Szewczyk", + "createdAt": 1566389887000, + "text": "

A helper function where rounging is your default rounding:

\n
let rounding = 4;\n\nlet round = (number) => { let multiply = Math.pow(10,rounding);  return Math.round(number*multiply)/multiply};\n\nconsole.log(round(0.040579431));\n
\n

=> 0.0406

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90629", + "creator": "Ibraheem", + "createdAt": 1564447506000, + "text": "

A slight variation on this is if you need to format a currency amount as either being a whole amount of currency or an amount with fractional currency parts.

\n\n

For example:

\n\n

1 should output $1

\n\n

1.1 should output $1.10

\n\n

1.01 should output $1.01

\n\n

Assuming amount is a number:

\n\n

const formatAmount = (amount) => amount % 1 === 0 ? amount : amount.toFixed(2);

\n\n

If amount is not a number then use parseFloat(amount) to convert it to a number.

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9062c", + "creator": "Crashalot", + "createdAt": 1576113865000, + "text": "

A slight modification of this answer that seems to work well.

\n

Function

\n
function roundToStep(value, stepParam) {\n   var step = stepParam || 1.0;\n   var inv = 1.0 / step;\n   return Math.round(value * inv) / inv;\n}\n
\n

Usage

\n
roundToStep(2.55) = 3\nroundToStep(2.55, 0.1) = 2.6\nroundToStep(2.55, 0.01) = 2.55\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9062b", + "creator": "Kiran Maniya", + "createdAt": 1569348255000, + "text": "

There is a solution working for all numbers. Give it a try. The expression is given below.

\n
Math.round((num + 0.00001) * 100) / 100. Try Math.round((1.005 + 0.00001) * 100) / 100 and Math.round((1.0049 + 0.00001) * 100) / 100\n
\n

I recently tested every possible solution and finally arrived at the output after trying almost 10 times.

\n

Here is a screenshot of issue arose during calculations,

\n

\"Screen.

\n

Head over to the amount field. It's returning almost infinite. I gave it a try for the toFixed() method, but it's not working for some cases (i.e., try with pi) and finally derived a solution given above.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9062e", + "creator": "ramya", + "createdAt": 1582469495000, + "text": "

As per the answer already given in comments with the link to http://jsfiddle.net/AsRqx/, the following one worked perfectly for me.

\n
function C(num)\n{\n    return +(Math.round(num + "e+2") + "e-2");\n}\n\nfunction N(num, places)\n{\n    return +(Math.round(num + "e+" + places) + "e-" + places);\n}\n\nC(1.005);\n\nN(1.005, 0);\nN(1.005, 1); // Up to 1 decimal places\nN(1.005, 2); // Up to 2 decimal places\nN(1.005, 3); // Up to 3 decimal places\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9062d", + "creator": "Kamil Kiełczewski", + "createdAt": 1578526448000, + "text": "

The mathematical floor and round definitions:

\n

\"Enter

\n

lead us to

\n

\r\n
\r\n
let round= x=> ( x+0.005 - (x+0.005)%0.01 +'' ).replace(/(\\...)(.*)/,'$1');\n\n// for a case like 1.384 we need to use a regexp to get only 2 digits after the dot\n// and cut off machine-error (epsilon)\n\nconsole.log(round(10));\nconsole.log(round(1.7777777));\nconsole.log(round(1.7747777));\nconsole.log(round(1.384));
\r\n
\r\n
\r\n

\n", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90630", + "creator": "Marcin Wanago", + "createdAt": 1584605341000, + "text": "

The easiest approach would be to use toFixed and then strip trailing zeros using the Number function:

\n\n
const number = 15.5;\nNumber(number.toFixed(2)); // 15.5\n
\n\n
const number = 1.7777777;\nNumber(number.toFixed(2)); // 1.78\n
\n", + "upvotes": 41, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32509082fcc3049e91b88", + "creator": "Neeraj", + "createdAt": 1587291815000, + "text": "this does not work for all cases. do extensive tests before posting answers.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b8a", + "creator": "Marcin Wanago", + "createdAt": 1587377158000, + "text": "@baburao Please post a case in which the above solution doesn't work", + "upvotes": 117, + "upvoterUsernames": [], + "downvotes": 117, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b8c", + "creator": "Kevin Jhangiani", + "createdAt": 1587679827000, + "text": "const number = 15; Number(number.toFixed(2)); //15.00 instead of 15", + "upvotes": 1371, + "upvoterUsernames": [], + "downvotes": 1371, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b8e", + "creator": "Marcin Wanago", + "createdAt": 1587727347000, + "text": "@KevinJhangiani const number = 15; Number(number.toFixed(2)); // 15 - I tested it both on newest Chrome and Firefox", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b8f", + "creator": "Kevin Jhangiani", + "createdAt": 1622046600000, + "text": "The commenters are totally right, and I realized the error in my code after posting that!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9062f", + "creator": "Mosè Bottacini", + "createdAt": 1584443042000, + "text": "

The proposed answers, while generally correct, don't consider the precision of the passed in number, which is not expressed as requirement in the original question, but it may be a requirement in case of scientific application where 3 is different from 3.00 (for example) as the number of decimal digits represents the precision of the instrument that have acquired the value or the accuracy of a calculation.

\n

In fact, the proposed answers rounds 3.001 to 3 while by keeping the information about the precision of the number should be 3.00.

\n

Below is a function that takes that in account:

\n

\r\n
\r\n
function roundTo(value, decimal) {\n\n    let absValue = Math.abs(value);\n    let int = Math.floor(absValue).toString().length;\n    let dec = absValue.toString().length - int;\n    dec -= (Number.isInteger(absValue) ? 0 : 1);\n    return value.toPrecision(int + Math.min(dec, decimal));\n\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 87, + "upvoterUsernames": [], + "downvotes": 87, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90632", + "creator": "Mohsen Alyafei", + "createdAt": 1590172178000, + "text": "

A simple general rounding function could be following:

\n\n

Steps are:

\n\n
    \n
  1. Multiply the number by (10 to the power of number of decimal place) using Math.pow(10,places).
  2. \n
  3. Round the result to whole integer using Math.Round.
  4. \n
  5. Divide the result back by (10 to the power of number of decimal place) Math.pow(10,places).
  6. \n
\n\n

Example:

\n\n

number is: 1.2375\nto be rounded to 3 decimal places

\n\n
    \n
  1. 1.2375 * (10^3) ==> 1.2375 * 1000 = 1237.5
  2. \n
  3. Round to integer ==> 1238
  4. \n
  5. Divide 1238 by (10^3) ==> 1238 / 1000 = 1.238
  6. \n
\n\n

(note: 10^3 means Math.pow(10,3)).

\n\n

\r\n
\r\n
 function numberRoundDecimal(v,n) {\r\n return Math.round((v+Number.EPSILON)*Math.pow(10,n))/Math.pow(10,n)}\r\n\r\n\r\n// ------- tests --------\r\nconsole.log(numberRoundDecimal(-0.024641163062896567,3))  // -0.025\r\nconsole.log(numberRoundDecimal(0.9993360575508052,3))     // 0.999\r\nconsole.log(numberRoundDecimal(1.0020739645577939,3))     // 1.002\r\nconsole.log(numberRoundDecimal(0.975,0))                  // 1\r\nconsole.log(numberRoundDecimal(0.975,1))                  // 1\r\nconsole.log(numberRoundDecimal(0.975,2))                  // 0.98\r\nconsole.log(numberRoundDecimal(1.005,2))                  // 1.01
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90633", + "creator": "Alexandre ELIOT", + "createdAt": 1596124174000, + "text": "

I reviewed every answer of this post.\nHere is my take on the matter:

\n

\r\n
\r\n
const nbRounds = 7;\nconst round = (x, n=2) => {\n  const precision = Math.pow(10, n)\n  return Math.round((x+Number.EPSILON) * precision ) / precision;\n}\nlet i = 0;\nwhile( nbRounds > i++ ) {\n  console.log(\"round(1.00083899, \",i,\") > \", round(1.00083899, i))\n  console.log(\"round(1.83999305, \",i,\") > \", round(1.83999305, i))\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90631", + "creator": "jerry", + "createdAt": 1588351150000, + "text": "

This function works for me. You just pass in the number and the places you want to round and it does what it needs to do easily.

\n
round(source, n) {\n  let places = Math.pow(10, n);\n\n  return Math.round(source * places) / places;\n}\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32509082fcc3049e91b93", + "creator": "Alex", + "createdAt": 1640168078000, + "text": "round(4.45, 0) = 4 expected 5 function does not work.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b94", + "creator": "Peter Mortensen", + "createdAt": 1651773378000, + "text": "It may work for you, but does it answer the question?", + "upvotes": 136, + "upvoterUsernames": [], + "downvotes": 136, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90634", + "creator": "superkeci", + "createdAt": 1603467539000, + "text": "

I've read all the answers, the answers of similar questions and the complexity of the most "good" solutions didn't satisfy me. I don't want to put a huge round function set, or a small one but fails on scientific notation. So, I came up with this function. It may help someone in my situation:

\n
function round(num, dec) {\n   const [sv, ev] = num.toString().split('e');\n   return Number(Number(Math.round(parseFloat(sv + 'e' + dec)) + 'e-' + dec) + 'e' + (ev || 0));\n}\n
\n

I didn't run any performance test because I will call this just to update the UI of my application. The function gives the following results for a quick test:

\n
// 1/3563143 = 2.806510993243886e-7\nround(1/3563143, 2)  // returns `2.81e-7`\n\nround(1.31645, 4)    // returns 1.3165\n\nround(-17.3954, 2)   // returns -17.4\n
\n

This is enough for me.

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32509082fcc3049e91b96", + "creator": "Alex", + "createdAt": 1640168381000, + "text": "round(4.45, 0) = 4 expected 5", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b97", + "creator": "Nathan Wailes", + "createdAt": 1644156036000, + "text": "@Alex I think you're double-rounding. 4.45 rounds to 4, not 5.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b99", + "creator": "Peter Mortensen", + "createdAt": 1651773652000, + "text": "Those test cases are the easy ones. What about 1.015 and other problematic numbers mentioned in the other answers and their comments?", + "upvotes": 1488, + "upvoterUsernames": [], + "downvotes": 1488, + "downvoterUsernames": [] + }, + { + "_id": "62f32509082fcc3049e91b9a", + "creator": "superkeci", + "createdAt": 1652973967000, + "text": "@Peter, it rounds well? Did you check it? What's the problem?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90636", + "creator": "danday74", + "createdAt": 1630710863000, + "text": "

A simple generic solution

\n

\r\n
\r\n
const round = (n, dp) => {\n  const h = +('1'.padEnd(dp + 1, '0')) // 10 or 100 or 1000 or etc\n  return Math.round(n * h) / h\n}\n\nconsole.log('round(2.3454, 3)', round(2.3454, 3)) // 2.345\nconsole.log('round(2.3456, 3)', round(2.3456, 3)) // 2.346\nconsole.log('round(2.3456, 2)', round(2.3456, 2)) // 2.35
\r\n
\r\n
\r\n

\n

Or just use Lodash round which has the same signature - for example, _.round(2.3456, 2)

\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90635", + "creator": "Butsaty", + "createdAt": 1629928149000, + "text": "

This works correctly with positive, negative and large numbers:

\n
function Round(value) {\n    const neat = +(Math.abs(value).toPrecision(15));\n    const rounded = Math.round(neat * 100) / 100;\n\n    return rounded * Math.sign(value);\n}\n\n//0.244 -> 0.24\n//0.245 -> 0.25\n//0.246 -> 0.25\n\n//-0.244 -> -0.24\n//-0.245 -> -0.25\n//-0.246 -> -0.25\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90639", + "creator": "Arifur Rahman Khan", + "createdAt": 1637745838000, + "text": "

Another simple solution (without writing any function) may to use toFixed() and then convert to float again:

\n

For example:

\n
var objNumber = 1201203.1256546456;\nobjNumber = parseFloat(objNumber.toFixed(2))\n
\n", + "upvotes": 27, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250a082fcc3049e91b9d", + "creator": "Marek Marczak", + "createdAt": 1639653811000, + "text": "No. It rounds up for values above (0).5 only..", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e90637", + "creator": "shahroz butt", + "createdAt": 1631977260000, + "text": "

Use something like this to round up:

\n
num = 519.805;\ndp = Math.pow(10, 2);\nnum = parseFloat(num.toString().concat("1"));\nrounded = Math.round((num + Number.EPSILON)* dp)/dp;\n
\n

As it would deal with numbers falling short where there is only one decimal place to round at the end.

\n", + "upvotes": 44, + "upvoterUsernames": [], + "downvotes": 44, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e90638", + "creator": "Deejers", + "createdAt": 1637245772000, + "text": "

Here's a modified version of astorije's answer that better supports rounding negative values.

\n
// https://stackoverflow.com/a/21323513/384884\n// Modified answer from astorije\nfunction round(value, precision) {\n    // Ensure precision exists\n    if (typeof precision === "undefined" || +precision === 0) {\n        // Just do a regular Math.round\n        return Math.round(value);\n    }\n\n    // Convert the value and precision variables both to numbers\n    value = +value;\n    precision = +precision;\n\n    // Ensure the value is a number and that precision is usable\n    if (isNaN(value) || !(typeof precision === "number" && precision % 1 === 0)) {\n        // Return NaN\n        return NaN;\n    }\n\n    // Get the sign of value\n    var signValue = Math.sign(value);\n\n    // Get the absolute value of value\n    value = Math.abs(value);\n\n    // Shift\n    value = value.toString().split("e");\n    value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] + precision) : precision)));\n\n    // Shift back\n    value = value.toString().split("e");\n    value = +(value[0] + "e" + (value[1] ? (+value[1] - precision) : -precision));\n\n    // Apply the sign\n    value = value * signValue;\n\n    // Return rounded value\n    return value;\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9063a", + "creator": "Victor dlf", + "createdAt": 1643289748000, + "text": "

2022, native, without library, modern browser, clear and readable.

\n

\r\n
\r\n
function round(\n  value,\n  minimumFractionDigits,\n  maximumFractionDigits\n) {\n  const formattedValue = value.toLocaleString('en', {\n    useGrouping: false,\n    minimumFractionDigits,\n    maximumFractionDigits\n  })\n  return Number(formattedValue)\n}\n\nconsole.log(round(21.891, 2, 3)) // 21.891\nconsole.log(round(1.8, 2)) // 1.8, if you need 1.80, remove the `Number` function. Return directly the `formattedValue`.\nconsole.log(round(21.0001, 0, 1)) // 21\nconsole.log(round(0.875, 3)) // 0.875
\r\n
\r\n
\r\n

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3250a082fcc3049e91ba2", + "creator": "Royi Namir", + "createdAt": 1649513168000, + "text": "Run this and see that results are not like you've written", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c1082fcc3049e9063b", + "creator": "Cybernetic", + "createdAt": 1657248139000, + "text": "

A function with readable options is much more inuitive:

\n
function round_number(options) {\n    const places = 10**options.decimal_places;\n    const res = Math.round(options.number * places)/places;\n    return(res)\n}\n
\n

Usage:

\n
round_number({\n    number : 0.5555555555555556,\n    decimal_places : 3\n})\n\n0.556\n
\n", + "upvotes": 114, + "upvoterUsernames": [], + "downvotes": 114, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9063c", + "creator": "S M Samnoon Abrar", + "createdAt": 1657262813000, + "text": "

Here I used a ternary operator to check if the number has fractional values. If it doesn't then I simply return the number.

\n

Otherwise, I use the Intl.NumberFormat constructor to get the desired value.

\n

Intl.NumberFormat is part of the ECMAScript Internationalization API Specification (ECMA402). It has pretty good browser support, including even IE11, and it is fully supported in Node.js.

\n

\r\n
\r\n
const numberFormatter = new Intl.NumberFormat('en-US', {\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2,\n});\n\nfunction getRoundedNumber(number) {\n  return number.toString().indexOf(\".\") == -1 ? number : numberFormatter.format(number);\n}\n\n\nconsole.log(getRoundedNumber(10));\nconsole.log(getRoundedNumber(1.7777777));\nconsole.log(getRoundedNumber(9.1));\nconsole.log(getRoundedNumber(2.345));\nconsole.log(getRoundedNumber(2.2095));\nconsole.log(getRoundedNumber(2.995));
\r\n
\r\n
\r\n

\n", + "upvotes": 72, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c1082fcc3049e9063d", + "creator": "Nahuel Ramos", + "createdAt": 1657638800000, + "text": "

Instead of using Math.round as @brian-ustas suggest, I prefer the Math.trunc approach to fix the the following situation:

\n
const twoDecimalRound = num => Math.round(num * 100) / 100;\nconst twoDecimalTrunc = num => Math.trunc(num * 100) / 100;\nconsole.info(twoDecimalRound(79.996)); // not desired output: 80;\nconsole.info(twoDecimalTrunc(79.996)); // desired output: 79.99;\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 3672908, + "uvac": 3672993 + } + }, + { + "_id": "62f321bb082fcc3049e8feb4", + "title": "How do I return the response from an asynchronous call?", + "title-lowercase": "how do i return the response from an asynchronous call?", + "creator": "Felix Kling", + "createdAt": 1357664774000, + "status": "open", + "text": "

How do I return the response/result from a function foo that makes an asynchronous request?

\n

I am trying to return the value from the callback, as well as assigning the result to a local variable inside the function and returning that one, but none of those ways actually return the response — they all return undefined or whatever the initial value of the variable result is.

\n

Example of an asynchronous function that accepts a callback (using jQuery's ajax function):

\n
function foo() {\n    var result;\n\n    $.ajax({\n        url: '...',\n        success: function(response) {\n            result = response;\n            // return response; // <- I tried that one as well\n        }\n    });\n\n    return result; // It always returns `undefined`\n}\n
\n

Example using Node.js:

\n
function foo() {\n    var result;\n\n    fs.readFile("path/to/file", function(err, data) {\n        result = data;\n        // return data; // <- I tried that one as well\n    });\n\n    return result; // It always returns `undefined`\n}\n
\n

Example using the then block of a promise:

\n
function foo() {\n    var result;\n\n    fetch(url).then(function(response) {\n        result = response;\n        // return response; // <- I tried that one as well\n    });\n\n    return result; // It always returns `undefined`\n}\n
\n", + "upvotes": 11587, + "upvoterUsernames": [], + "downvotes": 5178, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 1958190, + "answers": 30, + "answerItems": [ + { + "_id": "62f321bd082fcc3049e9010b", + "creator": "Hemant Bavle", + "createdAt": 1392749917000, + "text": "

The simplest solution is to create a JavaScript function and call it for the Ajax success callback.

\n
function callServerAsync(){\n    $.ajax({\n        url: '...',\n        success: function(response) {\n\n            successCallback(response);\n        }\n    });\n}\n\nfunction successCallback(responseObj){\n    // Do something like read the response and show data\n    alert(JSON.stringify(responseObj)); // Only applicable to a JSON response\n}\n\nfunction foo(callback) {\n\n    $.ajax({\n        url: '...',\n        success: function(response) {\n           return callback(null, response);\n        }\n    });\n}\n\nvar result = foo(function(err, result){\n          if (!err)\n           console.log(result);\n});\n
\n", + "upvotes": 358, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9010c", + "creator": "Nic", + "createdAt": 1400810701000, + "text": "

You are using Ajax incorrectly. The idea is not to have it return anything, but instead hand off the data to something called a callback function, which handles the data.

\n\n

That is:

\n\n
function handleData( responseData ) {\n\n    // Do what you want with the data\n    console.log(responseData);\n}\n\n$.ajax({\n    url: \"hi.php\",\n    ...\n    success: function ( data, status, XHR ) {\n        handleData(data);\n    }\n});\n
\n\n

Returning anything in the submit handler will not do anything. You must instead either hand off the data, or do what you want with it directly inside the success function.

\n", + "upvotes": 517, + "upvoterUsernames": [], + "downvotes": 235, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9010f", + "creator": "David R Tribble", + "createdAt": 1443048723000, + "text": "

Short answer: Your foo() method returns immediately, while the $ajax() call executes asynchronously after the function returns. The problem is then how or where to store the results retrieved by the async call once it returns.

\n\n

Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the foo() method, and to store the results in a member of that object after the async call completes.

\n\n
function foo(result) {\n    $.ajax({\n        url: '...',\n        success: function(response) {\n            result.response = response;   // Store the async result\n        }\n    });\n}\n\nvar result = { response: null };   // Object to hold the async result\nfoo(result);                       // Returns before the async completes\n
\n\n

Note that the call to foo() will still return nothing useful. However, the result of the async call will now be stored in result.response.

\n", + "upvotes": 65, + "upvoterUsernames": [], + "downvotes": 24, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322eb082fcc3049e912bd", + "creator": "Felix Kling", + "createdAt": 1443048787000, + "text": "While this works, it's not really better than assigning to a global variable.", + "upvotes": 20, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e9010e", + "creator": "jsbisht", + "createdAt": 1441198477000, + "text": "

Another approach to return a value from an asynchronous function, is to pass in an object that will store the result from the asynchronous function.

\n\n

Here is an example of the same:

\n\n
var async = require(\"async\");\n\n// This wires up result back to the caller\nvar result = {};\nvar asyncTasks = [];\nasyncTasks.push(function(_callback){\n    // some asynchronous operation\n    $.ajax({\n        url: '...',\n        success: function(response) {\n            result.response = response;\n            _callback();\n        }\n    });\n});\n\nasync.parallel(asyncTasks, function(){\n    // result is available after performing asynchronous operation\n    console.log(result)\n    console.log('Done');\n});\n
\n\n

I am using the result object to store the value during the asynchronous operation. This allows the result be available even after the asynchronous job.

\n\n

I use this approach a lot. I would be interested to know how well this approach works where wiring the result back through consecutive modules is involved.

\n", + "upvotes": 203, + "upvoterUsernames": [], + "downvotes": 91, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9010d", + "creator": "Maleen Abewardana", + "createdAt": 1409040696000, + "text": "

Angular 1

\n

People who are using AngularJS, can handle this situation using promises.

\n

Here it says,

\n
\n

Promises can be used to unnest asynchronous functions and allows one to chain multiple functions together.

\n
\n

You can find a nice explanation here also.

\n

An example found in documentation mentioned below.

\n
  promiseB = promiseA.then(\n    function onSuccess(result) {\n      return result + 1;\n    }\n    ,function onError(err) {\n      // Handle error\n    }\n  );\n\n // promiseB will be resolved immediately after promiseA is resolved\n // and its value will be the result of promiseA incremented by 1.\n
\n

Angular 2 and later

\n

In Angular 2 with look at the following example, but its recommended to use observables with Angular 2.

\n
 search(term: string) {\n     return this.http\n       .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)\n       .map((response) => response.json())\n       .toPromise();\n}\n
\n

You can consume that in this way,

\n
search() {\n    this.searchService.search(this.searchField.value)\n      .then((result) => {\n    this.result = result.artists.items;\n  })\n  .catch((error) => console.error(error));\n}\n
\n

See the original post here. But TypeScript does not support native ES6 Promises, if you want to use it, you might need plugin for that.

\n

Additionally, here is the promises specification.

\n", + "upvotes": 253, + "upvoterUsernames": [], + "downvotes": 72, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322eb082fcc3049e912c0", + "creator": "Benjamin Gruenbaum", + "createdAt": 1415068150000, + "text": "This does not explain how promises would solve this issue at all though.", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90111", + "creator": "Pablo Matias Gomez", + "createdAt": 1461336428000, + "text": "

The short answer is, you have to implement a callback like this:

\n
function callback(response) {\n    // Here you can do what ever you want with the response object.\n    console.log(response);\n}\n\n$.ajax({\n    url: "...",\n    success: callback\n});\n
\n", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 56, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90110", + "creator": "rohithpr", + "createdAt": 1453743836000, + "text": "

While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:

\n\n
if (!name) {\n  name = async1();\n}\nasync2(name);\n
\n\n

You'd end up going through async1; check if name is undefined or not and call the callback accordingly.

\n\n
async1(name, callback) {\n  if (name)\n    callback(name)\n  else {\n    doSomething(callback)\n  }\n}\n\nasync1(name, async2)\n
\n\n

While it is okay in small examples it gets annoying when you have a lot of similar cases and error handling involved.

\n\n

Fibers helps in solving the issue.

\n\n
var Fiber = require('fibers')\n\nfunction async1(container) {\n  var current = Fiber.current\n  var result\n  doSomething(function(name) {\n    result = name\n    fiber.run()\n  })\n  Fiber.yield()\n  return result\n}\n\nFiber(function() {\n  var name\n  if (!name) {\n    name = async1()\n  }\n  async2(name)\n  // Make any number of async calls from here\n}\n
\n\n

You can checkout the project here.

\n", + "upvotes": 147, + "upvoterUsernames": [], + "downvotes": 49, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322eb082fcc3049e912c4", + "creator": "Aluan Haddad", + "createdAt": 1521402234000, + "text": "Is this still relevant?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90112", + "creator": "Vinoth Rajendran", + "createdAt": 1464269200000, + "text": "

You can use this custom library (written using Promise) to make a remote call.

\n\n
function $http(apiConfig) {\n    return new Promise(function (resolve, reject) {\n        var client = new XMLHttpRequest();\n        client.open(apiConfig.method, apiConfig.url);\n        client.send();\n        client.onload = function () {\n            if (this.status >= 200 && this.status < 300) {\n                // Performs the function \"resolve\" when this.status is equal to 2xx.\n                // Your logic here.\n                resolve(this.response);\n            }\n            else {\n                // Performs the function \"reject\" when this.status is different than 2xx.\n                reject(this.statusText);\n            }\n        };\n        client.onerror = function () {\n            reject(this.statusText);\n        };\n    });\n}\n
\n\n

Simple usage example:

\n\n
$http({\n    method: 'get',\n    url: 'google.com'\n}).then(function(response) {\n    console.log(response);\n}, function(error) {\n    console.log(error)\n});\n
\n", + "upvotes": 98, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90113", + "creator": "Francisco Carmona", + "createdAt": 1464856273000, + "text": "

Have a look at this example:

\n\n
var app = angular.module('plunker', []);\n\napp.controller('MainCtrl', function($scope,$http) {\n\n    var getJoke = function(){\n        return $http.get('http://api.icndb.com/jokes/random').then(function(res){\n            return res.data.value;  \n        });\n    }\n\n    getJoke().then(function(res) {\n        console.log(res.joke);\n    });\n});\n
\n\n

As you can see getJoke is returning a resolved promise (it is resolved when returning res.data.value). So you wait until the $http.get request is completed and then console.log(res.joke) is executed (as a normal asynchronous flow).

\n\n

This is the plnkr:

\n\n

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

\n\n

ES6 way (async - await)

\n\n
(function(){\n  async function getJoke(){\n    let response = await fetch('http://api.icndb.com/jokes/random');\n    let data = await response.json();\n    return data.value;\n  }\n\n  getJoke().then((joke) => {\n    console.log(joke);\n  });\n})();\n
\n", + "upvotes": 206, + "upvoterUsernames": [], + "downvotes": 81, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90114", + "creator": "Johannes Fahrenkrug", + "createdAt": 1470925056000, + "text": "

I will answer with a horrible-looking, hand-drawn comic. The second image is the reason why result is undefined in your code example.

\n\n

\"enter

\n", + "upvotes": 499, + "upvoterUsernames": [], + "downvotes": 229, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ec082fcc3049e912c9", + "creator": "Hassan Baig", + "createdAt": 1517790778000, + "text": "Would be great if you added lines of code with each image to illustrate the concepts.", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90115", + "creator": "Mohan Dere", + "createdAt": 1471081001000, + "text": "

Here are some approaches to work with asynchronous requests:

\n
    \n
  1. Browser Promise object
  2. \n
  3. Q - A promise library for JavaScript
  4. \n
  5. A+ Promises.js
  6. \n
  7. jQuery deferred
  8. \n
  9. XMLHttpRequest API
  10. \n
  11. Using callback concept - As implementation in first answer
  12. \n
\n

Example: jQuery deferred implementation to work with multiple requests

\n

\r\n
\r\n
var App = App || {};\n\nApp = {\n    getDataFromServer: function(){\n\n      var self = this,\n                 deferred = $.Deferred(),\n                 requests = [];\n\n      requests.push($.getJSON('request/ajax/url/1'));\n      requests.push($.getJSON('request/ajax/url/2'));\n\n      $.when.apply(jQuery, requests).done(function(xhrResponse) {\n        return deferred.resolve(xhrResponse.result);\n      });\n      return deferred;\n    },\n\n    init: function(){\n\n        this.getDataFromServer().done(_.bind(function(resp1, resp2) {\n\n           // Do the operations which you wanted to do when you\n           // get a response from Ajax, for example, log response.\n        }, this));\n    }\n};\nApp.init();
\r\n
\r\n
\r\n

\n", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ec082fcc3049e912cc", + "creator": "Henke", + "createdAt": 1620575254000, + "text": "Why include a Stack Snippet that outputs an error?", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90116", + "creator": "Mahfuzur Rahman", + "createdAt": 1493021343000, + "text": "

Use a callback() function inside the foo() success.\nTry it in this way. It is simple and easy to understand.

\n
var lat = "";\nvar lon = "";\n\nfunction callback(data) {\n    lat = data.lat;\n    lon = data.lon;\n}\n\nfunction getLoc() {\n    var url = "http://ip-api.com/json"\n    $.getJSON(url, function(data) {\n        callback(data);\n    });\n}\n\ngetLoc();\n
\n", + "upvotes": 53, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90117", + "creator": "Alireza", + "createdAt": 1495618690000, + "text": "

This is one of the places which two-way data binding or store concept that's used in many new JavaScript frameworks will work great for you...

\n

So if you are using Angular, React, or any other frameworks which do two-way data binding or store concept, this issue is simply fixed for you, so in easy words, your result is undefined at the first stage, so you have got result = undefined before you receive the data, then as soon as you get the result, it will be updated and get assigned to the new value which response of your Ajax call...

\n

But how you can do it in pure JavaScript or jQuery for example as you asked in this question?

\n

You can use a callback, promise and recently observable to handle it for you. For example, in promises we have some function like success() or then() which will be executed when your data is ready for you. The same with callback or the subscribe function on an observable.

\n

For example, in your case which you are using jQuery, you can do something like this:

\n
$(document).ready(function(){\n    function foo() {\n        $.ajax({url: "api/data", success: function(data){\n            fooDone(data); // After we have data, we pass it to fooDone\n        }});\n    };\n\n    function fooDone(data) {\n        console.log(data); // fooDone has the data and console.log it\n    };\n\n    foo(); // The call happens here\n});\n
\n

For more information, study promises and observables which are newer ways to do this async stuff.

\n", + "upvotes": 177, + "upvoterUsernames": [], + "downvotes": 53, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ec082fcc3049e912d0", + "creator": "Matthew Brent", + "createdAt": 1525449425000, + "text": "This is actually incorrect as React is one-way data binding", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90119", + "creator": "mikemaccana", + "createdAt": 1496397070000, + "text": "

2017 answer: you can now do exactly what you want in every current browser and Node.js

\n

This is quite simple:

\n\n

Here's a working version of your code:

\n
(async function(){\n\n    var response = await superagent.get('...')\n    console.log(response)\n\n})()\n
\n

await is supported in all current browsers and Node.js 8

\n", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 68, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ec082fcc3049e912d2", + "creator": "Juan Mendes", + "createdAt": 1538664694000, + "text": "IE 11 is still a current browser in 2018, sadly and it doesn't support await/async", + "upvotes": 3065, + "upvoterUsernames": [], + "downvotes": 3065, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90118", + "creator": "amaksr", + "createdAt": 1495853224000, + "text": "

Another solution is to execute code via the sequential executor nsynjs.

\n

If the underlying function is promisified

\n

nsynjs will evaluate all promises sequentially, and put the promise result into the data property:

\n

\r\n
\r\n
function synchronousCode() {\n\n    var getURL = function(url) {\n        return window.fetch(url).data.text().data;\n    };\n    \n    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';\n    console.log('received bytes:',getURL(url).length);\n    \n};\n\nnsynjs.run(synchronousCode,{},function(){\n    console.log('synchronousCode done');\n});
\r\n
<script src=\"https://rawgit.com/amaksr/nsynjs/master/nsynjs.js\"></script>
\r\n
\r\n
\r\n

\n

If the underlying function is not promisified

\n

Step 1. Wrap the function with a callback into the nsynjs-aware wrapper (if it has a promisified version, you can skip this step):

\n
var ajaxGet = function (ctx,url) {\n    var res = {};\n    var ex;\n    $.ajax(url)\n    .done(function (data) {\n        res.data = data;\n    })\n    .fail(function(e) {\n        ex = e;\n    })\n    .always(function() {\n        ctx.resume(ex);\n    });\n    return res;\n};\najaxGet.nsynjsHasCallback = true;\n
\n

Step 2. Put synchronous logic into function:

\n
function process() {\n    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);\n}\n
\n

Step 3. Run function in synchronous manner via nsynjs:

\n
nsynjs.run(process,this,function () {\n    console.log("synchronous function finished");\n});\n
\n

Nsynjs will evaluate all operators and expressions step-by-step, pausing execution in case if the result of some slow function is not ready.

\n

More examples are here.

\n", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9011a", + "creator": "Khoa Bui", + "createdAt": 1499286522000, + "text": "

Of course there are many approaches like synchronous request, promise, but from my experience I think you should use the callback approach. It's natural to asynchronous behavior of JavaScript.

\n

So, your code snippet can be rewritten to be a little different:

\n
function foo() {\n    var result;\n\n    $.ajax({\n        url: '...',\n        success: function(response) {\n            myCallback(response);\n        }\n    });\n\n    return result;\n}\n\nfunction myCallback(response) {\n    // Does something.\n}\n
\n", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ec082fcc3049e912d6", + "creator": "Aluan Haddad", + "createdAt": 1521402533000, + "text": "There's nothing inherently asynchronous about callbacks or JavaScript.", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f322ec082fcc3049e912d7", + "creator": "Henke", + "createdAt": 1620562912000, + "text": "Why keep var result; and return result;? The latter will still always return undefined!", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e9011b", + "creator": "Haim Zamir", + "createdAt": 1508901741000, + "text": "

Let's see the forest first before looking at the trees.

\n

There are many informative answers with great details here, I won't repeat any of them. The key to programming in JavaScript is having first the correct mental model of overall execution.

\n
    \n
  1. Your entry point(s) is executed as the result of an event. For\nexample, a script tag with code is loaded into the browser.\n(Accordingly, this is why you may need to be concerned with the\nreadiness of the page to run your code if it requires DOM elements\nto be constructed first, etc.)
  2. \n
  3. Your code executes to completion--however many asynchronous calls it\nmakes--without executing any of your callbacks, including XHR\nrequests, set timeouts, DOM event handlers, etc. Each of those callbacks waiting to be executed will sit in a queue, waiting their turn to be run after other events that fired have all finished execution.
  4. \n
  5. Each individual callback to an XHR request, set timeout or DOM\nthe event once invoked will then run to completion.
  6. \n
\n

The good news is that if you understand this point well, you will never have to worry about race conditions. You should first and foremost thing of how you want to organize your code as essentially the response to different discrete events, and how you want to thread them together into a logical sequence. You can use promises or higher level new async/await as tools to that end, or you can roll your own.

\n

But you shouldn't use any tactical tools to solve a problem until you are comfortable with the actual problem domain. Draw a map of these dependencies to know what needs to run when. Attempting an ad-hoc approach to all these callbacks is just not going to serve you well.

\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9011c", + "creator": "Pieter Jan Bonestroo", + "createdAt": 1515870800000, + "text": "

The question was:

\n
\n

How do I return the response from an asynchronous call?

\n
\n

which can be interpreted as:

\n
\n

How to make asynchronous code look synchronous?

\n
\n

The solution will be to avoid callbacks, and use a combination of Promises and async/await.

\n

I would like to give an example for an Ajax request.

\n

(Although it can be written in JavaScript, I prefer to write it in Python, and compile it to JavaScript using Transcrypt. It will be clear enough.)

\n

Let’s first enable jQuery usage, to have $ available as S:

\n
__pragma__ ('alias', 'S', '$')\n
\n

Define a function which returns a Promise, in this case an Ajax call:

\n
def read(url: str):\n    deferred = S.Deferred()\n    S.ajax({'type': "POST", 'url': url, 'data': { },\n        'success': lambda d: deferred.resolve(d),\n        'error': lambda e: deferred.reject(e)\n    })\n    return deferred.promise()\n
\n

Use the asynchronous code as if it were synchronous:

\n
async def readALot():\n    try:\n        result1 = await read("url_1")\n        result2 = await read("url_2")\n    except Exception:\n        console.warn("Reading a lot failed")\n
\n", + "upvotes": 52, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9011d", + "creator": "Fernando Carvajal", + "createdAt": 1516774735000, + "text": "

Using ES2017 you should have this as the function declaration.

\n
async function foo() {\n  var response = await $.ajax({url: '...'})\n  return response;\n}\n
\n

And executing it like this.

\n
(async function() {\n  try {\n    var result = await foo()\n    console.log(result)\n  } catch (e) {}\n})()\n
\n

Or the Promise syntax.

\n
foo().then(response => {\n  console.log(response)\n\n}).catch(error => {\n  console.log(error)\n\n})\n
\n

Stack Snippet that demonstrates the code above.

\n

\r\n
\r\n
// The function declaration:\nasync function foo() {\n  var response = await $.ajax({\n    url: 'https://jsonplaceholder.typicode.com/todos/1'\n  })\n  return response;\n}\n\n// Execute it like this:\n(async function() {\n  try {\n    var result = await foo()\n    console.log(result)\n  } catch (e) {}\n})()\n\n// Or use Promise syntax:\nfoo().then(response => {\n  console.log(response)\n}).catch(error => {\n  console.log(error)\n})
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
<script src=\n\"https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 40, + "upvoterUsernames": [], + "downvotes": 12, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ed082fcc3049e912db", + "creator": "Zum Dummi", + "createdAt": 1550368929000, + "text": "could that second function to reuseable??", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f322ed082fcc3049e912dd", + "creator": "Ken Ingram", + "createdAt": 1561790872000, + "text": "How do you use the results if oncolse,log is called? Doesn't everything just go to the console at that point?", + "upvotes": 51, + "upvoterUsernames": [], + "downvotes": 51, + "downvoterUsernames": [] + }, + { + "_id": "62f322ed082fcc3049e912df", + "creator": "RAUSHAN KUMAR", + "createdAt": 1632237352000, + "text": "I have a requirement to return some data after calculation from the callback function. How could i do that", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 7, + "downvoterUsernames": [] + }, + { + "_id": "62f322ed082fcc3049e912e1", + "creator": "CherryDT", + "createdAt": 1635502618000, + "text": "It's not possible.", + "upvotes": 208, + "upvoterUsernames": [], + "downvotes": 208, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e9011e", + "creator": "James", + "createdAt": 1518881188000, + "text": "

ECMAScript 6 has 'generators' which allow you to easily program in an asynchronous style.

\n\n
function* myGenerator() {\n    const callback = yield;\n    let [response] = yield $.ajax(\"https://stackoverflow.com\", {complete: callback});\n    console.log(\"response is:\", response);\n\n    // examples of other things you can do\n    yield setTimeout(callback, 1000);\n    console.log(\"it delayed for 1000ms\");\n    while (response.statusText === \"error\") {\n        [response] = yield* anotherGenerator();\n    }\n}\n
\n\n

To run the above code you do this:

\n\n
const gen = myGenerator(); // Create generator\ngen.next(); // Start it\ngen.next((...args) => gen.next([...args])); // Set its callback function\n
\n\n

If you need to target browsers that don't support ES6 you can run the code through Babel or closure-compiler to generate ECMAScript 5.

\n\n

The callback ...args are wrapped in an array and destructured when you read them so that the pattern can cope with callbacks that have multiple arguments. For example with node fs:

\n\n
const [err, data] = yield fs.readFile(filePath, \"utf-8\", callback);\n
\n", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e9011f", + "creator": "Alex Montoya", + "createdAt": 1531432095000, + "text": "

Here is an example that works:\n

\r\n
\r\n
const validateName = async userName => {\n  const url = \"https://jsonplaceholder.typicode.com/todos/1\";\n  try {\n    const response = await axios.get(url);\n    return response.data\n  } catch (err) {\n    return false;\n  }\n};\n\nvalidateName(\"user\")\n .then(data => console.log(data))\n .catch(reason => console.log(reason.message))
\r\n
.as-console-wrapper { max-height: 100% !important; top: 0; }
\r\n
<script src=\n\"https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js\"></script>
\r\n
\r\n
\r\n

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90120", + "creator": "Sumer", + "createdAt": 1543754499000, + "text": "

Simple code example to convert XHR on Node.js to async-await

\n
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;\nvar xhttp = new XMLHttpRequest();\n\nfunction xhrWrapWithPromise() {\n  return new Promise((resolve, reject) => {\n    xhttp.onreadystatechange = function() {\n      if (this.readyState == 4) {\n        if (this.status == 200) {\n          resolve(this.responseText);\n        } else {\n          reject(new Error("Couldn't feth data finally"));\n        }\n      }\n    };\n    xhttp.open("GET", "https://www.w3schools.com/xml/xmlhttp_info.txt", true);\n    xhttp.send();\n  });\n}\n\n// We need to wrap await in Async function so and anonymous IIFE here\n(async _ => {\n  try {\n    let result = await xhrWrapWithPromise();\n    console.log(result);\n  } catch (error) {\n    console.log(error);\n  }\n})();\n
\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ed082fcc3049e912e6", + "creator": "Henke", + "createdAt": 1620571532000, + "text": "This code does not seem to work as intended. I tried it in a Stack Snippet, and the only output was {}.", + "upvotes": 499, + "upvoterUsernames": [], + "downvotes": 499, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90121", + "creator": "Amir Fo", + "createdAt": 1544191823000, + "text": "

Using Promise

\n

The most perfect answer to this question is using Promise.

\n
function ajax(method, url, params) {\n  return new Promise(function(resolve, reject) {\n    var xhr = new XMLHttpRequest();\n    xhr.onload = function() {\n      resolve(this.responseText);\n    };\n    xhr.onerror = reject;\n    xhr.open(method, url);\n    xhr.send(params);\n  });\n}\n
\n

Usage

\n
ajax("GET", "/test", "acrive=1").then(function(result) {\n    // Code depending on result\n})\n.catch(function() {\n    // An error occurred\n});\n
\n
\n

But wait...!

\n

There is a problem with using promises!

\n

Why should we use our own custom Promise?

\n

I was using this solution for a while until I figured out there is an error in old browsers:

\n
\n

Uncaught ReferenceError: Promise is not defined

\n
\n

So I decided to implement my own Promise class for ES3 to below JavaScript compilers if it's not defined. Just add this code before your main code and then safely use Promise!

\n
if(typeof Promise === "undefined"){\n    function _classCallCheck(instance, Constructor) {\n        if (!(instance instanceof Constructor)) {\n            throw new TypeError("Cannot call a class as a function");\n        }\n    }\n    var Promise = function () {\n        function Promise(main) {\n            var _this = this;\n            _classCallCheck(this, Promise);\n            this.value = undefined;\n            this.callbacks = [];\n            var resolve = function resolve(resolveValue) {\n                _this.value = resolveValue;\n                _this.triggerCallbacks();\n            };\n            var reject = function reject(rejectValue) {\n                _this.value = rejectValue;\n                _this.triggerCallbacks();\n            };\n            main(resolve, reject);\n        }\n        Promise.prototype.then = function then(cb) {\n            var _this2 = this;\n            var next = new Promise(function (resolve) {\n                _this2.callbacks.push(function (x) {\n                    return resolve(cb(x));\n                });\n            });\n            return next;\n        };\n        Promise.prototype.catch = function catch_(cb) {\n            var _this2 = this;\n            var next = new Promise(function (reject) {\n                _this2.callbacks.push(function (x) {\n                    return reject(cb(x));\n                });\n            });\n            return next;\n        };\n        Promise.prototype.triggerCallbacks = function triggerCallbacks() {\n            var _this3 = this;\n            this.callbacks.forEach(function (cb) {\n                cb(_this3.value);\n            });\n        };\n        return Promise;\n    }();\n}\n
\n", + "upvotes": 62, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ed082fcc3049e912e8", + "creator": "Alaska", + "createdAt": 1632853934000, + "text": "I reckon you could also use a callback :D, but this is incredible.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90122", + "creator": "Murtaza Hussain", + "createdAt": 1555431196000, + "text": "

Use of async/await with a transpilers like Babel to get it working in older browsers. You’ll also have to install this Babel preset and polyfill from npm: npm i -D babel-preset-env babel-polyfill.

\n
function getData(ajaxurl) { \n  return $.ajax({\n    url: ajaxurl,\n    type: 'GET',\n  });\n};\n\nasync test() {\n  try {\n    const res = await getData('https://api.icndb.com/jokes/random')\n    console.log(res)\n  } catch(err) {\n    console.log(err);\n  }\n}\n\ntest();\n
\n

Or the .then callback is just another way to write the same logic.

\n
getData(ajaxurl).then(function(res) {\n    console.log(res)\n}\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90123", + "creator": "Kamil Kiełczewski", + "createdAt": 1560239770000, + "text": "

Await

\n

A request works in an asynchronous way, so you can't read the data synchronously as in typical code. However, using async/await you can create asynchronous code which looks close/similar to the usual synchronous/sequential style. Code which processes response data needs to be wrapped by an async function (load in the below snippet) and inside it you need to add the await keyword before foo() (which also uses async/await).

\n

\r\n
\r\n
async function foo() {\n  var url = 'https://jsonplaceholder.typicode.com/todos/1';\n  var result = (await fetch(url)).text(); // Or .json()\n  return result;\n}\n\nasync function load() {\n  var result = await foo();\n  console.log(result);\n}\n\nload();
\r\n
\r\n
\r\n

\n

Remember that an async function always (implicitly) wraps its result into a promise (so it returns a promise).

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90124", + "creator": "nonopolarity", + "createdAt": 1568100353000, + "text": "

I think no matter what method or mechanism used, or whatever the framework is (Angular/React) that hides it from you, the following principle holds:

\n\n
    \n
  1. In the flow of the program (think code or even the lowest level: machine code), the data may not arrive back 2 seconds later, 3 seconds later, or may not arrive at all, so there is no usual return to use in order to return the data.

  2. \n
  3. It is the classic \"observer pattern\". (It can be in the form of a \"callback\".) It is: \"hey, I am interested in knowing a successful arrival of data; would you let me know when it does.\" So you register an observer to be notified (or a function to be called to notify about the successful arrival of the data.) You also usually register an observer for the failure of arrival of such data.

  4. \n
  5. When it is successful arrival of data, or a failure of the return of such data, the registered observers (or callbacks) are notified together with the data (or called with the data). If the observer is registered in the form of a callback function foo, then foo(data) will be called. If the observer is registered in the form of an object foo, then depending on the interface, it could be that foo.notify(data) is called.

  6. \n
\n", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f322ed082fcc3049e912ec", + "creator": "nonopolarity", + "createdAt": 1611040139000, + "text": "ok it is about the naming... what I wanted to put through was, it is the classic "notify me when ready" kind of pattern", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321bd082fcc3049e90125", + "creator": "Philipp Claßen", + "createdAt": 1589277393000, + "text": "

Originally, callbacks were used for asynchronous operations (e.g., in the XMLHttpRequest API). Now promise-based APIs like the browser's Fetch API have become the default solution and the nicer async/await syntax is supported by all modern browsers and on Node.js (server side).

\n

A common scenario - fetching JSON data from the server - can look like this:

\n
async function fetchResource(url) {\n  const res = await fetch(url);\n  if (!res.ok) {\n    throw new Error(res.statusText);\n  }\n  return res.json();\n}\n
\n

To use it in another function:

\n
async function doSomething() {\n  try {\n    const data = await fetchResource("https://example.test/resource/1");\n    // ...\n  } catch (e) {\n    // Handle error\n    ...\n  }\n}\n
\n

If you design a modern API, it is strongly recommended to prefer promise-based style over callbacks. If you inherited an API that relies on callbacks, it is possible to wrap it as a promise:

\n
function sleep(timeout) {\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      resolve();\n    }, timeout);\n  });\n}\n\nasync function fetchAfterTwoSeconds(url) {\n  await sleep(2000);\n  return fetchResource(url);\n}\n
\n

In Node.js, which historically relied exclusively on callbacks, that technique is so common that they added a helper function called util.promisify.

\n", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90127", + "creator": "Abd Abughazaleh", + "createdAt": 1604830631000, + "text": "

async: false

\n

I solved it by setting async to false and restructure my Ajax call:

\n

I set a global function called sendRequest(type, url, data) with three parameters to be called every time everywhere:

\n
function sendRequest(type, url, data) {\n    let returnValue = null;\n    $.ajax({\n        url: url,\n        type: type,\n        async: false,\n        data: data,\n        dataType: 'json',\n        success: function (resp) {\n            returnValue = resp;\n        }\n    });\n    return returnValue;\n}\n
\n

Now call the function:

\n
let password = $("#password").val();\n        let email = $("#email").val();\n        let data = {\n            email: email,\n            password: password,\n        };\n        let  resp =  sendRequest('POST', 'http://localhost/signin')}}", data);\n        console.log(resp);\n
\n
\n

Important Note in code is : async: false

\n
\n

If this solution is not working with you, please note this may not working in some of browsers or jQuery versions.

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90126", + "creator": "David Spector", + "createdAt": 1604164675000, + "text": "

Since await always returns a Promise, simply do an extra await (inside an async function) to extract the value:

\n

\r\n
\r\n
test(); // This alerts \"hello\"\n\n// This is the outer function that wants to get the string result of inner()\nasync function test() {\n  var str=await await inner();\n  alert(str);\n} // test\n\n// This ia an inner function that can do arbitrary async operations\nasync function inner() {\n  return Promise.resolve('hello');\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321bd082fcc3049e90128", + "creator": "Rudresh Oza", + "createdAt": 1646709329000, + "text": "

use async & await

\n

Sample Code:

\n
const data = async() => {\n   const res = await get('https://getdata.com')\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 2, + "commentItems": [ + { + "_id": "62f321bd082fcc3049e90088", + "creator": "Sunil Kumar", + "createdAt": 1631100395000, + "text": "use deasync like this stackoverflow.com/a/47051880/2083877", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321bd082fcc3049e90089", + "creator": "Felix Kling", + "createdAt": 1634307200000, + "text": "@Liam: It's just an example for an asynchronous function that accepts a callback.", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1969779, + "uvac": 1969809 + } + }, + { + "_id": "62f321bb082fcc3049e8fec1", + "title": ""Thinking in AngularJS" if I have a jQuery background?", + "title-lowercase": ""thinking in angularjs" if i have a jquery background?", + "creator": "Mark Rajcok", + "createdAt": 1361419796000, + "status": "open", + "text": "

Suppose I'm familiar with developing client-side applications in jQuery, but now I'd like to start using AngularJS. Can you describe the paradigm shift that is necessary? Here are a few questions that might help you frame an answer:

\n\n\n\n

I'm not looking for a detailed comparison between jQuery and AngularJS.

\n", + "upvotes": 6710, + "upvoterUsernames": [], + "downvotes": 2201, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 848423, + "answers": 12, + "answerItems": [ + { + "_id": "62f321c0082fcc3049e90402", + "creator": "Mark Rajcok", + "createdAt": 1361419796000, + "text": "

Imperative → declarative

\n\n

In jQuery, selectors are used to find DOM elements and then bind/register event handlers to them. When an event triggers, that (imperative) code executes to update/change the DOM.

\n\n

In AngularJS, you want to think about views rather than DOM elements. Views are (declarative) HTML that contain AngularJS directives. Directives set up the event handlers behind the scenes for us and give us dynamic databinding. Selectors are rarely used, so the need for IDs (and some types of classes) is greatly diminished. Views are tied to models (via scopes). Views are a projection of the model. Events change models (that is, data, scope properties), and the views that project those models update \"automatically.\"

\n\n

In AngularJS, think about models, rather than jQuery-selected DOM elements that hold your data. Think about views as projections of those models, rather than registering callbacks to manipulate what the user sees.

\n\n

Separation of concerns

\n\n

jQuery employs unobtrusive JavaScript - behavior (JavaScript) is separated from the structure (HTML).

\n\n

AngularJS uses controllers and directives (each of which can have their own controller, and/or compile and linking functions) to remove behavior from the view/structure (HTML). Angular also has services and filters to help separate/organize your application.

\n\n

See also https://stackoverflow.com/a/14346528/215945

\n\n

Application design

\n\n

One approach to designing an AngularJS application:

\n\n
    \n
  1. Think about your models. Create services or your own JavaScript objects for those models.
  2. \n
  3. Think about how you want to present your models -- your views. Create HTML templates for each view, using the necessary directives to get dynamic databinding.
  4. \n
  5. Attach a controller to each view (using ng-view and routing, or ng-controller). Have the controller find/get only whatever model data the view needs to do its job. Make controllers as thin as possible.
  6. \n
\n\n

Prototypal inheritance

\n\n

You can do a lot with jQuery without knowing about how JavaScript prototypal inheritance works. When developing AngularJS applications, you will avoid some common pitfalls if you have a good understanding of JavaScript inheritance. Recommended reading: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

\n", + "upvotes": 717, + "upvoterUsernames": [], + "downvotes": 311, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90403", + "creator": "Ulises", + "createdAt": 1361420991000, + "text": "
\n

Can you describe the paradigm shift that is necessary?

\n
\n\n

Imperative vs Declarative

\n\n

With jQuery you tell the DOM what needs to happen, step by step. With AngularJS you describe what results you want but not how to do it. More on this here. Also, check out Mark Rajcok's answer.

\n\n
\n

How do I architect and design client-side web apps differently?

\n
\n\n

AngularJS is an entire client-side framework that uses the MVC pattern (check out their graphical representation). It greatly focuses on separation of concerns.

\n\n
\n

What is the biggest difference? What should I stop doing/using; what should I start doing/using instead?

\n
\n\n

jQuery is a library

\n\n

AngularJS is a beautiful client-side framework, highly testable, that combines tons of cool stuff such as MVC, dependency injection, data binding and much more.

\n\n

It focuses on separation of concerns and testing (unit testing and end-to-end testing), which facilitates test-driven development.

\n\n

The best way to start is going through their awesome tutorial. You can go through the steps in a couple of hours; however, in case you want to master the concepts behind the scenes, they include a myriad of reference for further reading.

\n\n
\n

Are there any server-side considerations/restrictions?

\n
\n\n

You may use it on existing applications where you are already using pure jQuery. However, if you want to fully take advantage of the AngularJS features you may consider coding the server side using a RESTful approach.

\n\n

Doing so will allow you to leverage their resource factory, which creates an abstraction of your server side RESTful API and makes server-side calls (get, save, delete, etc.) incredibly easy.

\n", + "upvotes": 240, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90404", + "creator": "Jin", + "createdAt": 1376489947000, + "text": "

They're apples and oranges. You don't want to compare them. They're two different things. AngularJs has already jQuery lite built in which allows you to perform basic DOM manipulation without even including the full blown jQuery version.

\n\n

jQuery is all about DOM manipulation. It solves all the cross browser pain otherwise you will have to deal with but it's not a framework that allows you to divide your app into components like AngularJS.

\n\n

A nice thing about AngularJs is that it allows you to separate/isolate the DOM manipulation in the directives. There are built-in directives ready for you to use such as ng-click. You can create your own custom directives that will contain all your view logic or DOM manipulation so you don't end up mingle DOM manipulation code in the controllers or services that should take care of the business logic.

\n\n

Angular breaks down your app into \n- Controllers\n- Services\n- Views\n- etc.

\n\n

and there is one more thing, that's the directive. It's an attribute you can attach to any DOM element and you can go nuts with jQuery within it without worrying about your jQuery ever conflicts with AngularJs components or messes up with its architecture.

\n\n

I heard from a meetup I attended, one of the founders of Angular said they worked really hard to separate out the DOM manipulation so do not try to include them back in.

\n", + "upvotes": 67, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90405", + "creator": "Evan Zamir", + "createdAt": 1379028060000, + "text": "

I find this question interesting, because my first serious exposure to JavaScript programming was Node.js and AngularJS. I never learned jQuery, and I guess that's a good thing, because I don't have to unlearn anything. In fact, I actively avoid jQuery solutions to my problems, and instead, solely look for an \"AngularJS way\" to solve them. So, I guess my answer to this question would essentially boil down to, \"think like someone who never learned jQuery\" and avoid any temptation to incorporate jQuery directly (obviously AngularJS uses it to some extent behind the scenes).

\n", + "upvotes": 38, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90407", + "creator": "Samuel", + "createdAt": 1383411233000, + "text": "

jQuery: you think a lot about 'QUERYing the DOM' for DOM elements and doing something.

\n\n

AngularJS: THE model is the truth, and you always think from that ANGLE.

\n\n

For example, when you get data from THE server which you intend to display in some format in the DOM, in jQuery, you need to '1. FIND' where in the DOM you want to place this data, the '2. UPDATE/APPEND' it there by creating a new node or just setting its innerHTML. Then when you want to update this view, you then '3. FIND' the location and '4. UPDATE'. This cycle of find and update all done within the same context of getting and formatting data from server is gone in AngularJS.

\n\n

With AngularJS you have your model (JavaScript objects you are already used to) and the value of the model tells you about the model (obviously) and about the view, and an operation on the model automatically propagates to the view, so you don't have to think about it. You will find yourself in AngularJS no longer finding things in the DOM.

\n\n

To put in another way, in jQuery, you need to think about CSS selectors, that is, where is the div or td that has a class or attribute, etc., so that I can get their HTML or color or value, but in AngularJS, you will find yourself thinking like this: what model am I dealing with, I will set the model's value to true. You are not bothering yourself of whether the view reflecting this value is a checked box or resides in a td element (details you would have often needed to think about in jQuery).

\n\n

And with DOM manipulation in AngularJS, you find yourself adding directives and filters, which you can think of as valid HTML extensions.

\n\n

One more thing you will experience in AngularJS: in jQuery you call the jQuery functions a lot, in AngularJS, AngularJS will call your functions, so AngularJS will 'tell you how to do things', but the benefits are worth it, so learning AngularJS usually means learning what AngularJS wants or the way AngularJS requires that you present your functions and it will call it accordingly. This is one of the things that makes AngularJS a framework rather than a library.

\n", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90406", + "creator": "Anand", + "createdAt": 1381395511000, + "text": "

jQuery is a DOM manipulation library.

\n\n

AngularJS is an MV* framework.

\n\n

In fact, AngularJS is one of the few JavaScript MV* frameworks (many JavaScript MVC tools still fall under the category library).

\n\n

Being a framework, it hosts your code and takes ownership of decisions about what to call and when!

\n\n

AngularJS itself includes a jQuery-lite edition within it. So for some basic DOM selection/manipulation, you really don't have to include the jQuery library (it saves many bytes to run on the network.)

\n\n

AngularJS has the concept of \"Directives\" for DOM manipulation and designing reusable UI components, so you should use it whenever you feel the need of doing DOM manipulation related stuff (directives are only place where you should write jQuery code while using AngularJS).

\n\n

AngularJS involves some learning curve (more than jQuery :-).

\n\n

-->For any developer coming from jQuery background, my first advice would be to \"learn JavaScript as a first class language before jumping onto a rich framework like AngularJS!\"\nI learned the above fact the hard way.

\n\n

Good luck.

\n", + "upvotes": 85, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90408", + "creator": "codevinsky", + "createdAt": 1383926883000, + "text": "

Listen to the podcast JavaScript Jabber: Episode #32 that features the original creators of AngularJS: Misko Hevery & Igor Minar. They talk a lot about what it's like to come to AngularJS from other JavaScript backgrounds, especially jQuery.

\n

One of the points made in the podcast made a lot of things click for me with respects to your question:

\n
\n

MISKO: [...] one of the things we thought about very hardly in Angular is, how do we provide lots of escape hatches so that you can get out and basically figure out a way out of this. So to us, the answer is this thing called “Directives”. And with directives, you essentially become a regular little jQuery JavaScript, you can do whatever you want.

\n

IGOR: So think of directive as the instruction to the compiler that tells it whenever you come across this certain element or this CSS in the template, and you keep this kind of code and that code is in charge of the element and everything below that element in the DOM tree.

\n
\n

A transcript of the entire episode is available at the link provided above.

\n

So, to directly answer your question: AngularJS is -very- opinionated and is a true MV* framework. However, you can still do all of the really cool stuff you know and love with jQuery inside of directives. It's not a matter of "How do I do what I used to in jQuery?" as much as it's a matter of "How do I supplement AngularJS with all of the stuff I used to do in jQuery?"

\n

It's really two very different states of mind.

\n", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e90409", + "creator": "Nick Manning", + "createdAt": 1391490474000, + "text": "

jQuery

\n\n

jQuery makes ridiculously long JavaScript commands like getElementByHerpDerp shorter and cross-browser.

\n\n

AngularJS

\n\n

AngularJS allows you to make your own HTML tags/attributes that do things which work well with dynamic web applications (since HTML was designed for static pages).

\n\n

Edit:

\n\n

Saying \"I have a jQuery background how do I think in AngularJS?\" is like saying \"I have an HTML background how do I think in JavaScript?\" The fact that you're asking the question shows you most likely don't understand the fundamental purposes of these two resources. This is why I chose to answer the question by simply pointing out the fundamental difference rather than going through the list saying \"AngularJS makes use of directives whereas jQuery uses CSS selectors to make a jQuery object which does this and that etc....\". This question does not require a lengthy answer.

\n\n

jQuery is a way to make programming JavaScript in the browser easier. Shorter, cross-browser commands, etc.

\n\n

AngularJS extends HTML, so you don't have to put <div> all over the place just to make an application. It makes HTML actually work for applications rather than what it was designed for, which is static, educational web pages. It accomplishes this in a roundabout way using JavaScript, but fundamentally it is an extension of HTML, not JavaScript.

\n", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9040a", + "creator": "Scott Rippey", + "createdAt": 1392969442000, + "text": "

To describe the \"paradigm shift\", I think a short answer can suffice.

\n\n

AngularJS changes the way you find elements

\n\n

In jQuery, you typically use selectors to find elements, and then wire them up:
\n$('#id .class').click(doStuff);

\n\n

In AngularJS, you use directives to mark the elements directly, to wire them up:
\n<a ng-click=\"doStuff()\">

\n\n

AngularJS doesn't need (or want) you to find elements using selectors - the primary difference between AngularJS's jqLite versus full-blown jQuery is that jqLite does not support selectors.

\n\n

So when people say \"don't include jQuery at all\", it's mainly because they don't want you to use selectors; they want you to learn to use directives instead. Direct, not select!

\n", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 43, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9040c", + "creator": "webketje", + "createdAt": 1403351948000, + "text": "

As a JavaScript MV* beginner and purely focusing on the application architecture (not the server/client-side matters), I would certainly recommend the following resource (which I am surprised wasn't mentioned yet): JavaScript Design Patterns, by Addy Osmani, as an introduction to different JavaScript Design Patterns. The terms used in this answer are taken from the linked document above. I'm not going to repeat what was worded really well in the accepted answer. Instead, this answer links back to the theoretical backgrounds which power AngularJS (and other libraries).

\n\n

Like me, you will quickly realize that AngularJS (or Ember.js, Durandal, & other MV* frameworks for that matter) is one complex framework assembling many of the different JavaScript design patterns.

\n\n

I found it easier also, to test (1) native JavaScript code and (2) smaller libraries for each one of these patterns separately before diving into one global framework. This allowed me to better understand which crucial issues a framework adresses (because you are personally faced with the problem).

\n\n

For example:

\n\n\n\n

NB: This list is not complete, nor 'the best libraries'; they just happen to be the libraries I used. These libraries also include more patterns, the ones mentioned are just their main focuses or original intents. If you feel something is missing from this list, please do mention it in the comments, and I will be glad to add it.

\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9040b", + "creator": "Dan", + "createdAt": 1400277011000, + "text": "

Those are some very nice, but lengthy answers.

\n\n

To sum up my experiences:

\n\n
    \n
  1. Controllers and providers (services, factories, etc.) are for modifying the data model, NOT HTML.
  2. \n
  3. HTML and directives define the layout and binding to the model.
  4. \n
  5. If you need to share data between controllers, create a service or factory - they are singletons that are shared across the application.
  6. \n
  7. If you need an HTML widget, create a directive.
  8. \n
  9. If you have some data and are now trying to update HTML... STOP! update the model, and make sure your HTML is bound to the model.
  10. \n
\n", + "upvotes": 86, + "upvoterUsernames": [], + "downvotes": 40, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c0082fcc3049e9040d", + "creator": "Huy Tran", + "createdAt": 1409119483000, + "text": "

Actually, if you're using AngularJS, you don't need jQuery anymore. AngularJS itself has the binding and directive, which is a very good \"replacement\" for most things you can do with jQuery.

\n\n

I usually develop mobile applications using AngularJS and Cordova. The ONLY thing from jQuery I needed is the Selector.

\n\n

By googling, I see that there is a standalone jQuery selector module out there. It's Sizzle.

\n\n

And I decided to make a tiny code snippet that help me quickly start a website using AngularJS with the power of jQuery Selector (using Sizzle).

\n\n

I shared my code here: https://github.com/huytd/Sizzular

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 855133, + "uvac": 855145 + } + }, + { + "_id": "62f321bb082fcc3049e8fedc", + "title": "Is it possible to apply CSS to half of a character?", + "title-lowercase": "is it possible to apply css to half of a character?", + "creator": "Mathew MacLean", + "createdAt": 1399652217000, + "status": "open", + "text": "

What I am looking for:

\n\n

A way to style one HALF of a character. (In this case, half the letter being transparent)

\n\n

What I have currently searched for and tried (With no luck):

\n\n\n\n

Below is an example of what I am trying to obtain.

\n\n

\"x\"

\n\n

Does a CSS or JavaScript solution exist for this, or am I going to have to resort to images? I would prefer not to go the image route as this text will end up being generated dynamically.

\n\n
\n\n

UPDATE:

\n\n

Since many have asked why I would ever want to style half of a character, this is why. My city had recently spent $250,000 to define a new \"brand\" for itself. This logo is what they came up with. Many people have complained about the simplicity and lack of creativity and continue to do so. My goal was to come up with this website as a joke. Type in 'Halifax' and you will see what I mean.

\n", + "upvotes": 4896, + "upvoterUsernames": [], + "downvotes": 1815, + "downvoterUsernames": [], + "hasAcceptedAnswer": false, + "views": 283653, + "answers": 17, + "answerItems": [ + { + "_id": "62f321c5082fcc3049e908f4", + "creator": "wvandaal", + "createdAt": 1399653226000, + "text": "

\"Example\"

\n
\n

JSFiddle DEMO

\n

We'll do it using just CSS pseudo selectors!

\n

This technique will work with dynamically generated content and different font sizes and widths.

\n

HTML:

\n
<div class='split-color'>Two is better than one.</div>\n
\n

CSS:

\n
.split-color > span {\n    white-space: pre-line;\n    position: relative;\n    color: #409FBF;\n}\n\n.split-color > span:before {\n    content: attr(data-content);\n    pointer-events: none;  /* Prevents events from targeting pseudo-element */\n    position: absolute;\n    overflow: hidden;\n    color: #264A73;\n    width: 50%;\n    z-index: 1;\n}\n
\n

To wrap the dynamically generated string, you could use a function like this:

\n
// Wrap each letter in a span tag and return an HTML string\n// that can be used to replace the original text\nfunction wrapString(str) {\n  var output = [];\n  str.split('').forEach(function(letter) {\n    var wrapper = document.createElement('span');\n    wrapper.dataset.content = wrapper.innerHTML = letter;\n\n    output.push(wrapper.outerHTML);\n  });\n\n  return output.join('');\n}\n\n// Replace the original text with the split-color text\nwindow.onload = function() {\n    var el  = document.querySelector('.split-color'),\n        txt = el.innerHTML;\n    \n    el.innerHTML = wrapString(txt);\n}\n
\n", + "upvotes": 292, + "upvoterUsernames": [], + "downvotes": 122, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f5", + "creator": "Adjit", + "createdAt": 1399653571000, + "text": "

Limited CSS and jQuery Solution

\n\n

I am not sure how elegant this solution is, but it cuts everything exactly in half: http://jsfiddle.net/9wxfY/11/

\n\n

Otherwise, I have created a nice solution for you... All you need to do is have this for your HTML:

\n\n

Take a look at this most recent, and accurate, edit as of 6/13/2016 : http://jsfiddle.net/9wxfY/43/

\n\n

As for the CSS, it is very limited... You only need to apply it to :nth-child(even)

\n\n

\r\n
\r\n
$(function(){\r\n  var $hc = $('.half-color');\r\n  var str = $hc.text();\r\n  $hc.html(\"\");\r\n\r\n  var i = 0;\r\n  var chars;\r\n  var dupText;\r\n\r\n  while(i < str.length){\r\n    chars = str[i];\r\n    if(chars == \" \") chars = \"&nbsp;\";\r\n    dupText = \"<span>\" + chars + \"</span>\";\r\n\r\n    var firstHalf = $(dupText);\r\n    var secondHalf = $(dupText);\r\n\r\n    $hc.append(firstHalf)\r\n    $hc.append(secondHalf)\r\n\r\n    var width = firstHalf.width()/2;\r\n\r\n    firstHalf.width(width);\r\n    secondHalf.css('text-indent', -width);\r\n\r\n    i++;\r\n  }\r\n});
\r\n
.half-color span{\r\n  font-size: 2em;\r\n  display: inline-block;\r\n  overflow: hidden;\r\n}\r\n.half-color span:nth-child(even){\r\n  color: red;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div class=\"half-color\">This is a sentence</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 57, + "upvoterUsernames": [], + "downvotes": 16, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f3", + "creator": "Razvan B.", + "createdAt": 1399653122000, + "text": "

\"enter


\n\n

I've just finished developing the plugin and it is available for everyone to use! Hope you will enjoy it.

\n\n

View Project on GitHub - View Project Website. (so you can see all the split styles)

\n\n

Usage

\n\n

First of all, make sure you have the jQuery library is included. The best way to get the latest jQuery version is to update your head tag with:

\n\n
<script src=\"http://code.jquery.com/jquery-latest.min.js\"></script>\n
\n\n

After downloading the files, make sure you include them in your project:

\n\n
<link rel=\"stylesheet\" type=\"text/css\" href=\"css/splitchar.css\">\n<script type=\"text/javascript\" src=\"js/splitchar.js\"></script>\n
\n\n

Markup

\n\n

All you have to do is to asign the class splitchar , followed by the desired style to the element wrapping your text. e.g

\n\n
<h1 class=\"splitchar horizontal\">Splitchar</h1>\n
\n\n

After all this is done, just make sure you call the jQuery function in your document ready file like this:

\n\n
$(\".splitchar\").splitchar();\n
\n\n

Customizing

\n\n

In order to make the text look exactly as you want it to, all you have to do is apply your design like this:

\n\n
.horizontal { /* Base CSS - e.g font-size */ }\n.horizontal:before { /* CSS for the left half */ }\n.horizontal:after { /* CSS for the right half */ }\n
\n\n


\nThat's it! Now you have the Splitchar plugin all set. More info about it at http://razvanbalosin.com/Splitchar.js/.

\n", + "upvotes": 779, + "upvoterUsernames": [], + "downvotes": 261, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32679082fcc3049e920ab", + "creator": "Mathew MacLean", + "createdAt": 1399654992000, + "text": "IT would appear so! Although, one issue I can see. What is causing the gap between some letters? For example, the huge gap between the two D's.", + "upvotes": 1061, + "upvoterUsernames": [], + "downvotes": 1061, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920ad", + "creator": "Mathew MacLean", + "createdAt": 1399655741000, + "text": "@BoltClock I've noticed that too.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920ae", + "creator": "Mathew MacLean", + "createdAt": 1399660424000, + "text": "For me italics is not a concern. @DA's concern however is quite a valid one.", + "upvotes": 35, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908f6", + "creator": "DA.", + "createdAt": 1399653771000, + "text": "

Yes, you can do this with only one character and only CSS:

\n

http://jsbin.com/rexoyice/1/

\n

\r\n
\r\n
h1 {\n  display: inline-block;\n  margin: 0; /* for demo snippet */\n  line-height: 1em; /* for demo snippet */\n  font-family: helvetica, arial, sans-serif;\n  font-weight: bold;\n  font-size: 300px;\n  background: linear-gradient(to right, #7db9e8 50%,#1e5799 50%);\n  background-clip: text;\n  -webkit-text-fill-color: transparent;\n}
\r\n
<h1>X</h1>
\r\n
\r\n
\r\n

\n

Visually, all the examples that use two characters (be it via JS, CSS pseudo elements, or just HTML) look fine, but note that that all adds content to the DOM which may cause accessibility--as well as text selection/cut/paste issues.

\n", + "upvotes": 272, + "upvoterUsernames": [], + "downvotes": 13, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f7", + "creator": "Sandro Paganotti", + "createdAt": 1399657804000, + "text": "

A nice solution that takes advantage of the background-clip: text support: http://jsfiddle.net/sandro_paganotti/wLkVt/

\n
span{\n   font-size: 100px;\n   background: linear-gradient(to right, black, black 50%, grey 50%, grey);\n   background-clip: text;\n   -webkit-text-fill-color: transparent;\n}\n
\n", + "upvotes": 42, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f8", + "creator": "Fiambre", + "createdAt": 1399664019000, + "text": "

Here an ugly implementation in canvas. I tried this solution, but the results are worse than I expected, so here it is anyway.

\n\n

\"Canvas

\n\n

\r\n
\r\n
$(\"div\").each(function() {\r\n  var CHARS = $(this).text().split('');\r\n  $(this).html(\"\");\r\n  $.each(CHARS, function(index, char) {\r\n    var canvas = $(\"<canvas />\")\r\n      .css(\"width\", \"40px\")\r\n      .css(\"height\", \"40px\")\r\n      .get(0);\r\n    $(\"div\").append(canvas);\r\n    var ctx = canvas.getContext(\"2d\");\r\n    var gradient = ctx.createLinearGradient(0, 0, 130, 0);\r\n    gradient.addColorStop(\"0\", \"blue\");\r\n    gradient.addColorStop(\"0.5\", \"blue\");\r\n    gradient.addColorStop(\"0.51\", \"red\");\r\n    gradient.addColorStop(\"1.0\", \"red\");\r\n    ctx.font = '130pt Calibri';\r\n    ctx.fillStyle = gradient;\r\n    ctx.fillText(char, 10, 130);\r\n  });\r\n});
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<div>Example Text</div>
\r\n
\r\n
\r\n

\n", + "upvotes": 146, + "upvoterUsernames": [], + "downvotes": 63, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32679082fcc3049e920b2", + "creator": "Toothbrush", + "createdAt": 1402328343000, + "text": "BTW, you can use 0.5 for the red colour stop, too.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908f2", + "creator": "Prisoner", + "createdAt": 1399652923000, + "text": "

Closest I can get:

\n\n

\r\n
\r\n
$(function(){\r\n  $('span').width($('span').width()/2);\r\n  $('span:nth-child(2)').css('text-indent', -$('span').width());\r\n});
\r\n
body{\r\n  font-family: arial;\r\n}\r\nspan{\r\n  display: inline-block;\r\n  overflow: hidden;\r\n}\r\nspan:nth-child(2){\r\n  color: red;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\r\n<span>X</span><span>X</span>
\r\n
\r\n
\r\n

\n\n

Demo: http://jsfiddle.net/9wxfY/2/

\n\n

Heres a version that just uses one span: http://jsfiddle.net/9wxfY/4/

\n", + "upvotes": 124, + "upvoterUsernames": [], + "downvotes": 59, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32679082fcc3049e920b5", + "creator": "Mathew MacLean", + "createdAt": 1399653291000, + "text": "When you go to implement more than one character it causes problems.", + "upvotes": 4820, + "upvoterUsernames": [], + "downvotes": 4820, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920b6", + "creator": "Hrishabh Gupta", + "createdAt": 1399975210000, + "text": "Problem will happen in copy.", + "upvotes": 457, + "upvoterUsernames": [], + "downvotes": 457, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920b8", + "creator": "MStrutt", + "createdAt": 1400008195000, + "text": "@Prisoner Thanks, I didn't think of it initially either, was just trying to minimise DOM interactions :)", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920ba", + "creator": "hooman", + "createdAt": 1577548626000, + "text": "This shows the wrong half of the x on the wrong side, like two brackets - ().", + "upvotes": 203, + "upvoterUsernames": [], + "downvotes": 203, + "downvoterUsernames": [] + }, + { + "_id": "62f32679082fcc3049e920bc", + "creator": "Prisoner", + "createdAt": 1578319031000, + "text": "@PythonMaster202 the inline preview is broken, check jsfiddle.net/9wxfY/4", + "upvotes": 219, + "upvoterUsernames": [], + "downvotes": 219, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c5082fcc3049e908fa", + "creator": "Shipow", + "createdAt": 1399926747000, + "text": "

\"Enter

\n\n

I just played with @Arbel's solution:

\n\n

\r\n
\r\n
var textToHalfStyle = $('.textToHalfStyle').text();\r\nvar textToHalfStyleChars = textToHalfStyle.split('');\r\n$('.textToHalfStyle').html('');\r\n$.each(textToHalfStyleChars, function(i,v){\r\n    $('.textToHalfStyle').append('<span class=\"halfStyle\" data-content=\"' + v + '\">' + v + '</span>');\r\n});
\r\n
body{\r\n    background-color: black;\r\n}\r\n.textToHalfStyle{\r\n    display:block;\r\n    margin: 200px 0 0 0;\r\n    text-align:center;\r\n}\r\n.halfStyle {\r\n    font-family: 'Libre Baskerville', serif;\r\n    position:relative;\r\n    display:inline-block;\r\n    width:1;\r\n    font-size:70px;\r\n    color: black;\r\n    overflow:hidden;\r\n    white-space: pre;\r\n    text-shadow: 1px 2px 0 white;\r\n}\r\n.halfStyle:before {\r\n    display:block;\r\n    z-index:1;\r\n    position:absolute;\r\n    top:0;\r\n    width: 50%;\r\n    content: attr(data-content); /* dynamic content for the pseudo element */\r\n    overflow:hidden;\r\n    color: white;\r\n}
\r\n
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js\"></script>\r\n<span class=\"textToHalfStyle\">Dr. Jekyll and M. Hide</span>
\r\n
\r\n
\r\n

\n", + "upvotes": 78, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908f9", + "creator": "MStrutt", + "createdAt": 1399920279000, + "text": "

Another CSS-only solution (though data-attribute is needed if you don't want to write letter-specific CSS). This one works more across the board (Tested IE 9/10, Chrome latest & FF latest)

\n\n

\r\n
\r\n
span {\r\n  position: relative;\r\n  color: rgba(50,50,200,0.5);\r\n}\r\n\r\nspan:before {\r\n  content: attr(data-char);\r\n  position: absolute;\r\n  width: 50%;\r\n  overflow: hidden;\r\n  color: rgb(50,50,200);\r\n}
\r\n
<span data-char=\"X\">X</span>
\r\n
\r\n
\r\n

\n", + "upvotes": 91, + "upvoterUsernames": [], + "downvotes": 45, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908fb", + "creator": "Sam Tremaine", + "createdAt": 1399973184000, + "text": "
.halfStyle {\n    position:relative;\n    display:inline-block;\n    font-size:68px; /* or any font size will work */\n    color: rgba(0,0,0,0.8); /* or transparent, any color */\n    overflow:hidden;\n    white-space: pre; /* to preserve the spaces from collapsing */\n    transform:rotate(4deg);\n    -webkit-transform:rotate(4deg);\n    text-shadow:2px 1px 3px rgba(0,0,0,0.3);\n}\n.halfStyle:before {\n    display:block;\n    z-index:1;\n    position:absolute;\n    top:-0.5px;\n    left:-3px;\n    width: 100%;\n    content: attr(data-content); /* dynamic content for the pseudo element */\n    overflow:hidden;\n    color: white;\n    transform:rotate(-4deg);\n    -webkit-transform:rotate(-4deg);\n    text-shadow:0 0 1px black;\n\n}\n
\n\n

http://experimental.samtremaine.co.uk/half-style/

\n\n

You can crowbar this code into doing all sorts of interesting things - this is just one implementation my associate and I came up with last night.

\n", + "upvotes": 59, + "upvoterUsernames": [], + "downvotes": 26, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908fd", + "creator": "Ricardo Zea", + "createdAt": 1400271671000, + "text": "

FWIW, here's my take on this doing it only with CSS: http://codepen.io/ricardozea/pen/uFbts/

\n\n

Several notes:

\n\n\n\n

HTML

\n\n
<span class=\"half-letter\"></span>\n
\n\n

SCSS

\n\n
.half-character { \n  display: inline-block;\n  font: bold 350px/.8 Arial;\n  position: relative;\n\n  &:before, &:after {\n    content: 'X'; //Change character here\n    display: inline-block;\n    width: 50%;\n    overflow: hidden;\n    color: #7db9e8;\n  }\n  &:after {\n    position: absolute;\n    top: 0;\n    left: 50%;\n    color: #1e5799;\n    transform: rotateY(-180deg);\n  }\n}\n
\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908fe", + "creator": "Ruskin", + "createdAt": 1412167165000, + "text": "

If you are interested in this, then Lucas Bebber's Glitch is a very similar and super cool effect:

\n\n

\"enter

\n\n

Created using a simple SASS Mixin such as

\n\n
.example-one {\n  font-size: 100px;\n  @include textGlitch(\"example-one\", 17, white, black, red, blue, 450, 115);\n}\n
\n\n

More details at Chris Coyer's CSS Tricks and Lucas Bebber's Codepen page

\n", + "upvotes": 102, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90900", + "creator": "Gauri Bhosle", + "createdAt": 1493876662000, + "text": "

You can use below code. Here in this example I have used h1 tag and added an attribute data-title-text=\"Display Text\" which will appear with different color text on h1 tag text element, which gives effect halfcolored text as shown in below example

\n\n

\"enter

\n\n

\r\n
\r\n
body {\r\n  text-align: center;\r\n  margin: 0;\r\n}\r\n\r\nh1 {\r\n  color: #111;\r\n  font-family: arial;\r\n  position: relative;\r\n  font-family: 'Oswald', sans-serif;\r\n  display: inline-block;\r\n  font-size: 2.5em;\r\n}\r\n\r\nh1::after {\r\n  content: attr(data-title-text);\r\n  color: #e5554e;\r\n  position: absolute;\r\n  left: 0;\r\n  top: 0;\r\n  clip: rect(0, 1000px, 30px, 0);\r\n}
\r\n
<h1 data-title-text=\"Display Text\">Display Text</h1>
\r\n
\r\n
\r\n

\n", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908ff", + "creator": "Sleek Geek", + "createdAt": 1452782492000, + "text": "

This can be achieved with just CSS :before selector and content property value.

\n\n

\r\n
\r\n
.halfed, .halfed1 {\r\n  float: left;\r\n}\r\n\r\n.halfed, .halfed1 {\r\n  font-family: arial;\r\n  font-size: 300px;\r\n  font-weight: bolder;\r\n  width: 200px;\r\n  height: 300px;\r\n  position: relative; /* To help hold the content value within */\r\n  overflow: hidden;\r\n  color: #000;\r\n}\r\n\r\n\r\n\r\n\r\n.halfed:before, .halfed1:before   {\r\n  width: 50%; /* How much we'd like to show */\r\n  overflow: hidden; /* Hide what goes beyond our dimension */  \r\n  content: 'X'; /* Halfed character */\r\n  height: 100%;\r\n  position: absolute;\r\n  color: #28507D;\r\n\r\n}\r\n\r\n\r\n\r\n/* For Horizontal cut off */ \r\n\r\n.halfed1:before   {\r\n  width: 100%;\r\n  height: 55%;\r\n  \r\n}
\r\n
<div class=\"halfed\"> X </div>\r\n\r\n<div class=\"halfed1\"> X </div>
\r\n
\r\n
\r\n

\n\n

>> See on jsFiddle

\n", + "upvotes": 26, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90902", + "creator": "the Hutt", + "createdAt": 1642584923000, + "text": "

All solutions work by splitting letters and wrapping them in <span>. We don't have to split letters in two cases:

\n\n

\r\n
\r\n
div {\n  font-size: 80px;\n  font-weight: bolder;\n  color: transparent;\n  padding: 0;\n  margin: 0;\n  background: linear-gradient(90deg, rgb(34, 67, 143) 0% 50%, #409FBF 50%);\n  background-clip: text;\n  -webkit-background-clip: text;\n}\n\n.one {\n  font-family: 'Nova Mono';\n  background-repeat: repeat-x;\n  background-size: 45px;\n}\n\n.two {\n  font-family: 'Gideon Roman';\n  writing-mode: vertical-lr;\n  text-orientation: upright;\n  letter-spacing: -35px;\n  height: 500px;\n}
\r\n
<!-- get the fonts -->\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Nova+Mono&display=swap\" rel=\"stylesheet\">\n<link href=\"https://fonts.googleapis.com/css2?family=Gideon+Roman&display=swap\" rel=\"stylesheet\">\n\n\n<div id='one' class=\"one\">X-RAY Winter</div>\n<div class=\"two\">Minty</div>
\r\n
\r\n
\r\n

\n

Expected output, in case the fonts are not available:

\n

\"enter

\n

I know use of background-clip and gradient has been already demonstrated in other answers, just putting the cases where you don't have to split the letters.

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e90901", + "creator": "Alireza", + "createdAt": 1497877133000, + "text": "

How about something like this for shorter text?

\n\n

It could even work for longer text if you did something with a loop, repeating the characters with JavaScript. Anyway, the result is something like this:

\n\n

\"Is

\n\n

\r\n
\r\n
p.char {\r\n  position: relative;\r\n  display: inline-block;\r\n  font-size: 60px;\r\n  color: red;\r\n}\r\n\r\np.char:before {\r\n  position: absolute;\r\n  content: attr(char);\r\n  width: 50%;\r\n  overflow: hidden;\r\n  color: black;\r\n}
\r\n
<p class=\"char\" char=\"S\">S</p>\r\n<p class=\"char\" char=\"t\">t</p>\r\n<p class=\"char\" char=\"a\">a</p>\r\n<p class=\"char\" char=\"c\">c</p>\r\n<p class=\"char\" char=\"k\">k</p>\r\n<p class=\"char\" char=\"o\">o</p>\r\n<p class=\"char\" char=\"v\">v</p>\r\n<p class=\"char\" char=\"e\">e</p>\r\n<p class=\"char\" char=\"r\">r</p>\r\n<p class=\"char\" char=\"f\">f</p>\r\n<p class=\"char\" char=\"l\">l</p>\r\n<p class=\"char\" char=\"o\">o</p>\r\n<p class=\"char\" char=\"w\">w</p>
\r\n
\r\n
\r\n

\n", + "upvotes": 58, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c5082fcc3049e908fc", + "creator": "Nic Bell", + "createdAt": 1400160401000, + "text": "

You can also do it using SVG, if you wish:

\n\n

\r\n
\r\n
var title = document.querySelector('h1'),\r\n    text = title.innerHTML,\r\n    svgTemplate = document.querySelector('svg'),\r\n    charStyle = svgTemplate.querySelector('#text');\r\n\r\nsvgTemplate.style.display = 'block';\r\n\r\nvar space = 0;\r\n\r\nfor (var i = 0; i < text.length; i++) {\r\n  var x = charStyle.cloneNode();\r\n  x.textContent = text[i];\r\n  svgTemplate.appendChild(x);\r\n  x.setAttribute('x', space);\r\n  space += x.clientWidth || 15;\r\n}\r\n\r\ntitle.innerHTML = '';\r\ntitle.appendChild(svgTemplate);
\r\n
<svg style=\"display: none; height: 100px; width: 100%\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\">\r\n    <defs id=\"FooDefs\">\r\n        <linearGradient id=\"MyGradient\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\r\n            <stop offset=\"50%\" stop-color=\"blue\" />\r\n            <stop offset=\"50%\" stop-color=\"red\" />\r\n        </linearGradient>\r\n    </defs>\r\n    <text y=\"50%\" id=\"text\" style=\"font-size: 72px; fill: url(#MyGradient)\"></text>\r\n</svg>\r\n\r\n<h1>This is not a solution X</h1>
\r\n
\r\n
\r\n

\n\n

http://codepen.io/nicbell/pen/jGcbq

\n", + "upvotes": 25, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + } + ], + "comments": 0, + "commentItems": [], + "sorter": { + "uvc": 288549, + "uvac": 288566 + } + }, + { + "_id": "62f321bb082fcc3049e8fef2", + "title": "Using async/await with a forEach loop", + "title-lowercase": "using async/await with a foreach loop", + "creator": "Saad", + "createdAt": 1464807358000, + "status": "open", + "text": "

Are there any issues with using async/await in a forEach loop? I'm trying to loop through an array of files and await on the contents of each file.

\n
import fs from 'fs-promise'\n\nasync function printFiles () {\n  const files = await getFilePaths() // Assume this works fine\n\n  files.forEach(async (file) => {\n    const contents = await fs.readFile(file, 'utf8')\n    console.log(contents)\n  })\n}\n\nprintFiles()\n
\n

This code does work, but could something go wrong with this? I had someone tell me that you're not supposed to use async/await in a higher-order function like this, so I just wanted to ask if there was any issue with this.

\n", + "upvotes": 4556, + "upvoterUsernames": [], + "downvotes": 2000, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 1521617, + "answers": 29, + "answerItems": [ + { + "_id": "62f321ca082fcc3049e90c9f", + "creator": "Bergi", + "createdAt": 1464807729000, + "text": "

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

\n\n

Reading in sequence

\n\n

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

\n\n
async function printFiles () {\n  const files = await getFilePaths();\n\n  for (const file of files) {\n    const contents = await fs.readFile(file, 'utf8');\n    console.log(contents);\n  }\n}\n
\n\n

Reading in parallel

\n\n

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

\n\n
async function printFiles () {\n  const files = await getFilePaths();\n\n  await Promise.all(files.map(async (file) => {\n    const contents = await fs.readFile(file, 'utf8')\n    console.log(contents)\n  }));\n}\n
\n", + "upvotes": 6850, + "upvoterUsernames": [], + "downvotes": 2214, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f32839082fcc3049e926da", + "creator": "Demonbane", + "createdAt": 1471284274000, + "text": "Could you please explain why does for ... of ... work?", + "upvotes": 113, + "upvoterUsernames": [], + "downvotes": 35, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926dc", + "creator": "arve0", + "createdAt": 1490785996000, + "text": "So files.map(async (file) => ... is equivalent to files.map((file) => new Promise((rej, res) => { ...?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926de", + "creator": "Bergi", + "createdAt": 1521552510000, + "text": "@Taurus I don't understand. What is bad for performance in comparison to what?", + "upvotes": 583, + "upvoterUsernames": [], + "downvotes": 583, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926df", + "creator": "Bergi", + "createdAt": 1521553997000, + "text": "@Taurus Yes, that's basically the two approaches from my answer. Neither of them uses forEach. Where's the problem?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e0", + "creator": "doubleOrt", + "createdAt": 1521555503000, + "text": "@Bergi However, I still don't understand how this is correct: If you want to read the files in parallel, you cannot use forEach indeed.", + "upvotes": 21, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e2", + "creator": "leonheess", + "createdAt": 1630090054000, + "text": "Void function return value is used ", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 5, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e4", + "creator": "Bergi", + "createdAt": 1630101442000, + "text": "@leonheess What's the problem? Where (what code position) are you getting that warning? What tool do you use?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e5", + "creator": "leonheess", + "createdAt": 1630188436000, + "text": "@Bergi my bad I used forEach instead of mal", + "upvotes": 197, + "upvoterUsernames": [], + "downvotes": 197, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e6", + "creator": "Griffi", + "createdAt": 1631743088000, + "text": "the best answer ;) Useful is to just think what the transpiler is doing with async / await and how the final code will look like", + "upvotes": 549, + "upvoterUsernames": [], + "downvotes": 549, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e8", + "creator": "Bergi", + "createdAt": 1631744761000, + "text": "@Griffi Why are you still using a transpiler for async/await? And what will it transpile to?", + "upvotes": 4857, + "upvoterUsernames": [], + "downvotes": 4857, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926e9", + "creator": "Augustin Riedinger", + "createdAt": 1639408930000, + "text": "There should be a forEachAwait operator that does just that!", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926eb", + "creator": "Bergi", + "createdAt": 1639410810000, + "text": "@AugustinRiedinger It's called for … of!", + "upvotes": 222, + "upvoterUsernames": [], + "downvotes": 222, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926ec", + "creator": "Bergi", + "createdAt": 1639472359000, + "text": "@AugustinRiedinger You cannot return anything useful from a forEach callback anyway", + "upvotes": 844, + "upvoterUsernames": [], + "downvotes": 844, + "downvoterUsernames": [] + }, + { + "_id": "62f32839082fcc3049e926ed", + "creator": "Adrian Bartholomew", + "createdAt": 1641920240000, + "text": "You would need to return contents.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90ca1", + "creator": "Hooman Askari", + "createdAt": 1503744441000, + "text": "

Both the solutions above work, however, Antonio's does the job with less code, here is how it helped me resolve data from my database, from several different child refs and then pushing them all into an array and resolving it in a promise after all is done:

\n\n
Promise.all(PacksList.map((pack)=>{\n    return fireBaseRef.child(pack.folderPath).once('value',(snap)=>{\n        snap.forEach( childSnap => {\n            const file = childSnap.val()\n            file.id = childSnap.key;\n            allItems.push( file )\n        })\n    })\n})).then(()=>store.dispatch( actions.allMockupItems(allItems)))\n
\n", + "upvotes": 7, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca0", + "creator": "Antonio Val", + "createdAt": 1499674551000, + "text": "

The p-iteration module on npm implements the Array iteration methods so they can be used in a very straightforward way with async/await.

\n\n

An example with your case:

\n\n
const { forEach } = require('p-iteration');\nconst fs = require('fs-promise');\n\n(async function printFiles () {\n  const files = await getFilePaths();\n\n  await forEach(files, async (file) => {\n    const contents = await fs.readFile(file, 'utf8');\n    console.log(contents);\n  });\n})();\n
\n", + "upvotes": 79, + "upvoterUsernames": [], + "downvotes": 27, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca3", + "creator": "LeOn - Han Li", + "createdAt": 1506283243000, + "text": "

One important caveat is: The await + for .. of method and the forEach + async way actually have different effect.

\n\n

Having await inside a real for loop will make sure all async calls are executed one by one. And the forEach + async way will fire off all promises at the same time, which is faster but sometimes overwhelmed(if you do some DB query or visit some web services with volume restrictions and do not want to fire 100,000 calls at a time).

\n\n

You can also use reduce + promise(less elegant) if you do not use async/await and want to make sure files are read one after another.

\n\n
files.reduce((lastPromise, file) => \n lastPromise.then(() => \n   fs.readFile(file, 'utf8')\n ), Promise.resolve()\n)\n
\n\n

Or you can create a forEachAsync to help but basically use the same for loop underlying.

\n\n
Array.prototype.forEachAsync = async function(cb){\n    for(let x of this){\n        await cb(x);\n    }\n}\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca2", + "creator": "Jay Edwards", + "createdAt": 1506121410000, + "text": "

it's pretty painless to pop a couple methods in a file that will handle asynchronous data in a serialized order and give a more conventional flavour to your code. For example:

\n\n
module.exports = function () {\n  var self = this;\n\n  this.each = async (items, fn) => {\n    if (items && items.length) {\n      await Promise.all(\n        items.map(async (item) => {\n          await fn(item);\n        }));\n    }\n  };\n\n  this.reduce = async (items, fn, initialValue) => {\n    await self.each(\n      items, async (item) => {\n        initialValue = await fn(initialValue, item);\n      });\n    return initialValue;\n  };\n};\n
\n\n

now, assuming that's saved at './myAsync.js' you can do something similar to the below in an adjacent file:

\n\n
...\n/* your server setup here */\n...\nvar MyAsync = require('./myAsync');\nvar Cat = require('./models/Cat');\nvar Doje = require('./models/Doje');\nvar example = async () => {\n  var myAsync = new MyAsync();\n  var doje = await Doje.findOne({ name: 'Doje', noises: [] }).save();\n  var cleanParams = [];\n\n  // FOR EACH EXAMPLE\n  await myAsync.each(['bork', 'concern', 'heck'], \n    async (elem) => {\n      if (elem !== 'heck') {\n        await doje.update({ $push: { 'noises': elem }});\n      }\n    });\n\n  var cat = await Cat.findOne({ name: 'Nyan' });\n\n  // REDUCE EXAMPLE\n  var friendsOfNyanCat = await myAsync.reduce(cat.friends,\n    async (catArray, friendId) => {\n      var friend = await Friend.findById(friendId);\n      if (friend.name !== 'Long cat') {\n        catArray.push(friend.name);\n      }\n    }, []);\n  // Assuming Long Cat was a friend of Nyan Cat...\n  assert(friendsOfNyanCat.length === (cat.friends.length - 1));\n}\n
\n", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f32839082fcc3049e926f1", + "creator": "Jay Edwards", + "createdAt": 1506416920000, + "text": "Minor addendum, don't forget to wrap your await/asyncs in try/catch blocks!!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90ca5", + "creator": "chharvey", + "createdAt": 1519346847000, + "text": "

In addition to @Bergi’s answer, I’d like to offer a third alternative. It's very similar to @Bergi’s 2nd example, but instead of awaiting each readFile individually, you create an array of promises, each which you await at the end.

\n\n
import fs from 'fs-promise';\nasync function printFiles () {\n  const files = await getFilePaths();\n\n  const promises = files.map((file) => fs.readFile(file, 'utf8'))\n\n  const contents = await Promise.all(promises)\n\n  contents.forEach(console.log);\n}\n
\n\n

Note that the function passed to .map() does not need to be async, since fs.readFile returns a Promise object anyway. Therefore promises is an array of Promise objects, which can be sent to Promise.all().

\n\n

In @Bergi’s answer, the console may log file contents in the order they’re read. For example if a really small file finishes reading before a really large file, it will be logged first, even if the small file comes after the large file in the files array. However, in my method above, you are guaranteed the console will log the files in the same order as the provided array.

\n", + "upvotes": 24, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca4", + "creator": "Zachary Ryan Smith", + "createdAt": 1517760227000, + "text": "

I would use the well-tested (millions of downloads per week) pify and async modules. If you are unfamiliar with the async module, I highly recommend you check out its docs. I've seen multiple devs waste time recreating its methods, or worse, making difficult-to-maintain async code when higher-order async methods would simplify code.

\n\n

\r\n
\r\n
const async = require('async')\r\nconst fs = require('fs-promise')\r\nconst pify = require('pify')\r\n\r\nasync function getFilePaths() {\r\n    return Promise.resolve([\r\n        './package.json',\r\n        './package-lock.json',\r\n    ]);\r\n}\r\n\r\nasync function printFiles () {\r\n  const files = await getFilePaths()\r\n\r\n  await pify(async.eachSeries)(files, async (file) => {  // <-- run in series\r\n  // await pify(async.each)(files, async (file) => {  // <-- run in parallel\r\n    const contents = await fs.readFile(file, 'utf8')\r\n    console.log(contents)\r\n  })\r\n  console.log('HAMBONE')\r\n}\r\n\r\nprintFiles().then(() => {\r\n    console.log('HAMBUNNY')\r\n})\r\n// ORDER OF LOGS:\r\n// package.json contents\r\n// package-lock.json contents\r\n// HAMBONE\r\n// HAMBUNNY\r\n```
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca6", + "creator": "Babakness", + "createdAt": 1519792868000, + "text": "

Using Task, futurize, and a traversable List, you can simply do

\n\n
async function printFiles() {\n  const files = await getFiles();\n\n  List(files).traverse( Task.of, f => readFile( f, 'utf-8'))\n    .fork( console.error, console.log)\n}\n
\n\n

Here is how you'd set this up

\n\n
import fs from 'fs';\nimport { futurize } from 'futurize';\nimport Task from 'data.task';\nimport { List } from 'immutable-ext';\n\nconst future = futurizeP(Task)\nconst readFile = future(fs.readFile)\n
\n\n

Another way to have structured the desired code would be

\n\n
const printFiles = files => \n  List(files).traverse( Task.of, fn => readFile( fn, 'utf-8'))\n    .fork( console.error, console.log)\n
\n\n

Or perhaps even more functionally oriented

\n\n
// 90% of encodings are utf-8, making that use case super easy is prudent\n\n// handy-library.js\nexport const readFile = f =>\n  future(fs.readFile)( f, 'utf-8' )\n\nexport const arrayToTaskList = list => taskFn => \n  List(files).traverse( Task.of, taskFn ) \n\nexport const readFiles = files =>\n  arrayToTaskList( files, readFile )\n\nexport const printFiles = files => \n  readFiles(files).fork( console.error, console.log)\n
\n\n

Then from the parent function

\n\n
async function main() {\n  /* awesome code with side-effects before */\n  printFiles( await getFiles() );\n  /* awesome code with side-effects after */\n}\n
\n\n

If you really wanted more flexibility in encoding, you could just do this (for fun, I'm using the proposed Pipe Forward operator )

\n\n
import { curry, flip } from 'ramda'\n\nexport const readFile = fs.readFile \n  |> future,\n  |> curry,\n  |> flip\n\nexport const readFileUtf8 = readFile('utf-8')\n
\n\n

PS - I didn't try this code on the console, might have some typos... \"straight freestyle, off the top of the dome!\" as the 90s kids would say. :-p

\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90ca7", + "creator": "Matt", + "createdAt": 1521731476000, + "text": "

Here are some forEachAsync prototypes. Note you'll need to await them:

\n\n
Array.prototype.forEachAsync = async function (fn) {\n    for (let t of this) { await fn(t) }\n}\n\nArray.prototype.forEachAsyncParallel = async function (fn) {\n    await Promise.all(this.map(fn));\n}\n
\n\n

Note while you may include this in your own code, you should not include this in libraries you distribute to others (to avoid polluting their globals).

\n", + "upvotes": 56, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283a082fcc3049e926f5", + "creator": "Damien Romito", + "createdAt": 1622647944000, + "text": "usage : await myArray. forEachAsyncParallel( async (item) => { await myAsyncFunction(item) })", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90ca8", + "creator": "Timothy Zorn", + "createdAt": 1522093738000, + "text": "\n\n

Instead of Promise.all in conjunction with Array.prototype.map (which does not guarantee the order in which the Promises are resolved), I use Array.prototype.reduce, starting with a resolved Promise:

\n\n
async function printFiles () {\n  const files = await getFilePaths();\n\n  await files.reduce(async (promise, file) => {\n    // This line will wait for the last async function to finish.\n    // The first iteration uses an already resolved Promise\n    // so, it will immediately continue.\n    await promise;\n    const contents = await fs.readFile(file, 'utf8');\n    console.log(contents);\n  }, Promise.resolve());\n}\n
\n", + "upvotes": 287, + "upvoterUsernames": [], + "downvotes": 121, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283a082fcc3049e926f7", + "creator": "parrker9", + "createdAt": 1522270138000, + "text": "This works perfectly, thank you so much. Could you explain what is happening here with Promise.resolve() and await promise;?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e926f9", + "creator": "GollyJer", + "createdAt": 1528503873000, + "text": "This is pretty cool. Am I right in thinking the files will be read in order and not all at once?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e926fa", + "creator": "Timothy Zorn", + "createdAt": 1559235113000, + "text": "@Shay, You mean sequential, not synchronous. This is still asynchronous - if other things are scheduled, they will run in between the iterations here.", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e926fc", + "creator": "user7075574", + "createdAt": 1580350789000, + "text": "Is there a way to let this apparently fast when you have an Express page that needs a list of resources to render...?", + "upvotes": 54, + "upvoterUsernames": [], + "downvotes": 54, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e926fe", + "creator": "Remi Mélisson", + "createdAt": 1616078480000, + "text": "my favorite option, I'd love to see a native Promise.chain() like all()", + "upvotes": 31, + "upvoterUsernames": [], + "downvotes": 31, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e92700", + "creator": "Bigyan Devkota", + "createdAt": 1627595081000, + "text": "This works perfectly -- thanks, here the important step is await promise; that's what does the heavy load", + "upvotes": 73, + "upvoterUsernames": [], + "downvotes": 73, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e92702", + "creator": "Andrew Smith", + "createdAt": 1644711126000, + "text": "This is extremely clever, I love it.", + "upvotes": 18, + "upvoterUsernames": [], + "downvotes": 18, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90ca9", + "creator": "Cisco", + "createdAt": 1529061470000, + "text": "

With ES2018, you are able to greatly simplify all of the above answers to:

\n
async function printFiles () {\n  const files = await getFilePaths()\n\n  for await (const contents of files.map(file => fs.readFile(file, 'utf8'))) {\n    console.log(contents)\n  }\n}\n
\n

See spec: proposal-async-iteration

\n

Simplified:

\n
  for await (const results of array) {\n    await longRunningTask()\n  }\n  console.log('I will wait')\n
\n
\n

2018-09-10: This answer has been getting a lot of attention recently, please see Axel Rauschmayer's blog post for further information about asynchronous iteration.

\n", + "upvotes": 760, + "upvoterUsernames": [], + "downvotes": 284, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283a082fcc3049e92704", + "creator": "Vadim Shvetsov", + "createdAt": 1547732091000, + "text": "How we delegates files array to the fs.readFile here? It tooks from iterable?", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e92706", + "creator": "jib", + "createdAt": 1613656363000, + "text": "This answer has the same issue as the OP: It accesses all files in parallel. The serialized printing of results merely hides it.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f3283a082fcc3049e92708", + "creator": "user13548229", + "createdAt": 1628959214000, + "text": "Less characters does not mean it is simpler. This is mostly convoluted and unreadable.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90caa", + "creator": "Scott Rudiger", + "createdAt": 1529600147000, + "text": "

Similar to Antonio Val's p-iteration, an alternative npm module is async-af:

\n\n
const AsyncAF = require('async-af');\nconst fs = require('fs-promise');\n\nfunction printFiles() {\n  // since AsyncAF accepts promises or non-promises, there's no need to await here\n  const files = getFilePaths();\n\n  AsyncAF(files).forEach(async file => {\n    const contents = await fs.readFile(file, 'utf8');\n    console.log(contents);\n  });\n}\n\nprintFiles();\n
\n\n

Alternatively, async-af has a static method (log/logAF) that logs the results of promises:

\n\n
const AsyncAF = require('async-af');\nconst fs = require('fs-promise');\n\nfunction printFiles() {\n  const files = getFilePaths();\n\n  AsyncAF(files).forEach(file => {\n    AsyncAF.log(fs.readFile(file, 'utf8'));\n  });\n}\n\nprintFiles();\n
\n\n

However, the main advantage of the library is that you can chain asynchronous methods to do something like:

\n\n
const aaf = require('async-af');\nconst fs = require('fs-promise');\n\nconst printFiles = () => aaf(getFilePaths())\n  .map(file => fs.readFile(file, 'utf8'))\n  .forEach(file => aaf.log(file));\n\nprintFiles();\n
\n\n

async-af

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cab", + "creator": "Beau", + "createdAt": 1552433489000, + "text": "

Currently the Array.forEach prototype property doesn't support async operations, but we can create our own poly-fill to meet our needs.

\n\n
// Example of asyncForEach Array poly-fill for NodeJs\n// file: asyncForEach.js\n// Define asynForEach function \nasync function asyncForEach(iteratorFunction){\n  let indexer = 0\n  for(let data of this){\n    await iteratorFunction(data, indexer)\n    indexer++\n  }\n}\n// Append it as an Array prototype property\nArray.prototype.asyncForEach = asyncForEach\nmodule.exports = {Array}\n
\n\n

And that's it! You now have an async forEach method available on any arrays that are defined after these to operations.

\n\n

Let's test it...

\n\n
// Nodejs style\n// file: someOtherFile.js\n\nconst readline = require('readline')\nArray = require('./asyncForEach').Array\nconst log = console.log\n\n// Create a stream interface\nfunction createReader(options={prompt: '>'}){\n  return readline.createInterface({\n    input: process.stdin\n    ,output: process.stdout\n    ,prompt: options.prompt !== undefined ? options.prompt : '>'\n  })\n}\n// Create a cli stream reader\nasync function getUserIn(question, options={prompt:'>'}){\n  log(question)\n  let reader = createReader(options)\n  return new Promise((res)=>{\n    reader.on('line', (answer)=>{\n      process.stdout.cursorTo(0, 0)\n      process.stdout.clearScreenDown()\n      reader.close()\n      res(answer)\n    })\n  })\n}\n\nlet questions = [\n  `What's your name`\n  ,`What's your favorite programming language`\n  ,`What's your favorite async function`\n]\nlet responses = {}\n\nasync function getResponses(){\n// Notice we have to prepend await before calling the async Array function\n// in order for it to function as expected\n  await questions.asyncForEach(async function(question, index){\n    let answer = await getUserIn(question)\n    responses[question] = answer\n  })\n}\n\nasync function main(){\n  await getResponses()\n  log(responses)\n}\nmain()\n// Should prompt user for an answer to each question and then \n// log each question and answer as an object to the terminal\n
\n\n

We could do the same for some of the other array functions like map...

\n\n
async function asyncMap(iteratorFunction){\n  let newMap = []\n  let indexer = 0\n  for(let data of this){\n    newMap[indexer] = await iteratorFunction(data, indexer, this)\n    indexer++\n  }\n  return newMap\n}\n\nArray.prototype.asyncMap = asyncMap\n
\n\n

... and so on :)

\n\n

Some things to note:

\n\n\n", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cad", + "creator": "PranavKAndro", + "createdAt": 1574627514000, + "text": "

Today I came across multiple solutions for this. Running the async await functions in the forEach Loop. By building the wrapper around we can make this happen.

\n\n

More detailed explanation on how it works internally, for the native forEach and why it is not able to make a async function call and other details on the various methods are provided in link here

\n\n

The multiple ways through which it can be done and they are as follows,

\n\n

Method 1 : Using the wrapper.

\n\n
await (()=>{\n     return new Promise((resolve,reject)=>{\n       items.forEach(async (item,index)=>{\n           try{\n               await someAPICall();\n           } catch(e) {\n              console.log(e)\n           }\n           count++;\n           if(index === items.length-1){\n             resolve('Done')\n           }\n         });\n     });\n    })();\n
\n\n

Method 2: Using the same as a generic function of Array.prototype

\n\n

Array.prototype.forEachAsync.js

\n\n
if(!Array.prototype.forEachAsync) {\n    Array.prototype.forEachAsync = function (fn){\n      return new Promise((resolve,reject)=>{\n        this.forEach(async(item,index,array)=>{\n            await fn(item,index,array);\n            if(index === array.length-1){\n                resolve('done');\n            }\n        })\n      });\n    };\n  }\n
\n\n

Usage :

\n\n
require('./Array.prototype.forEachAsync');\n\nlet count = 0;\n\nlet hello = async (items) => {\n\n// Method 1 - Using the Array.prototype.forEach \n\n    await items.forEachAsync(async () => {\n         try{\n               await someAPICall();\n           } catch(e) {\n              console.log(e)\n           }\n        count++;\n    });\n\n    console.log(\"count = \" + count);\n}\n\nsomeAPICall = () => {\n    return new Promise((resolve, reject) => {\n        setTimeout(() => {\n            resolve(\"done\") // or reject('error')\n        }, 100);\n    })\n}\n\nhello(['', '', '', '']); // hello([]) empty array is also be handled by default\n
\n\n

Method 3 :

\n\n

Using Promise.all

\n\n
  await Promise.all(items.map(async (item) => {\n        await someAPICall();\n        count++;\n    }));\n\n    console.log(\"count = \" + count);\n
\n\n

Method 4 : Traditional for loop or modern for loop

\n\n
// Method 4 - using for loop directly\n\n// 1. Using the modern for(.. in..) loop\n   for(item in items){\n\n        await someAPICall();\n        count++;\n    }\n\n//2. Using the traditional for loop \n\n    for(let i=0;i<items.length;i++){\n\n        await someAPICall();\n        count++;\n    }\n\n\n    console.log(\"count = \" + count);\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283a082fcc3049e9270d", + "creator": "PranavKAndro", + "createdAt": 1574747842000, + "text": "In certain conditions where its not possible it will be helpful. Also error handling is done by forEach api by default so no issues. Its taken care !", + "upvotes": 423, + "upvoterUsernames": [], + "downvotes": 423, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cac", + "creator": "master_dodo", + "createdAt": 1558908529000, + "text": "

Bergi's solution works nicely when fs is promise based.\nYou can use bluebird, fs-extra or fs-promise for this.

\n\n

However, solution for node's native fs libary is as follows:

\n\n
const result = await Promise.all(filePaths\n    .map( async filePath => {\n      const fileContents = await getAssetFromCache(filePath, async function() {\n\n        // 1. Wrap with Promise    \n        // 2. Return the result of the Promise\n        return await new Promise((res, rej) => {\n          fs.readFile(filePath, 'utf8', function(err, data) {\n            if (data) {\n              res(data);\n            }\n          });\n        });\n      });\n\n      return fileContents;\n    }));\n
\n\n

Note:\nrequire('fs') compulsorily takes function as 3rd arguments, otherwise throws error:

\n\n
TypeError [ERR_INVALID_CALLBACK]: Callback must be a function\n
\n", + "upvotes": 14, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cae", + "creator": "gsaandy", + "createdAt": 1575219595000, + "text": "

Just adding to the original answer

\n\n\n\n
async function printFiles() {\n  const files = await getFilePaths();\n  const fileReadPromises = [];\n\n  const readAndLogFile = async filePath => {\n    const contents = await fs.readFile(file, \"utf8\");\n    console.log(contents);\n    return contents;\n  };\n\n  files.forEach(file => {\n    fileReadPromises.push(readAndLogFile(file));\n  });\n\n  await Promise.all(fileReadPromises);\n}\n\n
\n\n\n\n
async function printFiles() {\n  const files = await getFilePaths();\n\n  for (let i = 0; i < files.length; i++) {\n    const file = files[i];\n    const contents = await fs.readFile(file, \"utf8\");\n    console.log(contents);\n  }\n}\n\n
\n", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90caf", + "creator": "Oliver Dixon", + "createdAt": 1587057527000, + "text": "

This solution is also memory-optimized so you can run it on 10,000's of data items and requests. Some of the other solutions here will crash the server on large data sets.

\n

In TypeScript:

\n
export async function asyncForEach<T>(array: Array<T>, callback: (item: T, index: number) => Promise<void>) {\n        for (let index = 0; index < array.length; index++) {\n            await callback(array[index], index);\n        }\n    }\n
\n

How to use?

\n
await asyncForEach(receipts, async (eachItem) => {\n    await ...\n})\n
\n", + "upvotes": 30, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283a082fcc3049e92711", + "creator": "JulienRioux", + "createdAt": 1647107439000, + "text": "Thanks, nice solution!!", + "upvotes": 188, + "upvoterUsernames": [], + "downvotes": 188, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb0", + "creator": "richytong", + "createdAt": 1590008228000, + "text": "

You can use Array.prototype.forEach, but async/await is not so compatible. This is because the promise returned from an async callback expects to be resolved, but Array.prototype.forEach does not resolve any promises from the execution of its callback. So then, you can use forEach, but you'll have to handle the promise resolution yourself.

\n\n

Here is a way to read and print each file in series using Array.prototype.forEach

\n\n
async function printFilesInSeries () {\n  const files = await getFilePaths()\n\n  let promiseChain = Promise.resolve()\n  files.forEach((file) => {\n    promiseChain = promiseChain.then(() => {\n      fs.readFile(file, 'utf8').then((contents) => {\n        console.log(contents)\n      })\n    })\n  })\n  await promiseChain\n}\n
\n\n

Here is a way (still using Array.prototype.forEach) to print the contents of files in parallel

\n\n
async function printFilesInParallel () {\n  const files = await getFilePaths()\n\n  const promises = []\n  files.forEach((file) => {\n    promises.push(\n      fs.readFile(file, 'utf8').then((contents) => {\n        console.log(contents)\n      })\n    )\n  })\n  await Promise.all(promises)\n}\n
\n", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283b082fcc3049e92714", + "creator": "Mark Odey", + "createdAt": 1590779016000, + "text": "The first senario is ideal for loops that needs to be ran in serie and you cant use for of", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb1", + "creator": "Wojciech Maj", + "createdAt": 1605782702000, + "text": "

If you'd like to iterate over all elements concurrently:

\n
async function asyncForEach(arr, fn) {\n  await Promise.all(arr.map(fn));\n}\n
\n

If you'd like to iterate over all elements non-concurrently (e.g. when your mapping function has side effects or running mapper over all array elements at once would be too resource costly):

\n

Option A: Promises

\n
function asyncForEachStrict(arr, fn) {\n  return new Promise((resolve) => {\n    arr.reduce(\n      (promise, cur, idx) => promise\n        .then(() => fn(cur, idx, arr)),\n      Promise.resolve(),\n    ).then(() => resolve());\n  });\n}\n
\n

Option B: async/await

\n
async function asyncForEachStrict(arr, fn) {\n  for (let idx = 0; idx < arr.length; idx += 1) {\n    const cur = arr[idx];\n\n    await fn(cur, idx, arr);\n  }\n}\n
\n", + "upvotes": 161, + "upvoterUsernames": [], + "downvotes": 161, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cb2", + "creator": "Adam Zerner", + "createdAt": 1608753076000, + "text": "

As other answers have mentioned, you're probably wanting it to be executed in sequence rather in parallel. Ie. run for first file, wait until it's done, then once it's done run for second file. That's not what will happen.

\n

I think it's important to address why this doesn't happen.

\n

Think about how forEach works. I can't find the source, but I presume it works something like this:

\n
const forEach = (arr, cb) => {\n  for (let i = 0; i < arr.length; i++) {\n    cb(arr[i]);\n  }\n};\n
\n

Now think about what happens when you do something like this:

\n
forEach(files, async logFile(file) {\n  const contents = await fs.readFile(file, 'utf8');\n  console.log(contents);\n});\n
\n

Inside forEach's for loop we're calling cb(arr[i]), which ends up being logFile(file). The logFile function has an await inside it, so maybe the for loop will wait for this await before proceeding to i++?

\n

No, it won't. Confusingly, that's not how await works. From the docs:

\n
\n

An await splits execution flow, allowing the caller of the async function to resume execution. After the await defers the continuation of the async function, execution of subsequent statements ensues. If this await is the last expression executed by its function execution continues by returning to the function's caller a pending Promise for completion of the await's function and resuming execution of that caller.

\n
\n

So if you have the following, the numbers won't be logged before "b":

\n
const delay = (ms) => {\n  return new Promise((resolve) => {\n    setTimeout(resolve, ms);\n  });\n};\n\nconst logNumbers = async () => {\n  console.log(1);\n  await delay(2000);\n  console.log(2);\n  await delay(2000);\n  console.log(3);\n};\n\nconst main = () => {\n  console.log("a");\n  logNumbers();\n  console.log("b");\n};\n\nmain();\n
\n

Circling back to forEach, forEach is like main and logFile is like logNumbers. main won't stop just because logNumbers does some awaiting, and forEach won't stop just because logFile does some awaiting.

\n", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cb3", + "creator": "yeah22", + "createdAt": 1611784626000, + "text": "

A simple drop-in solution for replacing a forEach() await loop that is not working is replacing forEach with map and adding Promise.all( to the beginning.

\n

For example:

\n

await y.forEach(async (x) => {

\n

to

\n

await Promise.all(y.map(async (x) => {

\n

An extra ) is needed at the end.

\n", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283b082fcc3049e92718", + "creator": "srmark", + "createdAt": 1633604785000, + "text": "Not quite the same. Promise.all will run all the promises concurrently. A for loop is meant to be sequential.", + "upvotes": 107, + "upvoterUsernames": [], + "downvotes": 107, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb5", + "creator": "Johnz", + "createdAt": 1617369529000, + "text": "

It is not good to call an asynchronous method from a loop. This is because each loop iteration will be delayed until the entire asynchronous operation completes. That is not very performant. It also averts the advantages of parallelization benefits of async/await.

\n

A better solution would be to create all promises at once, then get access to the results using Promise.all(). Otherwise, each successive operation will not start until the previous one has completed.

\n

Consequently, the code may be refactored as follows;

\n
const printFiles = async () => {\n  const files = await getFilePaths();\n  const results = [];\n  files.forEach((file) => {\n    results.push(fs.readFile(file, 'utf8'));\n  });\n  const contents = await Promise.all(results);\n  console.log(contents);\n}\n
\n", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cb4", + "creator": "ChenZeTong", + "createdAt": 1613007956000, + "text": "

Here is a great example for using async in forEach loop.

\n

Write your own asyncForEach

\n
async function asyncForEach(array, callback) {  \n    for (let index = 0; index < array.length; index++) {\n        await callback(array[index], index, array)\n    }\n}\n
\n

You can use it like this

\n
await asyncForEach(array, async function(item,index,array){\n     //await here\n   }\n)\n
\n", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cb6", + "creator": "krupesh Anadkat", + "createdAt": 1621399275000, + "text": "

Picture worth 1000 words - For Sequential Approach Only

\n
\n

Background : I was in similar situation last night. I used async function as foreach argument. The result was un-predictable. When I did testing for my code 3 times, it ran without issues 2 times and failed 1 time. (something weird)

\n

Finally I got my head around & did some scratch pad testing.

\n

Scenario 1 - How un-sequential it can get with async in foreach

\n

\"enter

\n
const getPromise = (time) => { \n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      resolve(`Promise resolved for ${time}s`)\n    }, time)\n  })\n}\n\nconst main = async () => {\n  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]\n  console.log('Before For Each Loop')\n\n  myPromiseArray.forEach(async (element, index) => {\n    let result = await element;\n    console.log(result);\n  })\n\n  console.log('After For Each Loop')\n}\n\nmain();\n
\n

Scenario 2 - Using for - of loop as @Bergi above suggested

\n

\"enter

\n
const getPromise = (time) => { \n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      resolve(`Promise resolved for ${time}s`)\n    }, time)\n  })\n}\n\nconst main = async () => {\n  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]\n  console.log('Before For Each Loop')\n\n  // AVOID USING THIS\n  // myPromiseArray.forEach(async (element, index) => {\n  //   let result = await element;\n  //   console.log(result);\n  // })\n\n  // This works well\n  for (const element of myPromiseArray) {\n    let result = await element;\n    console.log(result)\n  }\n\n  console.log('After For Each Loop')\n}\n\nmain();\n
\n

If you are little old school like me, you could simply use the classic for loop, that works too :)

\n
const getPromise = (time) => { \n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      resolve(`Promise resolved for ${time}s`)\n    }, time)\n  })\n}\n\nconst main = async () => {\n  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]\n  console.log('Before For Each Loop')\n\n  // AVOID USING THIS\n  // myPromiseArray.forEach(async (element, index) => {\n  //   let result = await element;\n  //   console.log(result);\n  // })\n\n  // This works well too - the classic for loop :)\n  for (let i = 0; i < myPromiseArray.length; i++) {\n    const result = await myPromiseArray[i];\n    console.log(result);\n  }\n\n  console.log('After For Each Loop')\n}\n\nmain();\n
\n

I hope this helps someone, good day, cheers!

\n", + "upvotes": 55, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283b082fcc3049e9271d", + "creator": "close", + "createdAt": 1646221795000, + "text": "I suggest using the phrase 'Before/After Loop' would make it less confusing when it's not a 'For Each Loop'.", + "upvotes": 143, + "upvoterUsernames": [], + "downvotes": 143, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb7", + "creator": "Matt Janssen", + "createdAt": 1635324348000, + "text": "

If you can't use async/await (IE11, old packer, etc.) then you can try this recursive function. I used fetch as my asynchronous call, but you could use any function that returns a promise.

\n
var urlsToGet = ['https://google.com', 'https://yahoo.com'];\n\nfetchOneAtATime(urlsToGet);\n\nfunction fetchOneAtATime(urls) {\n    if (urls.length === 0) {\n        return;\n    }\n    fetch(urls[0]).finally(() => fetchOneAtATime(urls.slice(1)));\n}\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283b082fcc3049e92720", + "creator": "Bergi", + "createdAt": 1635324601000, + "text": "Why use finally instead of then? This will ignore errors, unlike async/await", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f3283b082fcc3049e92721", + "creator": "Matt Janssen", + "createdAt": 1635338229000, + "text": "This would be if you want to do every fetch, regardless of the success of preceding calls. Good idea on the empty check and not mutating the array! ✔", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb9", + "creator": "João Pimentel Ferreira", + "createdAt": 1641766298000, + "text": "

This does not use async/await as the OP requested and only works if you are in the back-end with NodeJS. Although it still may be helpful for some people, because the example given by OP is to read file contents, and normally you do file reading in the backend.

\n

Fully asynchronous and non-blocking:

\n
const fs = require("fs")\nconst async = require("async")\n\nconst obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"}\nconst configs = {}\n\nasync.forEachOf(obj, (value, key, callback) => {\n    fs.readFile(__dirname + value, "utf8", (err, data) => {\n        if (err) return callback(err)\n        try {\n            configs[key] = JSON.parse(data);\n        } catch (e) {\n            return callback(e)\n        }\n        callback()\n    });\n}, err => {\n    if (err) console.error(err.message)\n    // configs is now a map of JSON data\n    doSomethingWith(configs)\n})\n
\n", + "upvotes": 37, + "upvoterUsernames": [], + "downvotes": 37, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283b082fcc3049e92723", + "creator": "Bergi", + "createdAt": 1641773686000, + "text": "Also, why do you say require("async").forEach only works in nodejs?", + "upvotes": 5451, + "upvoterUsernames": [], + "downvotes": 5451, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321ca082fcc3049e90cb8", + "creator": "Jatin Saradgikar", + "createdAt": 1635432199000, + "text": "

You can use the async.forEach loop from the async package:

\n
async.forEach(dataToLoop(array), async(data, cb) => {\n                variable = await MongoQuery;\n            }, function(err) {\n                console.log(err);  \n              })\n            })\n            .catch((err)=>{\n              console.log(err);\n            })\n
\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cba", + "creator": "Yilmaz", + "createdAt": 1643743737000, + "text": "
\n
files.forEach(async (file) => {\n    const contents = await fs.readFile(file, 'utf8')\n})\n
\n
\n

The issue is, the promise returned by the iteration function is ignored by forEach(). As a result, all the fs.readFile functions\nare invoked in the same round of the event loop, which means they are started in parallel, not in sequential, and the execution continues immediately after invoking forEach(), without\nwaiting for all the fs.readFile operations to complete. Since forEach does not wait for each promise to resolve, the loop actually finishes iterating before promises are resolved. You may end up trying to access values that are not available yet.

\n", + "upvotes": 43, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321ca082fcc3049e90cbb", + "creator": "tenbits", + "createdAt": 1649250626000, + "text": "

In 2022 I would still advise using external libraries to handle all this async flow. I've created the module alot🔗 for similar things.

\n

Your example would be:

\n
import fs from 'fs-promise'\nimport alot from 'alot'\n\nasync function printFiles () {\n    const files = await getFilePaths() // Assume this works fine\n\n    await alot(files)\n        .forEachAsync(async file => {\n            let content = await fs.readFile(file, 'utf8');\n            console.log(content);\n        })\n        .toArrayAsync({ threads: 4 });\n    }\n}\nprintFiles()\n
\n

For simple examples surely the async for..of would do the job, but as soon the task is more complicated you have to use some utility for this.

\n

Alot has dozens of other methods that you can chain, like mapAsync, filterAsync, groupAsync, etc.

\n

As an example:

\n\n
\n
import fs from 'fs-promise'\nimport alot from 'alot'\nimport axios from 'axios'\nimport { File } from 'atma-io'\n\nlet paths = await getFilePaths();\nlet products = await alot(paths)\n    .mapAsync(async path => await File.readAsync<IProductMeta>(path))\n    .mapAsync(async meta => await axios.get(`${server}/api/product/${meta.productId}`))\n    .mapAsync(resp => resp.data)\n    .filterAsync(product => product.price > 100)\n    .sortBy(product => product.price, 'asc')\n    .takeAsync(50)\n    .toArrayAsync({ threads: 5, errors: 'include' });\n
\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f3283c082fcc3049e92725", + "creator": "Bergi", + "createdAt": 1649253133000, + "text": "What is threads: 4? JS doesn't have threads", + "upvotes": 10, + "upvoterUsernames": [], + "downvotes": 10, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 1, + "commentItems": [ + { + "_id": "62f321c9082fcc3049e90c5b", + "creator": "Bergi", + "createdAt": 1596999529000, + "text": "@KernelMode The forEach method is the higher-order function here", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 17, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 1526174, + "uvac": 1526203 + } + }, + { + "_id": "62f321bb082fcc3049e8feed", + "title": "Can (a== 1 && a ==2 && a==3) ever evaluate to true?", + "title-lowercase": "can (a== 1 && a ==2 && a==3) ever evaluate to true?", + "creator": "Dimpu Aravind Buddha", + "createdAt": 1516047647000, + "status": "open", + "text": "
\n

Moderator note: Please resist the urge to edit the code or remove this notice. The pattern of whitespace may be part of the question and therefore should not be tampered with unnecessarily. If you are in the \"whitespace is insignificant\" camp, you should be able to accept the code as is.

\n
\n\n

Is it ever possible that (a== 1 && a ==2 && a==3) could evaluate to true in JavaScript?

\n\n

This is an interview question asked by a major tech company. It happened two weeks back, but I'm still trying to find the answer. I know we never write such code in our day-to-day job, but I'm curious.

\n", + "upvotes": 3820, + "upvoterUsernames": [], + "downvotes": 1199, + "downvoterUsernames": [], + "hasAcceptedAnswer": true, + "views": 397418, + "answers": 25, + "answerItems": [ + { + "_id": "62f321c8082fcc3049e90bd8", + "creator": "jontro", + "createdAt": 1516048639000, + "text": "

It can be accomplished using the following in the global scope. For nodejs use global instead of window in the code below.

\n\n

\r\n
\r\n
var val = 0;\r\nObject.defineProperty(window, 'a', {\r\n  get: function() {\r\n    return ++val;\r\n  }\r\n});\r\nif (a == 1 && a == 2 && a == 3) {\r\n  console.log('yay');\r\n}
\r\n
\r\n
\r\n

\n\n

This answer abuses the implicit variables provided by the global scope in the execution context by defining a getter to retrieve the variable.

\n", + "upvotes": 266, + "upvoterUsernames": [], + "downvotes": 70, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ae082fcc3049e9253e", + "creator": "jontro", + "createdAt": 1516049230000, + "text": "@jfriend00 you mean if you placed var a; somewhere?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92540", + "creator": "jontro", + "createdAt": 1516051787000, + "text": "@jfriend00 well sure. Not sure that it would add much more value in combination with the other already answers. Will update the answer", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bd6", + "creator": "Jonas Wilms", + "createdAt": 1516048508000, + "text": "

IT IS POSSIBLE!

\n\n

\r\n
\r\n
var i = 0;\r\n\r\nwith({\r\n  get a() {\r\n    return ++i;\r\n  }\r\n}) {\r\n  if (a == 1 && a == 2 && a == 3)\r\n    console.log(\"wohoo\");\r\n}
\r\n
\r\n
\r\n

\n\n

This uses a getter inside of a with statement to let a evaluate to three different values.

\n\n

... this still does not mean this should be used in real code...

\n\n

Even worse, this trick will also work with the use of ===.

\n\n

\r\n
\r\n
  var i = 0;\r\n\r\n  with({\r\n    get a() {\r\n      return ++i;\r\n    }\r\n  }) {\r\n    if (a !== a)\r\n      console.log(\"yep, this is printed.\");\r\n  }
\r\n
\r\n
\r\n

\n", + "upvotes": 812, + "upvoterUsernames": [], + "downvotes": 170, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ae082fcc3049e92541", + "creator": "jfriend00", + "createdAt": 1516049091000, + "text": "@Pointy - And, I program in strict mode where with is not allowed.", + "upvotes": 12, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92543", + "creator": "J_rite", + "createdAt": 1516101435000, + "text": "@Pointy in the accepted answer they do something similar without the with so it can happen", + "upvotes": 13, + "upvoterUsernames": [], + "downvotes": 6, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92545", + "creator": "Jonas Wilms", + "createdAt": 1516106895000, + "text": "@jorrit no one would use ==. And === prevents the accepted answer", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92547", + "creator": "Ori Shalom", + "createdAt": 1516793959000, + "text": "Notice, with this solution the condition will evaluate to true even if you will use ===!", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92549", + "creator": "Isaac Abramowitz", + "createdAt": 1532547563000, + "text": "I definitely find this the most unique, creative and non-sneaky way (weird spacing) of solving this!", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9254b", + "creator": "Pac0", + "createdAt": 1538734254000, + "text": "I added example with triple equals in the answer. (And yes, you can do it without with to lure the == checks)", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bd7", + "creator": "Kevin B", + "createdAt": 1516048515000, + "text": "

If you take advantage of how == works, you could simply create an object with a custom toString (or valueOf) function that changes what it returns each time it is used such that it satisfies all three conditions.

\n\n

\r\n
\r\n
const a = {\r\n  i: 1,\r\n  toString: function () {\r\n    return a.i++;\r\n  }\r\n}\r\n\r\nif(a == 1 && a == 2 && a == 3) {\r\n  console.log('Hello World!');\r\n}
\r\n
\r\n
\r\n

\n\n
\n\n

The reason this works is due to the use of the loose equality operator. When using loose equality, if one of the operands is of a different type than the other, the engine will attempt to convert one to the other. In the case of an object on the left and a number on the right, it will attempt to convert the object to a number by first calling valueOf if it is callable, and failing that, it will call toString. I used toString in this case simply because it's what came to mind, valueOf would make more sense. If I instead returned a string from toString, the engine would have then attempted to convert the string to a number giving us the same end result, though with a slightly longer path.

\n", + "upvotes": 6333, + "upvoterUsernames": [], + "downvotes": 2885, + "downvoterUsernames": [], + "accepted": true, + "commentItems": [ + { + "_id": "62f327ae082fcc3049e9254d", + "creator": "Sterling Archer", + "createdAt": 1516048647000, + "text": "Could you achieve this by altering the implied valueOf() operation?", + "upvotes": 82, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9254f", + "creator": "Kevin B", + "createdAt": 1516048699000, + "text": "Yes, valueOf works in place of toString for the same reason", + "upvotes": 66, + "upvoterUsernames": [], + "downvotes": 19, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92551", + "creator": "Kevin B", + "createdAt": 1516101463000, + "text": "fortunately, no.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92552", + "creator": "AncientSwordRage", + "createdAt": 1516102142000, + "text": "If i and 1, 2, and 3 are numbers, why is it calling toString?", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92554", + "creator": "rkosegi", + "createdAt": 1516133032000, + "text": "Just curious, isn't such expression a == 1 && a == 2 && a == 3 subject to JIT optimization?", + "upvotes": 36, + "upvoterUsernames": [], + "downvotes": 36, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92556", + "creator": "Cruncher", + "createdAt": 1516150824000, + "text": "@rkosegi well doesn't seem to be optimized in this scenario. If it was compiled it would likely optimize the expression to false", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92558", + "creator": "ryenus", + "createdAt": 1516151943000, + "text": "why not using this? i.e.,: toString: function() { return this.i++; }", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9255a", + "creator": "Jonas Wilms", + "createdAt": 1516170099000, + "text": "@rkosegi no, its not possible in js. Code behaves as defined in the specs, even if optimized", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9255b", + "creator": "Jonas Wilms", + "createdAt": 1516204785000, + "text": "@zxxc that wont work with this answer, but with the three following", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9255c", + "creator": "Onur", + "createdAt": 1516530299000, + "text": "Does order of evaluation always from left to right? Asking because depending on the js engine, should we write the i++ statement as i--?", + "upvotes": 446, + "upvoterUsernames": [], + "downvotes": 446, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e9255e", + "creator": "hungrykoala", + "createdAt": 1516779483000, + "text": "What is the value of a when it's being compared to the numbers?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92560", + "creator": "Kevin B", + "createdAt": 1516780667000, + "text": "when it is finally compared to the number, it is the number it is comparing to. but, a contains an object.", + "upvotes": 661, + "upvoterUsernames": [], + "downvotes": 661, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bd9", + "creator": "Jeff", + "createdAt": 1516079694000, + "text": "

I couldn't resist - the other answers are undoubtedly true, but you really can't walk past the following code:

\n\n

\r\n
\r\n
var aᅠ = 1;\r\nvar a = 2;\r\nvar ᅠa = 3;\r\nif(aᅠ==1 && a== 2 &&ᅠa==3) {\r\n    console.log(\"Why hello there!\")\r\n}
\r\n
\r\n
\r\n

\n\n

Note the weird spacing in the if statement (that I copied from your question). It is the half-width Hangul (that's Korean for those not familiar) which is an Unicode space character that is not interpreted by ECMA script as a space character - this means that it is a valid character for an identifier. Therefore there are three completely different variables, one with the Hangul after the a, one with it before and the last one with just a. Replacing the space with _ for readability, the same code would look like this:

\n\n

\r\n
\r\n
var a_ = 1;\r\nvar a = 2;\r\nvar _a = 3;\r\nif(a_==1 && a== 2 &&_a==3) {\r\n    console.log(\"Why hello there!\")\r\n}
\r\n
\r\n
\r\n

\n\n

Check out the validation on Mathias' variable name validator. If that weird spacing was actually included in their question, I feel sure that it's a hint for this kind of answer.

\n\n

Don't do this. Seriously.

\n\n

Edit: It has come to my attention that (although not allowed to start a variable) the Zero-width joiner and Zero-width non-joiner characters are also permitted in variable names - see Obfuscating JavaScript with zero-width characters - pros and cons?.

\n\n

This would look like the following:

\n\n

\r\n
\r\n
var a= 1;\r\nvar a‍= 2; //one zero-width character\r\nvar a‍‍= 3; //two zero-width characters (or you can use the other one)\r\nif(a==1&&a‍==2&&a‍‍==3) {\r\n    console.log(\"Why hello there!\")\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 3706, + "upvoterUsernames": [], + "downvotes": 1581, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327ae082fcc3049e92561", + "creator": "Taemyr", + "createdAt": 1516101861000, + "text": "Could you use U+2060 or similar instead?", + "upvotes": 527, + "upvoterUsernames": [], + "downvotes": 527, + "downvoterUsernames": [] + }, + { + "_id": "62f327ae082fcc3049e92563", + "creator": "Jeff", + "createdAt": 1516213722000, + "text": "@holger ha! There might be only one variable “a”, and 1, 2 and 3 could all be defined as variables with the same value as a. Good times...", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bdc", + "creator": "Nina Scholz", + "createdAt": 1516115513000, + "text": "

Alternatively, you could use a class for it and an instance for the check.

\n\n

\r\n
\r\n
function A() {\r\n    var value = 0;\r\n    this.valueOf = function () { return ++value; };\r\n}\r\n\r\nvar a = new A;\r\n\r\nif (a == 1 && a == 2 && a == 3) {\r\n    console.log('bingo!');\r\n}
\r\n
\r\n
\r\n

\n\n

EDIT

\n\n

Using ES6 classes it would look like this

\n\n

\r\n
\r\n
class A {\r\n  constructor() {\r\n    this.value = 0;\r\n    this.valueOf();\r\n  }\r\n  valueOf() {\r\n    return this.value++;\r\n  };\r\n}\r\n\r\nlet a = new A;\r\n\r\nif (a == 1 && a == 2 && a == 3) {\r\n  console.log('bingo!');\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 167, + "upvoterUsernames": [], + "downvotes": 32, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e92565", + "creator": "Dave C", + "createdAt": 1516207879000, + "text": "just function A() {value = 0; at the start?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bdb", + "creator": "Patrick Dark", + "createdAt": 1516102638000, + "text": "

This is also possible using a series of self-overwriting getters:

\n\n

(This is similar to jontro's solution, but doesn't require a counter variable.)

\n\n

\r\n
\r\n
(() => {\r\n    \"use strict\";\r\n    Object.defineProperty(this, \"a\", {\r\n        \"get\": () => {\r\n            Object.defineProperty(this, \"a\", {\r\n                \"get\": () => {\r\n                    Object.defineProperty(this, \"a\", {\r\n                        \"get\": () => {\r\n                            return 3;\r\n                        }\r\n                    });\r\n                    return 2;\r\n                },\r\n                configurable: true\r\n            });\r\n            return 1;\r\n        },\r\n        configurable: true\r\n    });\r\n    if (a == 1 && a == 2 && a == 3) {\r\n        document.body.append(\"Yes, it’s possible.\");\r\n    }\r\n})();
\r\n
\r\n
\r\n

\n", + "upvotes": 187, + "upvoterUsernames": [], + "downvotes": 39, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e92568", + "creator": "Makyen", + "createdAt": 1516122276000, + "text": "Note that the approach of using a getter also works with ===, not just ==.", + "upvotes": 64, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9256a", + "creator": "Roy Tinker", + "createdAt": 1516735852000, + "text": "This solution relies on this being the global object inside the body of the arrow function.", + "upvotes": 22, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9256b", + "creator": "Patrick Roberts", + "createdAt": 1516813029000, + "text": "@Midnightas I wouldn't categorize any other answers as "pyramid code".", + "upvotes": 1256, + "upvoterUsernames": [], + "downvotes": 1256, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9256c", + "creator": "Johannes", + "createdAt": 1531128349000, + "text": "Note this also works with arbitrary order, doesn't it? Like, (a == 3 && a == 2 && a == 1)?", + "upvotes": 183, + "upvoterUsernames": [], + "downvotes": 183, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bde", + "creator": "Draco18s no longer trusts SE", + "createdAt": 1516128292000, + "text": "

I don't see this answer already posted, so I'll throw this one into the mix too. This is similar to Jeff's answer with the half-width Hangul space.

\n\n

\r\n
\r\n
var a = 1;\r\nvar a = 2;\r\nvar а = 3;\r\nif(a == 1 && a == 2 && а == 3) {\r\n    console.log(\"Why hello there!\")\r\n}
\r\n
\r\n
\r\n

\n\n

You might notice a slight discrepancy with the second one, but the first and third are identical to the naked eye. All 3 are distinct characters:

\n\n

a - Latin lower case A
\n - Full Width Latin lower case A
\nа - Cyrillic lower case A

\n\n

The generic term for this is \"homoglyphs\": different unicode characters that look the same. Typically hard to get three that are utterly indistinguishable, but in some cases you can get lucky. A, Α, А, and Ꭺ would work better (Latin-A, Greek Alpha, Cyrillic-A, and Cherokee-A respectively; unfortunately the Greek and Cherokee lower-case letters are too different from the Latin a: α,, and so doesn't help with the above snippet).

\n\n

There's an entire class of Homoglyph Attacks out there, most commonly in fake domain names (eg. wikipediа.org (Cyrillic) vs wikipedia.org (Latin)), but it can show up in code as well; typically referred to as being underhanded (as mentioned in a comment, [underhanded] questions are now off-topic on PPCG, but used to be a type of challenge where these sorts of things would show up). I used this website to find the homoglyphs used for this answer.

\n", + "upvotes": 165, + "upvoterUsernames": [], + "downvotes": 34, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e9256f", + "creator": "user743382", + "createdAt": 1516148233000, + "text": ""Slight discrepancy" is not how I would call that.", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92571", + "creator": "Draco18s no longer trusts SE", + "createdAt": 1516149433000, + "text": "@hvd Entirely depends on your font rendering. This is what I see.", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bdd", + "creator": "MonkeyZeus", + "createdAt": 1516123640000, + "text": "

Rule number one of interviews; never say impossible.

\n

No need for hidden character trickery.

\n

\r\n
\r\n
window.__defineGetter__( 'a', function(){\n    if( typeof i !== 'number' ){\n        // define i in the global namespace so that it's not lost after this function runs\n        i = 0;\n    }\n    return ++i;\n});\n\nif( a == 1 && a == 2 && a == 3 ){\n    console.log( 'Oh dear, what have we done?' );\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 116, + "upvoterUsernames": [], + "downvotes": 41, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bda", + "creator": "ocomfd", + "createdAt": 1516083680000, + "text": "

If it is asked if it is possible (not MUST), it can ask \"a\" to return a random number. It would be true if it generates 1, 2, and 3 sequentially.

\n\n

\r\n
\r\n
with({\r\n  get a() {\r\n    return Math.floor(Math.random()*4);\r\n  }\r\n}){\r\n  for(var i=0;i<1000;i++){\r\n    if (a == 1 && a == 2 && a == 3){\r\n      console.log(\"after \" + (i+1) + \" trials, it becomes true finally!!!\");\r\n      break;\r\n    }\r\n  }\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 497, + "upvoterUsernames": [], + "downvotes": 226, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e92574", + "creator": "Piyin", + "createdAt": 1516631586000, + "text": "But what if it takes more than 1000 trials?", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92576", + "creator": "Skeets", + "createdAt": 1516641590000, + "text": "@Piyin If it takes more than 1000 trials you win a prize!", + "upvotes": 15, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92578", + "creator": "KyleFairns", + "createdAt": 1517306952000, + "text": "Lowest: 1, Highest: 412.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be0", + "creator": "Kos", + "createdAt": 1516131348000, + "text": "

When you can't do anything without regular expressions:

\n\n

\r\n
\r\n
var a = {\r\n  r: /\\d/g, \r\n  valueOf: function(){\r\n    return this.r.exec(123)[0]\r\n  }\r\n}\r\n\r\nif (a == 1 && a == 2 && a == 3) {\r\n    console.log(\"!\")\r\n}
\r\n
\r\n
\r\n

\n\n

It works because of custom valueOf method that is called when Object compared with primitive (such as Number). Main trick is that a.valueOf returns new value every time because it's calling exec on regular expression with g flag, which causing updating lastIndex of that regular expression every time match is found. So first time this.r.lastIndex == 0, it matches 1 and updates lastIndex: this.r.lastIndex == 1, so next time regex will match 2 and so on.

\n", + "upvotes": 304, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e9257b", + "creator": "Abdillah", + "createdAt": 1516157107000, + "text": "I see, so the this.r regex object remember the state / index. Thanks!", + "upvotes": 154, + "upvoterUsernames": [], + "downvotes": 154, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9257d", + "creator": "Bergi", + "createdAt": 1516292447000, + "text": "I would recommend to pass a string to exec though, not an integer to be stringified.", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9257e", + "creator": "Aleksey Solovey", + "createdAt": 1517234558000, + "text": "use regex and now you have two problems", + "upvotes": 89, + "upvoterUsernames": [], + "downvotes": 89, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bdf", + "creator": "Ben Aubin", + "createdAt": 1516129462000, + "text": "

This one uses the defineProperty with a nice side-effect causing global variable!

\n\n

\r\n
\r\n
var _a = 1\r\n\r\nObject.defineProperty(this, \"a\", {\r\n  \"get\": () => {\r\n    return _a++;\r\n  },\r\n  configurable: true\r\n});\r\n\r\nconsole.log(a)\r\nconsole.log(a)\r\nconsole.log(a)
\r\n
\r\n
\r\n

\n", + "upvotes": 23, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e92581", + "creator": "Nina Scholz", + "createdAt": 1516131257000, + "text": "you could use a closure over a: get: (a => () => ++a)(0), no global necessary.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92582", + "creator": "Ben Aubin", + "createdAt": 1516131332000, + "text": "@NinaScholz sure, but we're talking about bad practices here - just let me have this :D", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be1", + "creator": "Gustavo Rodríguez", + "createdAt": 1516132619000, + "text": "

Actually the answer to the first part of the question is \"Yes\" in every programming language. For example, this is in the case of C/C++:

\n\n
#define a   (b++)\nint b = 1;\nif (a ==1 && a== 2 && a==3) {\n    std::cout << \"Yes, it's possible!\" << std::endl;\n} else {\n    std::cout << \"it's impossible!\" << std::endl;\n}\n
\n", + "upvotes": 46, + "upvoterUsernames": [], + "downvotes": 20, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327af082fcc3049e92584", + "creator": "LukeS", + "createdAt": 1516138156000, + "text": "Most languages will allow you to overwrite the operator", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92586", + "creator": "CAD97", + "createdAt": 1516145823000, + "text": "And you can do it in Java by using reflection and messing up the integer cache.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92588", + "creator": "Tas", + "createdAt": 1516146362000, + "text": "I very much doubt this works in C, given the use of std::cout", + "upvotes": 4, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9258a", + "creator": "Gustavo Rodríguez", + "createdAt": 1516149913000, + "text": "@tas: I know. You should change it to printf. But you got the idea, I think.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9258c", + "creator": "Jason Carr", + "createdAt": 1516167880000, + "text": "Can't do it in languages that woudn't support mutation in that spot, e.g. nothing comparable is available in haskell", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e9258e", + "creator": "Eric Duminil", + "createdAt": 1516184872000, + "text": "@CAD97: I tried it. It works with Integer.valueOf(1). Can it also work with primitive ints?", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92590", + "creator": "Uyghur Lives Matter", + "createdAt": 1516199352000, + "text": "The question is asking about JavaScript, not C++.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92591", + "creator": "Gustavo Rodríguez", + "createdAt": 1516203411000, + "text": "@cpburnz As I said, I'm talking about the first part of the question. I think the second part has been sufficiently answered.", + "upvotes": 11, + "upvoterUsernames": [], + "downvotes": 11, + "downvoterUsernames": [] + }, + { + "_id": "62f327af082fcc3049e92592", + "creator": "Tim", + "createdAt": 1516203601000, + "text": "not only does the question explicitly mention javascript, it is also tagged as such. C/C++ is completely irrelevant", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be2", + "creator": "Théophile", + "createdAt": 1516132811000, + "text": "

Here's another variation, using an array to pop off whatever values you want.

\n\n

\r\n
\r\n
const a = {\r\n  n: [3,2,1],\r\n  toString: function () {\r\n    return a.n.pop();\r\n  }\r\n}\r\n\r\nif(a == 1 && a == 2 && a == 3) {\r\n  console.log('Yes');\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 74, + "upvoterUsernames": [], + "downvotes": 33, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90be3", + "creator": "BaggersIO", + "createdAt": 1516138396000, + "text": "

Okay, another hack with generators:

\n\n

\r\n
\r\n
const value = function* () {\r\n  let i = 0;\r\n  while(true) yield ++i;\r\n}();\r\n\r\nObject.defineProperty(this, 'a', {\r\n  get() {\r\n    return value.next().value;\r\n  }\r\n});\r\n\r\nif (a === 1 && a === 2 && a === 3) {\r\n  console.log('yo!');\r\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 60, + "upvoterUsernames": [], + "downvotes": 28, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90be5", + "creator": "mehulmpt", + "createdAt": 1516174777000, + "text": "

This is possible in case of variable a being accessed by, say 2 web workers through a SharedArrayBuffer as well as some main script. The possibility is low, but it is possible that when the code is compiled to machine code, the web workers update the variable a just in time so the conditions a==1, a==2 and a==3 are satisfied.

\n\n

This can be an example of race condition in multi-threaded environment provided by web workers and SharedArrayBuffer in JavaScript.

\n\n

Here is the basic implementation of above:

\n\n

main.js

\n\n
// Main Thread\n\nconst worker = new Worker('worker.js')\nconst modifiers = [new Worker('modifier.js'), new Worker('modifier.js')] // Let's use 2 workers\nconst sab = new SharedArrayBuffer(1)\n\nmodifiers.forEach(m => m.postMessage(sab))\nworker.postMessage(sab)\n
\n\n

worker.js

\n\n
let array\n\nObject.defineProperty(self, 'a', {\n  get() {\n    return array[0]\n  }\n});\n\naddEventListener('message', ({data}) => {\n    array = new Uint8Array(data)\n    let count = 0\n    do {\n        var res = a == 1 && a == 2 && a == 3\n        ++count\n    } while(res == false) // just for clarity. !res is fine\n    console.log(`It happened after ${count} iterations`)\n    console.log('You should\\'ve never seen this')\n})\n
\n\n

modifier.js

\n\n
addEventListener('message' , ({data}) => {\n    setInterval( () => {\n        new Uint8Array(data)[0] = Math.floor(Math.random()*3) + 1\n    })\n})\n
\n\n

On my MacBook Air, it happens after around 10 billion iterations on the first attempt:

\n\n

\"enter

\n\n

Second attempt:

\n\n

\"enter

\n\n

As I said, the chances will be low, but given enough time, it'll hit the condition.

\n\n

Tip: If it takes too long on your system. Try only a == 1 && a == 2 and change Math.random()*3 to Math.random()*2. Adding more and more to list drops the chance of hitting.

\n", + "upvotes": 382, + "upvoterUsernames": [], + "downvotes": 178, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327b0082fcc3049e92595", + "creator": "qntm", + "createdAt": 1516451102000, + "text": "@mehulmpt I wasn't aware that a web worker could mutate variables on the main thread. Can you provide some example code?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e92597", + "creator": "mehulmpt", + "createdAt": 1516478106000, + "text": "@qntm variable is not in the main thread. everything is accessing shared memory", + "upvotes": 214, + "upvoterUsernames": [], + "downvotes": 214, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e92599", + "creator": "Bergi", + "createdAt": 1516684100000, + "text": "Uh, how do you access a variable through a SharedArrayBuffer? Even though multiple workers can share buffers, they cannot share variables.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259a", + "creator": "Ry-", + "createdAt": 1516687928000, + "text": "This isn’t possible (and is kind of why SharedArrayBuffer exists).", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259b", + "creator": "mehulmpt", + "createdAt": 1516712117000, + "text": "@Bergi check the example above", + "upvotes": 112, + "upvoterUsernames": [], + "downvotes": 112, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259c", + "creator": "mehulmpt", + "createdAt": 1516712125000, + "text": "@qntm check the example above", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259d", + "creator": "qntm", + "createdAt": 1516723601000, + "text": "I think this is now an elaborate wrapper around @jontro's answer.", + "upvotes": 5, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259e", + "creator": "mehulmpt", + "createdAt": 1536402510000, + "text": "@ErwinBolwidt SharedArrayBuffer is back", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e9259f", + "creator": "gotofritz", + "createdAt": 1592953308000, + "text": "Sorry but if it works once in 1e10 iterations it means it has a 0.000000001% chances of working - i.e., it does NOT work", + "upvotes": 811, + "upvoterUsernames": [], + "downvotes": 811, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925a1", + "creator": "mehulmpt", + "createdAt": 1600009208000, + "text": "@gotofritz looks like somebody never worked with high load systems", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be7", + "creator": "Preda7or", + "createdAt": 1516198696000, + "text": "

Same, but different, but still same (can be \"tested\" multiple times):

\n\n

\r\n
\r\n
const a = { valueOf: () => this.n = (this.n || 0) % 3 + 1}\r\n    \r\nif(a == 1 && a == 2 && a == 3) {\r\n  console.log('Hello World!');\r\n}\r\n\r\nif(a == 1 && a == 2 && a == 3) {\r\n  console.log('Hello World!');\r\n}
\r\n
\r\n
\r\n

\n\n

My idea started from how Number object type equation works.

\n", + "upvotes": 47, + "upvoterUsernames": [], + "downvotes": 21, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90be4", + "creator": "Frank W. Zammetti", + "createdAt": 1516139857000, + "text": "

Honestly though, whether there is a way for it to evaluate to true or not (and as others have shown, there are multiple ways), the answer I'd be looking for, speaking as someone who has conducted hundreds of interviews, would be something along the lines of:

\n\n

\"Well, maybe yes under some weird set of circumstances that aren't immediately obvious to me... but if I encountered this in real code then I would use common debugging techniques to figure out how and why it was doing what it was doing and then immediately refactor the code to avoid that situation... but more importantly: I would absolutely NEVER write that code in the first place because that is the very definition of convoluted code, and I strive to never write convoluted code\".

\n\n

I guess some interviewers would take offense to having what is obviously meant to be a very tricky question called out, but I don't mind developers who have an opinion, especially when they can back it up with reasoned thought and can dovetail my question into a meaningful statement about themselves.

\n", + "upvotes": 127, + "upvoterUsernames": [], + "downvotes": 60, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327b0082fcc3049e925a3", + "creator": "TylerH", + "createdAt": 1516203869000, + "text": "This doesn't answer the question.", + "upvotes": 17, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925a4", + "creator": "Jonas Wilms", + "createdAt": 1516204430000, + "text": "The sad thing about this answer is that a 1rep user answered that yesterday and got 2 downvotes causing him to delete this question.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 3, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925a5", + "creator": "Phil", + "createdAt": 1516526035000, + "text": "@TylerH the question asks if the code could evaluate to true, not how.", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925a6", + "creator": "Alfred Armstrong", + "createdAt": 1516727109000, + "text": "In other words, you never maintain code you didn't write yourself? Good luck with that attitude in interviews.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be6", + "creator": "georg", + "createdAt": 1516189067000, + "text": "

Example without getters or valueOf:

\n\n

\r\n
\r\n
a = [1,2,3];\r\na.join = a.shift;\r\nconsole.log(a == 1 && a == 2 && a == 3);
\r\n
\r\n
\r\n

\n\n

This works because == invokes toString which calls .join for Arrays.

\n\n

Another solution, using Symbol.toPrimitive which is an ES6 equivalent of toString/valueOf:

\n\n

\r\n
\r\n
let i = 0;\r\nlet a = { [Symbol.toPrimitive]: () => ++i };\r\n\r\nconsole.log(a == 1 && a == 2 && a == 3);
\r\n
\r\n
\r\n

\n", + "upvotes": 974, + "upvoterUsernames": [], + "downvotes": 417, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f327b0082fcc3049e925a9", + "creator": "Jonas Wilms", + "createdAt": 1516203568000, + "text": "without valueOf, well... its more indirect but basically the same thing.", + "upvotes": 19, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925aa", + "creator": "Andrew", + "createdAt": 1516222020000, + "text": "This makes so much sense it almost feels useful.", + "upvotes": 16, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f327b0082fcc3049e925ab", + "creator": "Justin Morgan", + "createdAt": 1544819301000, + "text": "a.join = a.shift is genius. Evil genius, but genius nonetheless.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90bee", + "creator": "Nguyễn Văn Phong", + "createdAt": 1618654965000, + "text": "

As we already know that the secret of loose equality operator (==) will try to convert both values to a common type. As a result, some functions will be invoked.

\n
\n

ToPrimitive(A) attempts to convert its object argument to a primitive\nvalue, by invoking varying sequences of A.toString and A.valueOf\nmethods on A.

\n
\n

So as other answers using Symbol.toPrimitive, .toString, .valueOf from integer. I would suggest the solution using an array with Array.pop like this.\n

\r\n
\r\n
let a = { array: [3, 2, 1], toString: () => a.array.pop() };\n\nif(a == 1 && a == 2 && a == 3) {\n  console.log('Hello World!');\n}
\r\n
\r\n
\r\n

\n

In this way, we can work with text like this

\n

\r\n
\r\n
let a = { array: [\"World\", \"Hello\"], toString: () => a.array.pop() };\n\nif(a == \"Hello\" && a == \"World\") {\n  console.log('Hello World!');\n}
\r\n
\r\n
\r\n

\n", + "upvotes": 1, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90be9", + "creator": "Salman A", + "createdAt": 1516272713000, + "text": "

This is an inverted version of @Jeff's answer* where a hidden character (U+115F, U+1160 or U+3164) is used to create variables that look like 1, 2 and 3.

\n\n

\r\n
\r\n
var  a = 1;\r\nvar ᅠ1 = a;\r\nvar ᅠ2 = a;\r\nvar ᅠ3 = a;\r\nconsole.log( a ==ᅠ1 && a ==ᅠ2 && a ==ᅠ3 );
\r\n
\r\n
\r\n

\n\n

* That answer can be simplified by using zero width non-joiner (U+200C) and zero width joiner (U+200D). Both of these characters are allowed inside identifiers but not at the beginning:

\n\n

\r\n
\r\n
var a = 1;\r\nvar a‌ = 2;\r\nvar a‍ = 3;\r\nconsole.log(a == 1 && a‌ == 2 && a‍ == 3);\r\n\r\n/****\r\nvar a = 1;\r\nvar a\\u200c = 2;\r\nvar a\\u200d = 3;\r\nconsole.log(a == 1 && a\\u200c == 2 && a\\u200d == 3);\r\n****/
\r\n
\r\n
\r\n

\n\n

Other tricks are possible using the same idea e.g. by using Unicode variation selectors to create variables that look exactly alike (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

\n", + "upvotes": 103, + "upvoterUsernames": [], + "downvotes": 22, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bea", + "creator": "IceCreamYou", + "createdAt": 1516400373000, + "text": "

Using Proxies:

\n\n
var a = new Proxy({ i: 0 }, {\n    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],\n});\nconsole.log(a == 1 && a == 2 && a == 3);\n
\n\n

Proxies basically pretend to be a target object (the first parameter), but intercept operations on the target object (in this case the \"get property\" operation) so that there is an opportunity to do something other than the default object behavior. In this case the \"get property\" action is called on a when == coerces its type in order to compare it to each number. This happens:

\n\n
    \n
  1. We create a target object, { i: 0 }, where the i property is our counter
  2. \n
  3. We create a Proxy for the target object and assign it to a
  4. \n
  5. For each a == comparison, a's type is coerced to a primitive value
  6. \n
  7. This type coercion results in calling a[Symbol.toPrimitive]() internally
  8. \n
  9. The Proxy intercepts getting the a[Symbol.toPrimitive] function using the \"get handler\"
  10. \n
  11. The Proxy's \"get handler\" checks that the property being gotten is Symbol.toPrimitive, in which case it increments and then returns the counter from the target object: ++target.i. If a different property is being retrieved, we just fall back to returning the default property value, target[name]
  12. \n
\n\n

So:

\n\n
var a = ...; // a.valueOf == target.i == 0\na == 1 && // a == ++target.i == 1\na == 2 && // a == ++target.i == 2\na == 3    // a == ++target.i == 3\n
\n\n

As with most of the other answers, this only works with a loose equality check (==), because strict equality checks (===) do not do type coercion that the Proxy can intercept.

\n", + "upvotes": 34, + "upvoterUsernames": [], + "downvotes": 4, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c4082fcc3049e92e82", + "creator": "Ry-", + "createdAt": 1516688024000, + "text": "There isn’t a point in using a proxy for this, though – defining Symbol.toPrimitive in the same way on an object would work just as well.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + } + ] + }, + { + "_id": "62f321c8082fcc3049e90be8", + "creator": "Omar Alshaker", + "createdAt": 1516202495000, + "text": "

An ECMAScript 6 answer that makes use of Symbols:

\n\n
const a = {value: 1};\na[Symbol.toPrimitive] = function() { return this.value++ };\nconsole.log((a == 1 && a == 2 && a == 3));\n
\n\n

Due to == usage, JavaScript is supposed to coerce a into something close to the second operand (1, 2, 3 in this case). But before JavaScript tries to figure coercing on its own, it tries to call Symbol.toPrimitive. If you provide Symbol.toPrimitive JavaScript would use the value your function returns. If not, JavaScript would call valueOf.

\n", + "upvotes": 39, + "upvoterUsernames": [], + "downvotes": 14, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bed", + "creator": "Jonathan Kuhl", + "createdAt": 1541357741000, + "text": "

By overriding valueOf in a class declaration, it can be done:

\n\n
class Thing {\n    constructor() {\n        this.value = 1;\n    }\n\n    valueOf() {\n        return this.value++;\n    }\n}\n\nconst a = new Thing();\n\nif(a == 1 && a == 2 && a == 3) {\n    console.log(a);\n}\n
\n\n

What happens is that valueOf is called in each comparison operator. On the first one, a will equal 1, on the second, a will equal 2, and so on and so forth, because each time valueOf is called, the value of a is incremented.

\n\n

Therefore the console.log will fire and output (in my terminal anyways) Thing: { value: 4}, indicating the conditional was true.

\n", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90bec", + "creator": "gafi", + "createdAt": 1516552277000, + "text": "

I think this is the minimal code to implement it:

\n\n

\r\n
\r\n
i=0,a={valueOf:()=>++i}\r\n\r\nif (a == 1 && a == 2 && a == 3) {\r\n  console.log('Mind === Blown');\r\n}
\r\n
\r\n
\r\n

\n\n

Creating a dummy object with a custom valueOf that increments a global variable i on each call. 23 characters!

\n", + "upvotes": 33, + "upvoterUsernames": [], + "downvotes": 9, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [] + }, + { + "_id": "62f321c8082fcc3049e90beb", + "creator": "Ramin Bateni", + "createdAt": 1516499970000, + "text": "

Yes, it is possible! 😎

\n\n

» JavaScript

\n\n

\r\n
\r\n
if‌=()=>!0;\r\nvar a = 9;\r\n\r\nif‌(a==1 && a== 2 && a==3)\r\n{\r\n    document.write(\"<h1>Yes, it is possible!😎</h1>\")\r\n}
\r\n
\r\n
\r\n

\n\n

The above code is a short version (thanks to @Forivin for its note in comments) and the following code is original:

\n\n

\r\n
\r\n
var a = 9;\r\n\r\nif‌(a==1 && a== 2 && a==3)\r\n{\r\n    //console.log(\"Yes, it is possible!😎\")\r\n    document.write(\"<h1>Yes, it is possible!😎</h1>\")\r\n}\r\n\r\n//--------------------------------------------\r\n\r\nfunction if‌(){return true;}
\r\n
\r\n
\r\n

\n\n
\n

If you just see top side of my code and run it you say WOW, how?

\n \n

So I think it is enough to say Yes, it is possible to someone that said to\n you: Nothing is impossible

\n \n

Trick: I used a hidden character after if to make a function that its name is similar to if. In JavaScript we can not override keywords so I forced to use this way. It is a fake if, but it works for you in this case!

\n
\n\n
\n\n

» C#

\n\n

Also I wrote a C# version (with increase property value technic):

\n\n
static int _a;\npublic static int a => ++_a;\n\npublic static void Main()\n{\n    if(a==1 && a==2 && a==3)\n    {\n        Console.WriteLine(\"Yes, it is possible!😎\");\n    }\n}\n
\n\n

Live Demo

\n", + "upvotes": 215, + "upvoterUsernames": [], + "downvotes": 97, + "downvoterUsernames": [], + "accepted": false, + "commentItems": [ + { + "_id": "62f329c4082fcc3049e92e89", + "creator": "muthukumar selvaraj", + "createdAt": 1516558618000, + "text": "function if<200c> { } how it is working why not working with function if<123c>{}", + "upvotes": 0, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f329c4082fcc3049e92e8b", + "creator": "Ramin Bateni", + "createdAt": 1516606718000, + "text": "@SaurabhChandraPatel, Yes KevinB answer is nice. My JS answer has trick, also I updated my answer with a C# version (It has no trick) ;)", + "upvotes": 8, + "upvoterUsernames": [], + "downvotes": 8, + "downvoterUsernames": [] + }, + { + "_id": "62f329c4082fcc3049e92e8d", + "creator": "Forivin", + "createdAt": 1516723328000, + "text": "The function declaration could be even shorter. if‌=()=>!0", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c4082fcc3049e92e8f", + "creator": "Cerbrus", + "createdAt": 1516723479000, + "text": "Why on earth did you use document.write? That's a surefire way not to get hired regardless of the rest of the answer.", + "upvotes": 6, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f329c4082fcc3049e92e90", + "creator": "Clearer", + "createdAt": 1516778375000, + "text": "@RAM Are you saying that the UN is largely irrelevant to the world as is? Blasphemy!", + "upvotes": 88, + "upvoterUsernames": [], + "downvotes": 88, + "downvoterUsernames": [] + }, + { + "_id": "62f329c4082fcc3049e92e92", + "creator": "Peter Mortensen", + "createdAt": 1521658732000, + "text": "What do you mean by "with increase property value technic" (it seems to be incomprehensible)?", + "upvotes": 83, + "upvoterUsernames": [], + "downvotes": 83, + "downvoterUsernames": [] + } + ] + } + ], + "comments": 4, + "commentItems": [ + { + "_id": "62f321c8082fcc3049e90bab", + "creator": "tomsmeding", + "createdAt": 1516144511000, + "text": "To the people that apparently voted to cloae this as too broad: is that a dig at Javascript, saying that there are too many valid answers?", + "upvotes": 149, + "upvoterUsernames": [], + "downvotes": 30, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bac", + "creator": "Dirk Vollmar", + "createdAt": 1516179482000, + "text": "@TrevorBoydSmith: This would work in many languages besides JavaScript, e.g. using operator overloading with side-effects in C# or Java.", + "upvotes": 3, + "upvoterUsernames": [], + "downvotes": 0, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bad", + "creator": "R.. GitHub STOP HELPING ICE", + "createdAt": 1516207415000, + "text": "This question does have some merit in that it calls for an understanding of the awfulness and danger of the language you'll be working with.", + "upvotes": 9, + "upvoterUsernames": [], + "downvotes": 1, + "downvoterUsernames": [] + }, + { + "_id": "62f321c8082fcc3049e90bae", + "creator": "Solomon Ucko", + "createdAt": 1547908892000, + "text": "@DirkVollmar Operator overloading in Java?", + "upvotes": 2, + "upvoterUsernames": [], + "downvotes": 2, + "downvoterUsernames": [] + } + ], + "sorter": { + "uvc": 401242, + "uvac": 401267 + } + } + ], + "users": [ + { + "_id": "62f2513ea5cd2c51555ff323", + "username": "Ikem Krueger", + "email": "Ikem Krueger@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 186, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513ea5cd2c51555ff325", + "username": "Vaulstein", + "email": "Vaulstein@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 18319, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513ea5cd2c51555ff327", + "username": "Sunil Kumar", + "email": "Sunil Kumar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b5"]] + }, + { + "_id": "62f2513ea5cd2c51555ff329", + "username": "Felix Kling", + "email": "Felix Kling@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 763849, + "questionIds": ["62f321bb082fcc3049e8feb4"], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1b"]] + }, + { + "_id": "62f2513fa5cd2c51555ff32b", + "username": "Peter Mortensen", + "email": "Peter Mortensen@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 30240, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff32d", + "username": "Braiam", + "email": "Braiam@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4324, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff32f", + "username": "Navid", + "email": "Navid@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 100, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff331", + "username": "Muhammad Umer", + "email": "Muhammad Umer@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 16276, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff333", + "username": "dr.dimitru", + "email": "dr.dimitru@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2555, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90345"]] + }, + { + "_id": "62f2513fa5cd2c51555ff335", + "username": "eaorak", + "email": "eaorak@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5334, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff337", + "username": "Константин Ван", + "email": "Константин Ван@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10508, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902bc"]] + }, + { + "_id": "62f2513fa5cd2c51555ff339", + "username": "Navruzbek Noraliev", + "email": "Navruzbek Noraliev@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 698, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff33b", + "username": "Rolando Niubó", + "email": "Rolando Niubó@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 593, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff33d", + "username": "Pashan", + "email": "Pashan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 118, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff33f", + "username": "mplungjan", + "email": "mplungjan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 157406, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90713"]] + }, + { + "_id": "62f2513fa5cd2c51555ff341", + "username": "Ali NajafZadeh", + "email": "Ali NajafZadeh@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff343", + "username": "Subodh Ghulaxe", + "email": "Subodh Ghulaxe@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 17977, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff345", + "username": "HumanInDisguise", + "email": "HumanInDisguise@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1225, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff347", + "username": "Alireza", + "email": "Alireza@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 9807, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff367"], + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3b"], + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff65"], + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff99"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd2"], + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a1"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900bc"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f9"], + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90117"], + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013a"], + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90172"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b1"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90218"], + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b4"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90302"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034f"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038d"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c0"], + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904aa"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d7"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90503"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052c"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90578"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d9"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90668"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90699"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f2"], + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e9079b"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907bc"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e0"], + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e9"], + ["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e90901"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90963"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90992"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909ba"], + ["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f9"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae1"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2c"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c52"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d26"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe7"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103f"] + ] + }, + { + "_id": "62f2513fa5cd2c51555ff349", + "username": "divine", + "email": "divine@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4413, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff34b", + "username": "Tal", + "email": "Tal@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 899, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff34d", + "username": "RobG", + "email": "RobG@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 136012, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff34f", + "username": "Gizmo", + "email": "Gizmo@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1828, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff351", + "username": "Mardoxx", + "email": "Mardoxx@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4154, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff353", + "username": "Gras Double", + "email": "Gras Double@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 15148, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f2513fa5cd2c51555ff355", + "username": "sarea", + "email": "sarea@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 216, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054a"]] + }, + { + "_id": "62f25140a5cd2c51555ff357", + "username": "Domino", + "email": "Domino@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5791, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff359", + "username": "Victor", + "email": "Victor@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 12573, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd9"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b98"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d40"] + ] + }, + { + "_id": "62f25140a5cd2c51555ff35b", + "username": "Tim", + "email": "Tim@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6121, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e3"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031e"] + ] + }, + { + "_id": "62f25140a5cd2c51555ff35d", + "username": "Andrew", + "email": "Andrew@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4916, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a66"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb3"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a2"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90379"] + ] + }, + { + "_id": "62f25140a5cd2c51555ff379", + "username": "Gus", + "email": "Gus@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6460, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065f"]] + }, + { + "_id": "62f25140a5cd2c51555ff37b", + "username": "Vivek", + "email": "Vivek@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10354, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff37d", + "username": "user2808054", + "email": "user2808054@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1336, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff37f", + "username": "phuclv", + "email": "phuclv@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 33338, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff381", + "username": "bytefish", + "email": "bytefish@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2096, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906fb"]] + }, + { + "_id": "62f25140a5cd2c51555ff383", + "username": "BillMux", + "email": "BillMux@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 65, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff385", + "username": "Jone Polvora", + "email": "Jone Polvora@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2011, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff387", + "username": "Aidin53", + "email": "Aidin53@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 161, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff389", + "username": "Jagadeesh", + "email": "Jagadeesh@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2590, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff38b", + "username": "Frank Fang", + "email": "Frank Fang@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2005, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff38d", + "username": "Santiago Corredoira", + "email": "Santiago Corredoira@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 45531, + "questionIds": ["62f321bb082fcc3049e8fec5"], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff38f", + "username": "Shubham Kushwah", + "email": "Shubham Kushwah@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 477, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff391", + "username": "alex", + "email": "alex@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 464234, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90706"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b4b"] + ] + }, + { + "_id": "62f25140a5cd2c51555ff394", + "username": "artem", + "email": "artem@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b3"]] + }, + { + "_id": "62f25140a5cd2c51555ff3fc", + "username": "chiccodoro", + "email": "chiccodoro@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 14109, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff3fe", + "username": "Lord Loh.", + "email": "Lord Loh.@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2397, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff400", + "username": "froginvasion", + "email": "froginvasion@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 833, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff402", + "username": "WynandB", + "email": "WynandB@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1347, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff404", + "username": "BTC", + "email": "BTC@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3602, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff406", + "username": "Matt", + "email": "Matt@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3444, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca7"]] + }, + { + "_id": "62f25140a5cd2c51555ff408", + "username": "r3wt", + "email": "r3wt@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4562, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff40a", + "username": "user3275211", + "email": "user3275211@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff40c", + "username": "EscapeNetscape", + "email": "EscapeNetscape@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2734, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff40e", + "username": "Mahi", + "email": "Mahi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1687, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff410", + "username": "soundly_typed", + "email": "soundly_typed@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 37537, + "questionIds": ["62f321bb082fcc3049e8fecd"], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff412", + "username": "OverCoder", + "email": "OverCoder@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1352, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff414", + "username": "Muhammad Ali", + "email": "Muhammad Ali@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 610, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e52"]] + }, + { + "_id": "62f25140a5cd2c51555ff416", + "username": "SwiftNinjaPro", + "email": "SwiftNinjaPro@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 699, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905ab"]] + }, + { + "_id": "62f25140a5cd2c51555ff418", + "username": "Geetanshu Gulati", + "email": "Geetanshu Gulati@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 572, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff41a", + "username": "Himanshu Shekhar", + "email": "Himanshu Shekhar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1078, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25140a5cd2c51555ff41c", + "username": "Omar bakhsh", + "email": "Omar bakhsh@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 856, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90323"], + ["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909fb"] + ] + }, + { + "_id": "62f25140a5cd2c51555ff41e", + "username": "Flimm", + "email": "Flimm@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 118940, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6c5", + "username": "17 of 26", + "email": "17 of 26@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 26772, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f19"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ade"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff6c7", + "username": "annakata", + "email": "annakata@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 72902, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6c9", + "username": "Larry Battle", + "email": "Larry Battle@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8710, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6cb", + "username": "Samih", + "email": "Samih@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1058, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6cd", + "username": "Neta", + "email": "Neta@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 841, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6cf", + "username": "mpen", + "email": "mpen@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 258668, + "questionIds": ["62f321bb082fcc3049e8fed2"], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffcf"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f1"], + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908db"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c46"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff6d2", + "username": "Déjà vu", + "email": "Déjà vu@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 27332, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6d5", + "username": "Rodrigo Leite", + "email": "Rodrigo Leite@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 586, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6d7", + "username": "FiniteLooper", + "email": "FiniteLooper@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 24582, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6d9", + "username": "Alwin Kesler", + "email": "Alwin Kesler@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6db", + "username": "Teepeemm", + "email": "Teepeemm@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4162, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6dd", + "username": "DanMan", + "email": "DanMan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10995, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a30"]] + }, + { + "_id": "62f25141a5cd2c51555ff6df", + "username": "Costa Michailidis", + "email": "Costa Michailidis@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90813"]] + }, + { + "_id": "62f25141a5cd2c51555ff6e1", + "username": "Pawel", + "email": "Pawel@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13528, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90815"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b8"], + ["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c96"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f00"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff6e3", + "username": "MukulSharma", + "email": "MukulSharma@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 231, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6e5", + "username": "S.D.", + "email": "S.D.@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 28904, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6e8", + "username": "BenKoshy", + "email": "BenKoshy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 30401, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6f0", + "username": "Adrien Be", + "email": "Adrien Be@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 18665, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6f2", + "username": "javad bat", + "email": "javad bat@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3260, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6f4", + "username": "Christophe Roussy", + "email": "Christophe Roussy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 15531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6f6", + "username": "onmyway133", + "email": "onmyway133@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 43312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ea"]] + }, + { + "_id": "62f25141a5cd2c51555ff6f8", + "username": "Sebastian Simon", + "email": "Sebastian Simon@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 16827, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6fa", + "username": "Rodrigo Siqueira", + "email": "Rodrigo Siqueira@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1154, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6fc", + "username": "WickyNilliams", + "email": "WickyNilliams@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5110, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff6fe", + "username": "Sebi", + "email": "Sebi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1375, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff700", + "username": "Mark K Cowan", + "email": "Mark K Cowan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1697, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff702", + "username": "iamandrewluca", + "email": "iamandrewluca@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2825, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba4"]] + }, + { + "_id": "62f25141a5cd2c51555ff704", + "username": "vitaly-t", + "email": "vitaly-t@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 22652, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df3"]] + }, + { + "_id": "62f25141a5cd2c51555ff706", + "username": "user13507084", + "email": "user13507084@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff708", + "username": "ZenAtWork", + "email": "ZenAtWork@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 71, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff70a", + "username": "Deepak paramesh", + "email": "Deepak paramesh@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 311, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90881"]] + }, + { + "_id": "62f25141a5cd2c51555ff70c", + "username": "Ryan Walker", + "email": "Ryan Walker@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff70e", + "username": "Jimmy", + "email": "Jimmy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 9566, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25141a5cd2c51555ff711", + "username": "Ravi Makwana", + "email": "Ravi Makwana@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2462, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff6"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b0"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff82b", + "username": "Esko Luontola", + "email": "Esko Luontola@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 71918, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff82a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90041"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff82d", + "username": "siride", + "email": "siride@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 188253, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff82c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90042"], + ["62f321b9082fcc3049e8feab", "62f32a4e082fcc3049e93077"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff82f", + "username": "Ryan McGeary", + "email": "Ryan McGeary@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 230622, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff82e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90043"], + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff6c"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91246"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff831", + "username": "Tom Wadley", + "email": "Tom Wadley@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 122133, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff830"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90044"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa5"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff833", + "username": "Fabien Ménager", + "email": "Fabien Ménager@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 140051, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff832"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90045"], + ["62f321bb082fcc3049e8feb2", "62f321bc082fcc3049e90085"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff835", + "username": "genehack", + "email": "genehack@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 129276, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff834"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90046"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff837", + "username": "rakib_", + "email": "rakib_@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 128163, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff836"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90047"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff839", + "username": "RNA", + "email": "RNA@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 138668, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff838"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90048"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff83b", + "username": "Ryan Lundy", + "email": "Ryan Lundy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 197530, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff83a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90049"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff83d", + "username": "Jon Skeet", + "email": "Jon Skeet@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1348687, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff83c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004a"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9122c"], + ["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fe8"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff83f", + "username": "Gumbo", + "email": "Gumbo@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 624562, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff83e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004b"], + ["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909ec"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91007"], + ["62f321b9082fcc3049e8feab", "62f32a0c082fcc3049e92fc2"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92adb"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff841", + "username": "Greg Hewgill", + "email": "Greg Hewgill@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 901000, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff840"], + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff860"], + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff889"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90071"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91245"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd6"], + ["62f321b9082fcc3049e8feab", "62f32a0c082fcc3049e92fc3"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff843", + "username": "hallski", + "email": "hallski@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 117801, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff842"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004d"], + ["62f321b9082fcc3049e8feab", "62f32a0c082fcc3049e92fc4"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff845", + "username": "Tsvetomir Tsonev", + "email": "Tsvetomir Tsonev@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 104104, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff844"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004e"], + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff11"], + ["62f321b9082fcc3049e8feab", "62f32a0c082fcc3049e92fc5"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff847", + "username": "Charles Salvia", + "email": "Charles Salvia@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 50913, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff846"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9004f"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff849", + "username": "nickf", + "email": "nickf@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 522861, + "questionIds": ["62f321bb082fcc3049e8fed8", "62f321bb082fcc3049e8fee9"], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff848"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90050"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900ae"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff84b", + "username": "Dan Moulding", + "email": "Dan Moulding@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 199819, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff84a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90051"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91227"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff84d", + "username": "hobbs", + "email": "hobbs@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 209621, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff84c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90052"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff84f", + "username": "Jorge E. Cardona", + "email": "Jorge E. Cardona@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 88595, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff84e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90053"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff851", + "username": "Blowsie", + "email": "Blowsie@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 39387, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff850"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90054"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff853", + "username": "Mike Hordecki", + "email": "Mike Hordecki@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 84739, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff852"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90055"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dcf"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff855", + "username": "Daniel Ruoso", + "email": "Daniel Ruoso@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 79240, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff854"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90056"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd0"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff857", + "username": "Michael Mrozek", + "email": "Michael Mrozek@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 162814, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff856"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90057"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd1"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff859", + "username": "dash", + "email": "dash@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 87736, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff858"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90058"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd2"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff85b", + "username": "codaddict", + "email": "codaddict@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 432415, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff85a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90059"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9123c"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd3"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff85d", + "username": "sykora", + "email": "sykora@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 90902, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff85c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005a"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd4"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff85f", + "username": "CB Bailey", + "email": "CB Bailey@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 708983, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff85e"], + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a0"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005b"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007d"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e4"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91226"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd5"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff862", + "username": "Adam Franco", + "email": "Adam Franco@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 76660, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff861"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005d"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd7"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff864", + "username": "Steve Harrison", + "email": "Steve Harrison@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 113822, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff863"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005e"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902cc"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c0"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90641"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd8"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff866", + "username": "Adam Zerner", + "email": "Adam Zerner@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 15796, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff865"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9005f"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f8"], + ["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb2"], + ["62f321b9082fcc3049e8feab", "62f329bb082fcc3049e92dd9"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff868", + "username": "JaredPar", + "email": "JaredPar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 708479, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff867"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90060"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff86a", + "username": "Bart Kiers", + "email": "Bart Kiers@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 161931, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff869"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90061"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff86c", + "username": "Patrick", + "email": "Patrick@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 87466, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff86b"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90062"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff86e", + "username": "Derek Park", + "email": "Derek Park@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 45068, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff86d"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90063"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff870", + "username": "Eli", + "email": "Eli@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 94404, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff86f"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90064"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff872", + "username": "Alex Martelli", + "email": "Alex Martelli@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 818500, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff871"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90065"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e38"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff874", + "username": "Tobi", + "email": "Tobi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 73017, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff873"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90066"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90908"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff876", + "username": "Blair Conrad", + "email": "Blair Conrad@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 222223, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff875"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90067"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff878", + "username": "Mark Harrison", + "email": "Mark Harrison@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 286275, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff877"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90068"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff87a", + "username": "Devin Jeanpierre", + "email": "Devin Jeanpierre@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 88921, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff879"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90069"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff87c", + "username": "sberry", + "email": "sberry@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 123081, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff87b"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006a"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff87e", + "username": "Xian", + "email": "Xian@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 75251, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff87d"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006b"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903cf"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff880", + "username": "user123444555621", + "email": "user123444555621@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 141668, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff87f"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006c"], + ["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908ba"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff882", + "username": "John Rutherford", + "email": "John Rutherford@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10684, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff881"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006d"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90178"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff884", + "username": "tvanfosson", + "email": "tvanfosson@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 511682, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff883"], + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff35e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006e"], + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff6b"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90945"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff886", + "username": "David Segonds", + "email": "David Segonds@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 81565, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff885"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9006f"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff888", + "username": "rslite", + "email": "rslite@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 78427, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff887"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90070"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff88b", + "username": "pycruft", + "email": "pycruft@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 62157, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff88a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90072"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff88d", + "username": "dirvine", + "email": "dirvine@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 54049, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff88c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90073"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff88f", + "username": "Robin Day", + "email": "Robin Day@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 98382, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff88e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90074"], + ["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909eb"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff891", + "username": "kender", + "email": "kender@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 83589, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff890"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90075"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff893", + "username": "Gryphius", + "email": "Gryphius@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 72133, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff892"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90076"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff895", + "username": "ScArcher2", + "email": "ScArcher2@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 82925, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff894"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90077"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff897", + "username": "daveb", + "email": "daveb@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 71115, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff896"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90078"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911df"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a1"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff899", + "username": "Grundlefleck", + "email": "Grundlefleck@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 120359, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff898"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90079"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e0"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff89b", + "username": "David Cournapeau", + "email": "David Cournapeau@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 75614, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff89a"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007a"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e1"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff89d", + "username": "Greg", + "email": "Greg@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 308791, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff89c"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007b"], + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008a"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e2"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904bf"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f16"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f8d"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff89f", + "username": "jAndy", + "email": "jAndy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 224559, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff89e"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007c"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e3"], + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e909ff"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8a2", + "username": "Justin Poliey", + "email": "Justin Poliey@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 16279, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a1"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007e"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e5"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8a4", + "username": "Pascal MARTIN", + "email": "Pascal MARTIN@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 387285, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a3"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e9007f"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e6"], + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff50"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8a6", + "username": "Sean Patrick Floyd", + "email": "Sean Patrick Floyd@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 286085, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a5"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90080"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e7"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8a8", + "username": "jgillich", + "email": "jgillich@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 65098, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a7"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90081"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e8"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8aa", + "username": "baisong", + "email": "baisong@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 53124, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8a9"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90082"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911e9"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8ac", + "username": "harto", + "email": "harto@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 88163, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8ab"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90083"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911ea"] + ] + }, + { + "_id": "62f25141a5cd2c51555ff8ae", + "username": "Marcin Gil", + "email": "Marcin Gil@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 66170, + "questionIds": [], + "answerIds": [ + ["62f2513ca5cd2c51555ff2be", "62f25141a5cd2c51555ff8ad"], + ["62f321b9082fcc3049e8feab", "62f321bc082fcc3049e90084"], + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911eb"] + ] + }, + { + "_id": "62f25142a5cd2c51555ff8f2", + "username": "markasoftware", + "email": "markasoftware@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 11657, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8f4", + "username": "Jayant Pareek", + "email": "Jayant Pareek@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 360, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8f6", + "username": "brandito", + "email": "brandito@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 687, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8f8", + "username": "Darren Griffith", + "email": "Darren Griffith@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3130, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8fa", + "username": "David Baucum", + "email": "David Baucum@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2018, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8fc", + "username": "Madbreaks", + "email": "Madbreaks@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 18280, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff8ff", + "username": "Jay", + "email": "Jay@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 258, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff901", + "username": "Alex", + "email": "Alex@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 32799, + "questionIds": [ + "62f321bb082fcc3049e8fef1", + "62f321bb082fcc3049e8feff", + "62f321bb082fcc3049e8fee3" + ], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d8"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90547"] + ] + }, + { + "_id": "62f25142a5cd2c51555ff903", + "username": "Shahid Hussain Abbasi", + "email": "Shahid Hussain Abbasi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2058, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1f"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6e"] + ] + }, + { + "_id": "62f25142a5cd2c51555ff905", + "username": "Rodrigo Borba", + "email": "Rodrigo Borba@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1176, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff907", + "username": "Nick Craver", + "email": "Nick Craver@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 613174, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff909", + "username": "Daniel Schaffer", + "email": "Daniel Schaffer@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 55190, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff90b", + "username": "DCShannon", + "email": "DCShannon@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2235, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff90d", + "username": "Steve Tolba", + "email": "Steve Tolba@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff90f", + "username": "Ninjakannon", + "email": "Ninjakannon@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3631, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff911", + "username": "Andrew Koster", + "email": "Andrew Koster@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1307, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff913", + "username": "Hendrik", + "email": "Hendrik@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1357, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff915", + "username": "RBoschini", + "email": "RBoschini@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 486, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff917", + "username": "Benj", + "email": "Benj@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1136, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff919", + "username": "Ted", + "email": "Ted@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 104, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff91b", + "username": "Rohit Kumar", + "email": "Rohit Kumar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2038, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25142a5cd2c51555ff91d", + "username": "Juan Lanus", + "email": "Juan Lanus@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2235, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb3f", + "username": "user456814", + "email": "user456814@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb41", + "username": "Yanni", + "email": "Yanni@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2547, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a1"]] + }, + { + "_id": "62f25143a5cd2c51555ffb43", + "username": "phillihp", + "email": "phillihp@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 618, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb46", + "username": "ˈvɔlə", + "email": "ˈvɔlə@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7921, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb48", + "username": "Stardust", + "email": "Stardust@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 969, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb4a", + "username": "Kartik Chugh", + "email": "Kartik Chugh@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 960, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb4c", + "username": "I am the Most Stupid Person", + "email": "I am the Most Stupid Person@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3035, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb4e", + "username": "Jacksonkr", + "email": "Jacksonkr@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 30835, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb50", + "username": "Halceyon", + "email": "Halceyon@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 299, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb52", + "username": "Lukas Liesis", + "email": "Lukas Liesis@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 21994, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb54", + "username": "Bob Brown", + "email": "Bob Brown@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1444, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb56", + "username": "Bergi", + "email": "Bergi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 583110, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9122d"], + ["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90c9f"] + ] + }, + { + "_id": "62f25143a5cd2c51555ffb58", + "username": "Calmarius", + "email": "Calmarius@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 17670, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff398"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d0"] + ] + }, + { + "_id": "62f25143a5cd2c51555ffb5a", + "username": "user2950593", + "email": "user2950593@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8316, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb5c", + "username": "Eric F.", + "email": "Eric F.@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 299, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb5e", + "username": "Henry Heleine", + "email": "Henry Heleine@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 655, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb60", + "username": "Darryl Hein", + "email": "Darryl Hein@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 139289, + "questionIds": ["62f321bb082fcc3049e8feef"], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d5"]] + }, + { + "_id": "62f25143a5cd2c51555ffb62", + "username": "Gerrit Brink", + "email": "Gerrit Brink@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 937, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb64", + "username": "dev", + "email": "dev@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2232, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb66", + "username": "tomsmeding", + "email": "tomsmeding@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 876, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb68", + "username": "Dirk Vollmar", + "email": "Dirk Vollmar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 167909, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb6a", + "username": "R.. GitHub STOP HELPING ICE", + "email": "R.. GitHub STOP HELPING ICE@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 202858, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb6c", + "username": "Solomon Ucko", + "email": "Solomon Ucko@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4150, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb6e", + "username": "Andrey", + "email": "Andrey@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 19564, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb70", + "username": "Mark", + "email": "Mark@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 37037, + "questionIds": ["62f321bb082fcc3049e8feda", "62f321bb082fcc3049e8feec"], + "answerIds": [ + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090e"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094d"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c15"] + ] + }, + { + "_id": "62f25143a5cd2c51555ffb73", + "username": "Qtax", + "email": "Qtax@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 32465, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb75", + "username": "Raza Ahmed", + "email": "Raza Ahmed@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2601, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb77", + "username": "Muaz Khan", + "email": "Muaz Khan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6898, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb79", + "username": "chryss", + "email": "chryss@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7293, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb7b", + "username": "Friedrich", + "email": "Friedrich@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1819, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903eb"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90884"] + ] + }, + { + "_id": "62f25143a5cd2c51555ffb7e", + "username": "bohem.be", + "email": "bohem.be@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1715, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb80", + "username": "Dan", + "email": "Dan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1923, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b0"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d6"], + ["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e9040b"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068f"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f06"] + ] + }, + { + "_id": "62f25143a5cd2c51555ffb82", + "username": "Kevin Connors", + "email": "Kevin Connors@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 67, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb84", + "username": "not my real name", + "email": "not my real name@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 391, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb86", + "username": "cptloop", + "email": "cptloop@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 695, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb88", + "username": "Mark Adler", + "email": "Mark Adler@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 90639, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb8b", + "username": "Rui Marques", + "email": "Rui Marques@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7711, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb8d", + "username": "Khaled Alshaya", + "email": "Khaled Alshaya@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 91884, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb8f", + "username": "SmallChess", + "email": "SmallChess@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb91", + "username": "Art", + "email": "Art@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 22749, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb93", + "username": "usr", + "email": "usr@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 165751, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb95", + "username": "viam0Zah", + "email": "viam0Zah@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 25389, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb98", + "username": "Michel Gokan Khan", + "email": "Michel Gokan Khan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2407, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb9a", + "username": "Muhammad Babar", + "email": "Muhammad Babar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 7964, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb9c", + "username": "Chris Ward", + "email": "Chris Ward@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 698, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffb9f", + "username": "Dogweather", + "email": "Dogweather@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 14125, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffba1", + "username": "doub1ejack", + "email": "doub1ejack@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 9957, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffba3", + "username": "SilverbackNet", + "email": "SilverbackNet@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2049, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffba5", + "username": "Randal Schwartz", + "email": "Randal Schwartz@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 34610, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffba7", + "username": "Thane Brimhall", + "email": "Thane Brimhall@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8947, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffba9", + "username": "Noon Silk", + "email": "Noon Silk@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 53106, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbab", + "username": "ala", + "email": "ala@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6650, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbad", + "username": "Shawn J. Molloy", + "email": "Shawn J. Molloy@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2338, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbaf", + "username": "NimChimpsky", + "email": "NimChimpsky@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 45255, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbb1", + "username": "ThiefMaster", + "email": "ThiefMaster@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 300878, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbb3", + "username": "Igor Popov", + "email": "Igor Popov@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 9431, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbb5", + "username": "Frank Shearar", + "email": "Frank Shearar@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 16839, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbb8", + "username": "nachocab", + "email": "nachocab@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 12200, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbba", + "username": "Kyle Clegg", + "email": "Kyle Clegg@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 37667, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbbc", + "username": "Stefano Borini", + "email": "Stefano Borini@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 133124, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbbe", + "username": "givanse", + "email": "givanse@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13903, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbc0", + "username": "mlissner", + "email": "mlissner@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 16290, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbc2", + "username": "Marco Demaio", + "email": "Marco Demaio@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 32560, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1e"]] + }, + { + "_id": "62f25143a5cd2c51555ffbc4", + "username": "abbood", + "email": "abbood@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 22337, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbc6", + "username": "Olle Härstedt", + "email": "Olle Härstedt@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3592, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbc8", + "username": "deceze", + "email": "deceze@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 494903, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbca", + "username": "Daniel Darabos", + "email": "Daniel Darabos@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 26539, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbcc", + "username": "user233232", + "email": "user233232@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5857, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbce", + "username": "pseudosudo", + "email": "pseudosudo@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1942, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbd0", + "username": "mr.freeze", + "email": "mr.freeze@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13211, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25143a5cd2c51555ffbd2", + "username": "Sneakyness", + "email": "Sneakyness@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5201, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc5c", + "username": "Onshop", + "email": "Onshop@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2673, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc5e", + "username": "user1385191", + "email": "user1385191@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc60", + "username": "Naftali", + "email": "Naftali@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 142562, + "questionIds": ["62f321bb082fcc3049e8fef9"], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc64", + "username": "Mark Schultheiss", + "email": "Mark Schultheiss@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 30716, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc69", + "username": "ankush981", + "email": "ankush981@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4947, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc6b", + "username": "rogerdpack", + "email": "rogerdpack@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 57751, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc6f", + "username": "Boffin", + "email": "Boffin@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc71", + "username": "Vitaly Zdanevich", + "email": "Vitaly Zdanevich@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 11230, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc74", + "username": "Bort", + "email": "Bort@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 817, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc76", + "username": "atomless", + "email": "atomless@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1272, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc78", + "username": "Khalid Habib", + "email": "Khalid Habib@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1015, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc7b", + "username": "Martijn", + "email": "Martijn@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 421, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc7d", + "username": "Clonkex", + "email": "Clonkex@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3069, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc7f", + "username": "varad_s", + "email": "varad_s@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 493, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc83", + "username": "RSolberg", + "email": "RSolberg@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 26565, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc85", + "username": "Cᴏʀʏ", + "email": "Cᴏʀʏ@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 102064, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc87", + "username": "Dan K.K.", + "email": "Dan K.K.@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5585, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc89", + "username": "Pacerier", + "email": "Pacerier@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 82180, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc8b", + "username": "sdm350", + "email": "sdm350@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 377, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc8d", + "username": "mcont", + "email": "mcont@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1580, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc8f", + "username": "aaaaaa", + "email": "aaaaaa@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc91", + "username": "Greener", + "email": "Greener@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1277, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc93", + "username": "Stijn de Witt", + "email": "Stijn de Witt@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 36994, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051a"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e91"] + ] + }, + { + "_id": "62f25144a5cd2c51555ffc95", + "username": "vbullinger", + "email": "vbullinger@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3848, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc97", + "username": "befzz", + "email": "befzz@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1172, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25144a5cd2c51555ffc9a", + "username": "Roy Prins", + "email": "Roy Prins@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffc9c", + "username": "Joel Mueller", + "email": "Joel Mueller@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 27871, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffc9e", + "username": "Amjad Masad", + "email": "Amjad Masad@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3995, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffca2", + "username": "vp_arth", + "email": "vp_arth@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13967, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffca4", + "username": "Zafar Kurbonov", + "email": "Zafar Kurbonov@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2027, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffca6", + "username": "T.Todua", + "email": "T.Todua@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 49529, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905cf"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90924"] + ] + }, + { + "_id": "62f25145a5cd2c51555ffca8", + "username": "uɥƃnɐʌuop", + "email": "uɥƃnɐʌuop@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13185, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcaa", + "username": "Kyle", + "email": "Kyle@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 31009, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcac", + "username": "einstein", + "email": "einstein@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 12589, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcae", + "username": "Testo Testini", + "email": "Testo Testini@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2160, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcb0", + "username": "cssyphus", + "email": "cssyphus@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 35388, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcb3", + "username": "aross", + "email": "aross@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2903, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcb5", + "username": "Mike Aron", + "email": "Mike Aron@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 461, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcb7", + "username": "Braden Best", + "email": "Braden Best@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8380, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af3"]] + }, + { + "_id": "62f25145a5cd2c51555ffcb9", + "username": "noob", + "email": "noob@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8682, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcbb", + "username": "Ryan Miller", + "email": "Ryan Miller@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 381, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcbd", + "username": "Manish", + "email": "Manish@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1918, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcbf", + "username": "Dan Bray", + "email": "Dan Bray@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90704"]] + }, + { + "_id": "62f25145a5cd2c51555ffcc2", + "username": "zzzzBov", + "email": "zzzzBov@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 168440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901dc"]] + }, + { + "_id": "62f25145a5cd2c51555ffcc4", + "username": "CRice", + "email": "CRice@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 11882, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcc6", + "username": "S At", + "email": "S At@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 392, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcc7", + "username": "Luis Perez", + "email": "Luis Perez@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 27024, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff35f"]] + }, + { + "_id": "62f25145a5cd2c51555ffcc9", + "username": "Anshul", + "email": "Anshul@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8852, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f8a"], + ["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff1"] + ] + }, + { + "_id": "62f25145a5cd2c51555ffccb", + "username": "Naor", + "email": "Naor@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 22703, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffccd", + "username": "Mathieu Larose", + "email": "Mathieu Larose@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1210, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffccf", + "username": "Codebeat", + "email": "Codebeat@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6341, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90498"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90893"] + ] + }, + { + "_id": "62f25145a5cd2c51555ffcd1", + "username": "Christoph", + "email": "Christoph@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 159513, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e2"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056b"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90907"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c7"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90585"] + ] + }, + { + "_id": "62f25145a5cd2c51555ffcd3", + "username": "ArtOfWarfare", + "email": "ArtOfWarfare@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 19398, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcd5", + "username": "tonix", + "email": "tonix@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6386, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25145a5cd2c51555ffcd7", + "username": "Nate Thompson", + "email": "Nate Thompson@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 165, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcd8", + "username": "Ville", + "email": "Ville@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3865, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff362"]] + }, + { + "_id": "62f25146a5cd2c51555ffcda", + "username": "Nolo", + "email": "Nolo@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 776, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcdc", + "username": "Corné", + "email": "Corné@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 95, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcdd", + "username": "VisioN", + "email": "VisioN@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 139405, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff361"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903aa"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b1"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f35"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcdf", + "username": "rep_movsd", + "email": "rep_movsd@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6634, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffce1", + "username": "marsze", + "email": "marsze@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 13665, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffce3", + "username": "Luis Cabrera Benito", + "email": "Luis Cabrera Benito@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1420, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffce6", + "username": "Ryan Smith", + "email": "Ryan Smith@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1195, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffce9", + "username": "satya164", + "email": "satya164@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 8403, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcea", + "username": "FrEsC 81", + "email": "FrEsC 81@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3971, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff360"]] + }, + { + "_id": "62f25146a5cd2c51555ffceb", + "username": "Redu", + "email": "Redu@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 23093, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff363"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc7"], + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b5"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc6"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffced", + "username": "gafi", + "email": "gafi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 11341, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff364"], + ["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bec"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcef", + "username": "Daniele Testa", + "email": "Daniele Testa@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1269, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcf0", + "username": "kind user", + "email": "kind user@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 35377, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff366"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900bb"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90542"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcf1", + "username": "Pawan", + "email": "Pawan@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 563, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff365"]] + }, + { + "_id": "62f25146a5cd2c51555ffcf2", + "username": "Clyde", + "email": "Clyde@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 712, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff369"]] + }, + { + "_id": "62f25146a5cd2c51555ffcf4", + "username": "sebastianf182", + "email": "sebastianf182@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 9578, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffcf8", + "username": "Srikrushna", + "email": "Srikrushna@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3667, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff368"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffde"], + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90146"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90999"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcf9", + "username": "alejoko", + "email": "alejoko@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 819, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36a"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe8"], + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f0"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90317"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcfa", + "username": "Lesly Revenge", + "email": "Lesly Revenge@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 856, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36b"]] + }, + { + "_id": "62f25146a5cd2c51555ffcfb", + "username": "meem", + "email": "meem@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 164, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36c"]] + }, + { + "_id": "62f25146a5cd2c51555ffcfc", + "username": "M. Hamza Rajput", + "email": "M. Hamza Rajput@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5867, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36d"]] + }, + { + "_id": "62f25146a5cd2c51555ffcfd", + "username": "vkarpov15", + "email": "vkarpov15@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 3284, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36e"], + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f4"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffcff", + "username": "kabirbaidhya", + "email": "kabirbaidhya@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2984, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff370"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93211"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffd00", + "username": "Kamil Kiełczewski", + "email": "Kamil Kiełczewski@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 73651, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff36f"], + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff43"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90100"], + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90123"], + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90289"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90313"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c3"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f6"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90532"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054b"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062d"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90703"], + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072b"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c0"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f1"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b3"], + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908f0"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90966"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4c"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a83"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac9"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae9"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba7"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bd0"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c13"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c30"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c56"], + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c82"], + ["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c9d"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2f"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d30"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d70"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd4"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e45"], + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e64"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e82"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee6"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa1"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd4"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91042"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffed"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057f"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93153"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffd01", + "username": "Baptiste Arnaud", + "email": "Baptiste Arnaud@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2248, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff372"]] + }, + { + "_id": "62f25146a5cd2c51555ffd03", + "username": "blazkovicz", + "email": "blazkovicz@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 712, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f25146a5cd2c51555ffd05", + "username": "hossein sedighian", + "email": "hossein sedighian@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1489, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff371"]] + }, + { + "_id": "62f25146a5cd2c51555ffd06", + "username": "Ran Turner", + "email": "Ran Turner@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10365, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff373"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c9"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90240"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90363"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90683"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9069e"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906d1"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90782"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a55"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f93"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffd08", + "username": "vdegenne", + "email": "vdegenne@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 10630, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e1"]] + }, + { + "_id": "62f25146a5cd2c51555ffd09", + "username": "Gass", + "email": "Gass@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4551, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff374"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ef1"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffd0a", + "username": "bmaggi", + "email": "bmaggi@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 2709, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff375"]] + }, + { + "_id": "62f25146a5cd2c51555ffd0b", + "username": "Pikamander2", + "email": "Pikamander2@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 6143, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff376"]] + }, + { + "_id": "62f25146a5cd2c51555ffd0c", + "username": "Svitlana Maksymchuk", + "email": "Svitlana Maksymchuk@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4270, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff395"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900cd"] + ] + }, + { + "_id": "62f25146a5cd2c51555ffd0d", + "username": "Steve purpose", + "email": "Steve purpose@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 17, + "questionIds": [], + "answerIds": [["62f2513da5cd2c51555ff2db", "62f25140a5cd2c51555ff377"]] + }, + { + "_id": "62f25146a5cd2c51555ffd0e", + "username": "tags2k", + "email": "tags2k@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 77859, + "questionIds": ["62f2513da5cd2c51555ff2db"], + "answerIds": [] + }, + { + "_id": "62f25147a5cd2c51555ffd0f", + "username": "Arnaud Gouder de Beauregard", + "email": "Arnaud Gouder de Beauregard@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1499, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff396"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ce"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd10", + "username": "nornagon", + "email": "nornagon@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 14895, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff397"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900cf"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd11", + "username": "Ariel", + "email": "Ariel@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 4472, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff399"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d1"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd12", + "username": "rgb_life", + "email": "rgb_life@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 353, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39a"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d2"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd13", + "username": "jpierson", + "email": "jpierson@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 15342, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39c"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d4"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd14", + "username": "Wayne Molina", + "email": "Wayne Molina@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 18642, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39b"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d3"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd15", + "username": "JMawer", + "email": "JMawer@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 249, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39d"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d5"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd16", + "username": "John Strickler", + "email": "John Strickler@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 24692, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39e"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d6"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd17", + "username": "Sam4Code", + "email": "Sam4Code@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 521, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff3a0"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d8"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd19", + "username": "David Spector", + "email": "David Spector@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 1263, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90126"]] + }, + { + "_id": "62f25147a5cd2c51555ffd1b", + "username": "Imdad", + "email": "Imdad@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 5864, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff39f"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d7"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b62"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd1c", + "username": "emolaus", + "email": "emolaus@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 599, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff3a2"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900da"] + ] + }, + { + "_id": "62f25147a5cd2c51555ffd1d", + "username": "weageoo", + "email": "weageoo@fake-email.com", + "salt": "eeea107755dc9ee2d2d194e9f6342365", + "key": "a60ce4354c04d3d9eaa88c2e3a702564bebb069675d7dd8ad04a3b837dc3680375c5b5b447b3f7bd4975f58467feebf8ea41c286b674c11f091755e8629fd74e", + "points": 97, + "questionIds": [], + "answerIds": [ + ["62f2513da5cd2c51555ff2c8", "62f25140a5cd2c51555ff3a1"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900d9"] + ] + }, + { + "_id": "62f3220a082fcc3049e90f5a", + "username": "chiborg", + "email": "chiborg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25423, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f5c", + "username": "evanmcd", + "email": "evanmcd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1929, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f5e", + "username": "Casey", + "email": "Casey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3247, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f60", + "username": "atheaos", + "email": "atheaos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 720, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f61", + "username": "Mote", + "email": "Mote@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10877, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff0f"]] + }, + { + "_id": "62f3220a082fcc3049e90f63", + "username": "Etienne Dupuis", + "email": "Etienne Dupuis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13080, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f65", + "username": "ZoomIn", + "email": "ZoomIn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1105, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f67", + "username": "cwingrav", + "email": "cwingrav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 931, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f68", + "username": "twernt", + "email": "twernt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19893, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff10"]] + }, + { + "_id": "62f3220a082fcc3049e90f69", + "username": "Simon_Weaver", + "email": "Simon_Weaver@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 132153, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff12"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033e"] + ] + }, + { + "_id": "62f3220a082fcc3049e90f6c", + "username": "Kzqai", + "email": "Kzqai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22108, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f6e", + "username": "Robert Johnstone", + "email": "Robert Johnstone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5311, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f70", + "username": "Wolfpack'08", + "email": "Wolfpack'08@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3702, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f72", + "username": "Izkata", + "email": "Izkata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8701, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f74", + "username": "carl", + "email": "carl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 48630, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f76", + "username": "Yana Agun Siswanto", + "email": "Yana Agun Siswanto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1800, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220a082fcc3049e90f78", + "username": "Black", + "email": "Black@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15866, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021f"], + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e29"] + ] + }, + { + "_id": "62f3220a082fcc3049e90f79", + "username": "user574889", + "email": "user574889@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4199, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff13"]] + }, + { + "_id": "62f3220a082fcc3049e90f7b", + "username": "vsync", + "email": "vsync@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105780, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90151"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90543"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90910"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de7"], + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e12"] + ] + }, + { + "_id": "62f3220a082fcc3049e90f7c", + "username": "aaronLile", + "email": "aaronLile@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5757, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff14"]] + }, + { + "_id": "62f3220b082fcc3049e90fac", + "username": "Pedro Rainho", + "email": "Pedro Rainho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4146, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff16"]] + }, + { + "_id": "62f3220b082fcc3049e90fad", + "username": "Abiy", + "email": "Abiy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2333, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff15"]] + }, + { + "_id": "62f3220b082fcc3049e90fae", + "username": "Lucas", + "email": "Lucas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16460, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff18"], + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2f"] + ] + }, + { + "_id": "62f3220b082fcc3049e90faf", + "username": "David Levin", + "email": "David Levin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6307, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff17"]] + }, + { + "_id": "62f3220c082fcc3049e90fb1", + "username": "YangombiUmpakati", + "email": "YangombiUmpakati@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 185, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220c082fcc3049e90fb2", + "username": "webvitaly", + "email": "webvitaly@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff19"]] + }, + { + "_id": "62f3220c082fcc3049e90fb3", + "username": "ScoRpion", + "email": "ScoRpion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11196, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1a"], + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff71"] + ] + }, + { + "_id": "62f3220c082fcc3049e90fb5", + "username": "Matt Brock", + "email": "Matt Brock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5202, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1c"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffbe"] + ] + }, + { + "_id": "62f3220c082fcc3049e90fb6", + "username": "Vaishu", + "email": "Vaishu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2255, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1b"]] + }, + { + "_id": "62f3220c082fcc3049e90fb7", + "username": "Maneesh Kumar", + "email": "Maneesh Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1327, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1d"]] + }, + { + "_id": "62f3220c082fcc3049e90fb8", + "username": "Code Spy", + "email": "Code Spy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8956, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1e"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019c"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019d"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095d"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6a"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f91"] + ] + }, + { + "_id": "62f3220c082fcc3049e9104a", + "username": "Ilia", + "email": "Ilia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12738, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220c082fcc3049e9104c", + "username": "Kenma", + "email": "Kenma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1777, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220c082fcc3049e9104d", + "username": "Matthias Wegtun", + "email": "Matthias Wegtun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1221, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff1f"]] + }, + { + "_id": "62f3220c082fcc3049e9104e", + "username": "Irfan DANISH", + "email": "Irfan DANISH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8077, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff21"]] + }, + { + "_id": "62f3220c082fcc3049e91050", + "username": "nbrooks", + "email": "nbrooks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17949, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220c082fcc3049e91051", + "username": "cssimsek", + "email": "cssimsek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1219, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff22"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fdd"] + ] + }, + { + "_id": "62f3220c082fcc3049e91052", + "username": "Andron", + "email": "Andron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6105, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff25"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905ca"] + ] + }, + { + "_id": "62f3220c082fcc3049e91053", + "username": "Gaurav", + "email": "Gaurav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 413, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff23"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d0"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906eb"], + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e9097d"] + ] + }, + { + "_id": "62f3220c082fcc3049e91054", + "username": "Premshankar Tiwari", + "email": "Premshankar Tiwari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2956, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff24"]] + }, + { + "_id": "62f3220c082fcc3049e91055", + "username": "Kareem", + "email": "Kareem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4665, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff26"]] + }, + { + "_id": "62f3220c082fcc3049e91056", + "username": "conceptdeluxe", + "email": "conceptdeluxe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff27"]] + }, + { + "_id": "62f3220c082fcc3049e91057", + "username": "pixellabme", + "email": "pixellabme@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff28"]] + }, + { + "_id": "62f3220d082fcc3049e91058", + "username": "Ross Brasseaux", + "email": "Ross Brasseaux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3784, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff20"]] + }, + { + "_id": "62f3220d082fcc3049e91059", + "username": "RN Kushwaha", + "email": "RN Kushwaha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2003, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff29"]] + }, + { + "_id": "62f3220d082fcc3049e9105a", + "username": "Roman Losev", + "email": "Roman Losev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1841, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2c"]] + }, + { + "_id": "62f3220d082fcc3049e9105b", + "username": "V31", + "email": "V31@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7608, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2a"]] + }, + { + "_id": "62f3220d082fcc3049e9105c", + "username": "Mathias Stavrou", + "email": "Mathias Stavrou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 851, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2b"]] + }, + { + "_id": "62f3220d082fcc3049e9105d", + "username": "Sangeet Shah", + "email": "Sangeet Shah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2969, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2e"], + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027a"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90571"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90abf"] + ] + }, + { + "_id": "62f3220d082fcc3049e9105e", + "username": "Prabhagaran", + "email": "Prabhagaran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3440, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2d"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d63"] + ] + }, + { + "_id": "62f3220d082fcc3049e9105f", + "username": "cbertelegni", + "email": "cbertelegni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 423, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff2f"]] + }, + { + "_id": "62f3220d082fcc3049e91060", + "username": "Disapamok", + "email": "Disapamok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1376, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff30"], + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3d"] + ] + }, + { + "_id": "62f3220e082fcc3049e91061", + "username": "Abrar Jahin", + "email": "Abrar Jahin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13364, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff32"]] + }, + { + "_id": "62f3220e082fcc3049e91062", + "username": "Oriol", + "email": "Oriol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10550, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff31"], + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5d"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90347"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a41"] + ] + }, + { + "_id": "62f3220e082fcc3049e91063", + "username": "No one", + "email": "No one@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1110, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff34"]] + }, + { + "_id": "62f3220e082fcc3049e91065", + "username": "Questionnaire", + "email": "Questionnaire@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 586, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3220e082fcc3049e91066", + "username": "lmcDevloper", + "email": "lmcDevloper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 332, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff33"]] + }, + { + "_id": "62f3220e082fcc3049e91067", + "username": "Wolfack", + "email": "Wolfack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2479, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff35"]] + }, + { + "_id": "62f3220e082fcc3049e91068", + "username": "Sky Yip", + "email": "Sky Yip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1011, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff36"]] + }, + { + "_id": "62f3220e082fcc3049e91069", + "username": "Abdul Aziz Al Basyir", + "email": "Abdul Aziz Al Basyir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 980, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff38"]] + }, + { + "_id": "62f3220e082fcc3049e9106a", + "username": "Arun Karnawat", + "email": "Arun Karnawat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 575, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff37"]] + }, + { + "_id": "62f3220f082fcc3049e9106b", + "username": "Peter Wone", + "email": "Peter Wone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16931, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff39"]] + }, + { + "_id": "62f3220f082fcc3049e9106c", + "username": "user6119825", + "email": "user6119825@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3a"]] + }, + { + "_id": "62f3220f082fcc3049e9106d", + "username": "Antoine Auffray", + "email": "Antoine Auffray@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3c"]] + }, + { + "_id": "62f3220f082fcc3049e9106e", + "username": "user8903269", + "email": "user8903269@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3e"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90305"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f10"] + ] + }, + { + "_id": "62f3220f082fcc3049e9106f", + "username": "Profesor08", + "email": "Profesor08@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1086, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff40"]] + }, + { + "_id": "62f3220f082fcc3049e91070", + "username": "user10145552", + "email": "user10145552@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff3f"]] + }, + { + "_id": "62f3220f082fcc3049e91071", + "username": "Muhammad", + "email": "Muhammad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5843, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff41"]] + }, + { + "_id": "62f3220f082fcc3049e91072", + "username": "L Y E S - C H I O U K H", + "email": "L Y E S - C H I O U K H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4441, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff42"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f5"], + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043f"] + ] + }, + { + "_id": "62f3220f082fcc3049e91073", + "username": "Aravinda Meewalaarachchi", + "email": "Aravinda Meewalaarachchi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff44"]] + }, + { + "_id": "62f32210082fcc3049e91074", + "username": "Brane", + "email": "Brane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3061, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff46"]] + }, + { + "_id": "62f32210082fcc3049e91075", + "username": "Ankush Kumar", + "email": "Ankush Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 729, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff45"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aec"] + ] + }, + { + "_id": "62f32210082fcc3049e91076", + "username": "Hasee Amarathunga", + "email": "Hasee Amarathunga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1755, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff48"]] + }, + { + "_id": "62f32210082fcc3049e91077", + "username": "Mojtaba Nava", + "email": "Mojtaba Nava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 754, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff47"]] + }, + { + "_id": "62f32210082fcc3049e91078", + "username": "Vicky P", + "email": "Vicky P@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 423, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff4a"]] + }, + { + "_id": "62f32210082fcc3049e91079", + "username": "centralhubb.com", + "email": "centralhubb.com@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2551, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff49"]] + }, + { + "_id": "62f32210082fcc3049e9107a", + "username": "Enrico König", + "email": "Enrico König@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 194, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff4c"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90107"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90108"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9e"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa1"] + ] + }, + { + "_id": "62f32210082fcc3049e9107c", + "username": "JJ Shaw", + "email": "JJ Shaw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 143, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32210082fcc3049e9107d", + "username": "udorb b", + "email": "udorb b@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff4b"]] + }, + { + "_id": "62f32210082fcc3049e9107e", + "username": "Sahil Thummar", + "email": "Sahil Thummar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1092, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff4d"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900ca"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c17"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa8"] + ] + }, + { + "_id": "62f32210082fcc3049e9107f", + "username": "user17087887", + "email": "user17087887@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fead", "62f321bc082fcc3049e8ff4e"]] + }, + { + "_id": "62f32210082fcc3049e91080", + "username": "Philip Morton", + "email": "Philip Morton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 126519, + "questionIds": ["62f321bb082fcc3049e8fead"], + "answerIds": [] + }, + { + "_id": "62f3224e082fcc3049e91081", + "username": "Stephen", + "email": "Stephen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 835, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff51"]] + }, + { + "_id": "62f3224e082fcc3049e91082", + "username": "Jamie Hutber", + "email": "Jamie Hutber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24953, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff52"]] + }, + { + "_id": "62f3224e082fcc3049e91083", + "username": "Pank", + "email": "Pank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13212, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff54"]] + }, + { + "_id": "62f3224e082fcc3049e91084", + "username": "user2436758", + "email": "user2436758@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 939, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff53"]] + }, + { + "_id": "62f3224e082fcc3049e91085", + "username": "Renganathan M G", + "email": "Renganathan M G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4799, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff56"]] + }, + { + "_id": "62f3224e082fcc3049e91086", + "username": "DWoldrich", + "email": "DWoldrich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3683, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff55"]] + }, + { + "_id": "62f3224e082fcc3049e91087", + "username": "Placeholder", + "email": "Placeholder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff58"]] + }, + { + "_id": "62f3224e082fcc3049e91088", + "username": "FutureNerd", + "email": "FutureNerd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 739, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff57"]] + }, + { + "_id": "62f3224e082fcc3049e91089", + "username": "Shubh", + "email": "Shubh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6527, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff59"]] + }, + { + "_id": "62f3224e082fcc3049e9108b", + "username": "gprasant", + "email": "gprasant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14951, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5a"]] + }, + { + "_id": "62f3224f082fcc3049e9108c", + "username": "Heich-B", + "email": "Heich-B@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 662, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5b"]] + }, + { + "_id": "62f3224f082fcc3049e9108d", + "username": "zangw", + "email": "zangw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37995, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5c"], + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90133"] + ] + }, + { + "_id": "62f3224f082fcc3049e9108e", + "username": "PippoApps.com", + "email": "PippoApps.com@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 536, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5e"]] + }, + { + "_id": "62f3224f082fcc3049e9108f", + "username": "Просто программист", + "email": "Просто программист@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff5f"]] + }, + { + "_id": "62f3224f082fcc3049e91090", + "username": "Wesam", + "email": "Wesam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 874, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff61"]] + }, + { + "_id": "62f3224f082fcc3049e91091", + "username": "Tân", + "email": "Tân@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff60"]] + }, + { + "_id": "62f3224f082fcc3049e91092", + "username": "Rabin Pantha", + "email": "Rabin Pantha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 901, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff62"]] + }, + { + "_id": "62f3224f082fcc3049e91093", + "username": "Pritam Banerjee", + "email": "Pritam Banerjee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff63"]] + }, + { + "_id": "62f3224f082fcc3049e91094", + "username": "Bikash Chapagain", + "email": "Bikash Chapagain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 190, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff64"]] + }, + { + "_id": "62f32250082fcc3049e91095", + "username": "Ashish", + "email": "Ashish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1878, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff66"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe7"], + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe9"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90221"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c5"] + ] + }, + { + "_id": "62f32250082fcc3049e91096", + "username": "Jerin K Alexander", + "email": "Jerin K Alexander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 285, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff67"]] + }, + { + "_id": "62f32250082fcc3049e91097", + "username": "snnsnn", + "email": "snnsnn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff68"]] + }, + { + "_id": "62f32250082fcc3049e91098", + "username": "Abdul Rahman", + "email": "Abdul Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff6a"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c1"] + ] + }, + { + "_id": "62f32250082fcc3049e91099", + "username": "Amin Azimi", + "email": "Amin Azimi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 538, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff69"]] + }, + { + "_id": "62f32250082fcc3049e9109b", + "username": "png", + "email": "png@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1069, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32250082fcc3049e9109d", + "username": "Luke T O'Brien", + "email": "Luke T O'Brien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2295, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32250082fcc3049e9109e", + "username": "Boris Guéry", + "email": "Boris Guéry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46766, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff6e"]] + }, + { + "_id": "62f32250082fcc3049e9109f", + "username": "user188973", + "email": "user188973@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff6d"]] + }, + { + "_id": "62f32251082fcc3049e910a0", + "username": "xloadx", + "email": "xloadx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1543, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff70"]] + }, + { + "_id": "62f32251082fcc3049e910a1", + "username": "Fred", + "email": "Fred@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3661, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff6f"]] + }, + { + "_id": "62f32251082fcc3049e910a2", + "username": "Mark Pieszak - Trilon.io", + "email": "Mark Pieszak - Trilon.io@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff72"]] + }, + { + "_id": "62f32251082fcc3049e910a3", + "username": "Swaprks", + "email": "Swaprks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff74"]] + }, + { + "_id": "62f32251082fcc3049e910a4", + "username": "Nadeem Yasin", + "email": "Nadeem Yasin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff73"]] + }, + { + "_id": "62f32251082fcc3049e910a5", + "username": "Anup", + "email": "Anup@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff76"]] + }, + { + "_id": "62f32251082fcc3049e910a8", + "username": "Good Night Nerd Pride", + "email": "Good Night Nerd Pride@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7291, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32251082fcc3049e910a9", + "username": "Patartics Milán", + "email": "Patartics Milán@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4730, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff75"]] + }, + { + "_id": "62f32251082fcc3049e910aa", + "username": "Bidyut", + "email": "Bidyut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 529, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff77"]] + }, + { + "_id": "62f32251082fcc3049e910ab", + "username": "user2496033", + "email": "user2496033@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff78"], + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015b"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018d"] + ] + }, + { + "_id": "62f32252082fcc3049e910ac", + "username": "bren", + "email": "bren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4098, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff79"]] + }, + { + "_id": "62f32252082fcc3049e910ad", + "username": "iConnor", + "email": "iConnor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19579, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7a"]] + }, + { + "_id": "62f32252082fcc3049e910ae", + "username": "Stefan Gruenwald", + "email": "Stefan Gruenwald@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2522, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7c"]] + }, + { + "_id": "62f32252082fcc3049e910af", + "username": "tilak", + "email": "tilak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7b"]] + }, + { + "_id": "62f32252082fcc3049e910b0", + "username": "Vinay Sharma", + "email": "Vinay Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1043, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7d"]] + }, + { + "_id": "62f32252082fcc3049e910b1", + "username": "Ashish Ratan", + "email": "Ashish Ratan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2784, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7e"]] + }, + { + "_id": "62f32252082fcc3049e910b2", + "username": "Jaydeep Jadav", + "email": "Jaydeep Jadav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 776, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff80"]] + }, + { + "_id": "62f32252082fcc3049e910b3", + "username": "Govind Singh", + "email": "Govind Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14908, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff7f"]] + }, + { + "_id": "62f32252082fcc3049e910b4", + "username": "Sakthi Karthik", + "email": "Sakthi Karthik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2956, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff81"], + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90195"] + ] + }, + { + "_id": "62f32252082fcc3049e910b5", + "username": "dnxit", + "email": "dnxit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6812, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff82"]] + }, + { + "_id": "62f32253082fcc3049e910b6", + "username": "Newse", + "email": "Newse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2310, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff83"]] + }, + { + "_id": "62f32253082fcc3049e910b7", + "username": "Ben", + "email": "Ben@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2036, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff84"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc9"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef8"] + ] + }, + { + "_id": "62f32253082fcc3049e910b8", + "username": "Azam Alvi", + "email": "Azam Alvi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6558, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff85"]] + }, + { + "_id": "62f32253082fcc3049e910ba", + "username": "Patrick W. McMahon", + "email": "Patrick W. McMahon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3321, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32253082fcc3049e910bb", + "username": "Adarsh Gowda K R", + "email": "Adarsh Gowda K R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 931, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff86"]] + }, + { + "_id": "62f32253082fcc3049e910bc", + "username": "devst3r", + "email": "devst3r@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 536, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff88"]] + }, + { + "_id": "62f32253082fcc3049e910bd", + "username": "stratum", + "email": "stratum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 259, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff87"]] + }, + { + "_id": "62f32253082fcc3049e910be", + "username": "Epiphany", + "email": "Epiphany@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1778, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8a"]] + }, + { + "_id": "62f32253082fcc3049e910bf", + "username": "SergeDirect", + "email": "SergeDirect@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1849, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff89"]] + }, + { + "_id": "62f32253082fcc3049e910c0", + "username": "vipul sorathiya", + "email": "vipul sorathiya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1308, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8c"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c05"] + ] + }, + { + "_id": "62f32253082fcc3049e910c1", + "username": "lalithkumar", + "email": "lalithkumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3362, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8b"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f4"] + ] + }, + { + "_id": "62f32254082fcc3049e910c2", + "username": "Divyesh Kanzariya", + "email": "Divyesh Kanzariya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3309, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8e"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91022"] + ] + }, + { + "_id": "62f32254082fcc3049e910c4", + "username": "A1rPun", + "email": "A1rPun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15643, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32254082fcc3049e910c6", + "username": "Muhammad Waqas", + "email": "Muhammad Waqas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1120, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8d"], + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f6"] + ] + }, + { + "_id": "62f32254082fcc3049e910c7", + "username": "1j01", + "email": "1j01@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3373, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff90"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90751"] + ] + }, + { + "_id": "62f32254082fcc3049e910c8", + "username": "Zigri2612", + "email": "Zigri2612@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff8f"]] + }, + { + "_id": "62f32254082fcc3049e910ca", + "username": "Mimouni", + "email": "Mimouni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3244, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32254082fcc3049e910cb", + "username": "user1012506", + "email": "user1012506@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1980, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff91"]] + }, + { + "_id": "62f32254082fcc3049e910cc", + "username": "madhur", + "email": "madhur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff92"]] + }, + { + "_id": "62f32254082fcc3049e910cd", + "username": "Abhishek K. Upadhyay", + "email": "Abhishek K. Upadhyay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 992, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff93"]] + }, + { + "_id": "62f32254082fcc3049e910ce", + "username": "Taufiq Rahman", + "email": "Taufiq Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5446, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff96"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90990"] + ] + }, + { + "_id": "62f32254082fcc3049e910d1", + "username": "SantoshK", + "email": "SantoshK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1651, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff94"]] + }, + { + "_id": "62f32254082fcc3049e910d2", + "username": "cascading-style", + "email": "cascading-style@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 414, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff95"]] + }, + { + "_id": "62f32255082fcc3049e910d3", + "username": "Kalpesh Panchal", + "email": "Kalpesh Panchal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 915, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff98"]] + }, + { + "_id": "62f32255082fcc3049e910d4", + "username": "sneha", + "email": "sneha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 415, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff97"]] + }, + { + "_id": "62f32255082fcc3049e910d5", + "username": "Maniruzzaman Akash", + "email": "Maniruzzaman Akash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4244, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9a"]] + }, + { + "_id": "62f32255082fcc3049e910d6", + "username": "Mohideen bin Mohammed", + "email": "Mohideen bin Mohammed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17155, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9b"]] + }, + { + "_id": "62f32255082fcc3049e910d7", + "username": "RuNpiXelruN", + "email": "RuNpiXelruN@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1727, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9c"]] + }, + { + "_id": "62f32255082fcc3049e910d8", + "username": "omkaartg", + "email": "omkaartg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9e"]] + }, + { + "_id": "62f32255082fcc3049e910d9", + "username": "HD..", + "email": "HD..@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1428, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9d"]] + }, + { + "_id": "62f32255082fcc3049e910da", + "username": "wild coder", + "email": "wild coder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 830, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ffa0"], + ["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c99"] + ] + }, + { + "_id": "62f32255082fcc3049e910db", + "username": "Andrei Todorut", + "email": "Andrei Todorut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3929, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ff9f"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909bb"] + ] + }, + { + "_id": "62f32256082fcc3049e910dc", + "username": "Alessandro Foolish Ciak DAnton", + "email": "Alessandro Foolish Ciak DAnton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 558, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ffa1"]] + }, + { + "_id": "62f32256082fcc3049e910dd", + "username": "Mustkeem K", + "email": "Mustkeem K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7316, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feaf", "62f321bc082fcc3049e8ffa2"]] + }, + { + "_id": "62f32256082fcc3049e910de", + "username": "venkatachalam", + "email": "venkatachalam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 102003, + "questionIds": ["62f321bb082fcc3049e8feaf"], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910df", + "username": "Zirak", + "email": "Zirak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37604, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa3"]] + }, + { + "_id": "62f32256082fcc3049e910e1", + "username": "Mahbubul Islam", + "email": "Mahbubul Islam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 998, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910e3", + "username": "Cristian Traìna", + "email": "Cristian Traìna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8562, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910e4", + "username": "Peter Olson", + "email": "Peter Olson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 132294, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa4"]] + }, + { + "_id": "62f32256082fcc3049e910e5", + "username": "xavierm02", + "email": "xavierm02@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa6"]] + }, + { + "_id": "62f32256082fcc3049e910e6", + "username": "Loupax", + "email": "Loupax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4480, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa7"]] + }, + { + "_id": "62f32256082fcc3049e910e8", + "username": "Stefan Fabian", + "email": "Stefan Fabian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 478, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910ea", + "username": "Rashid Iqbal", + "email": "Rashid Iqbal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 853, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c8"]] + }, + { + "_id": "62f32256082fcc3049e910ec", + "username": "mikep", + "email": "mikep@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4686, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910ee", + "username": "deb", + "email": "deb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6002, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910f0", + "username": "GreenAsJade", + "email": "GreenAsJade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14156, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32256082fcc3049e910f1", + "username": "Saša", + "email": "Saša@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4027, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa8"]] + }, + { + "_id": "62f32256082fcc3049e910f2", + "username": "zykadelic", + "email": "zykadelic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1043, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffa9"]] + }, + { + "_id": "62f32256082fcc3049e910f3", + "username": "Ekramul Hoque", + "email": "Ekramul Hoque@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4767, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffaa"]] + }, + { + "_id": "62f32257082fcc3049e910f5", + "username": "Renze de Waal", + "email": "Renze de Waal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 533, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32257082fcc3049e910f6", + "username": "yckart", + "email": "yckart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30648, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffab"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902de"] + ] + }, + { + "_id": "62f32257082fcc3049e910f7", + "username": "Enrico", + "email": "Enrico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 593, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffac"]] + }, + { + "_id": "62f32257082fcc3049e910f8", + "username": "Ben Lesh", + "email": "Ben Lesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 106852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffad"]] + }, + { + "_id": "62f32257082fcc3049e910f9", + "username": "Jeff Noel", + "email": "Jeff Noel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffae"]] + }, + { + "_id": "62f32257082fcc3049e910fa", + "username": "slosd", + "email": "slosd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2964, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb0"]] + }, + { + "_id": "62f32257082fcc3049e910fb", + "username": "Roger", + "email": "Roger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1916, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffaf"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5a"] + ] + }, + { + "_id": "62f32257082fcc3049e910fc", + "username": "Don Vincent Preziosi", + "email": "Don Vincent Preziosi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 405, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb1"]] + }, + { + "_id": "62f32257082fcc3049e910fd", + "username": "NullPointer", + "email": "NullPointer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2708, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb2"]] + }, + { + "_id": "62f32257082fcc3049e910fe", + "username": "sofiax", + "email": "sofiax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 519, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb3"]] + }, + { + "_id": "62f32257082fcc3049e910ff", + "username": "Ardi", + "email": "Ardi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 318, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb4"]] + }, + { + "_id": "62f32258082fcc3049e91101", + "username": "wharding28", + "email": "wharding28@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb6"]] + }, + { + "_id": "62f32258082fcc3049e91102", + "username": "Nigel Sheridan-Smith", + "email": "Nigel Sheridan-Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 687, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb5"]] + }, + { + "_id": "62f32258082fcc3049e91103", + "username": "Salvador Dali", + "email": "Salvador Dali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 202019, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb7"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e4"] + ] + }, + { + "_id": "62f32258082fcc3049e91105", + "username": "Hontoni", + "email": "Hontoni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1302, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32258082fcc3049e91107", + "username": "mboeckle", + "email": "mboeckle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 928, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb8"]] + }, + { + "_id": "62f32258082fcc3049e91109", + "username": "Dughall", + "email": "Dughall@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 679, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32258082fcc3049e9110b", + "username": "EigenFool", + "email": "EigenFool@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 319, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32258082fcc3049e9110c", + "username": "vatsal", + "email": "vatsal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3705, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffba"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053c"] + ] + }, + { + "_id": "62f32258082fcc3049e9110e", + "username": "Masoud Aghaei", + "email": "Masoud Aghaei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f92"]] + }, + { + "_id": "62f32258082fcc3049e9110f", + "username": "amd", + "email": "amd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19706, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffb9"]] + }, + { + "_id": "62f32258082fcc3049e91111", + "username": "CSᵠ", + "email": "CSᵠ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9979, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32258082fcc3049e91113", + "username": "Scott 混合理论", + "email": "Scott 混合理论@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2172, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32258082fcc3049e91114", + "username": "Do Hoa Vinh", + "email": "Do Hoa Vinh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 338, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffbc"]] + }, + { + "_id": "62f32258082fcc3049e91116", + "username": "penguin", + "email": "penguin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffbb"]] + }, + { + "_id": "62f32258082fcc3049e91117", + "username": "Nejc Lepen", + "email": "Nejc Lepen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 347, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffbd"]] + }, + { + "_id": "62f32259082fcc3049e91118", + "username": "flurdy", + "email": "flurdy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3622, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc0"]] + }, + { + "_id": "62f32259082fcc3049e91119", + "username": "Ryan", + "email": "Ryan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3104, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffbf"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d7"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90958"] + ] + }, + { + "_id": "62f32259082fcc3049e9111b", + "username": "Ankur Loriya", + "email": "Ankur Loriya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3156, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32259082fcc3049e9111d", + "username": "Eugene Tiurin", + "email": "Eugene Tiurin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3769, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc2"], + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90033"], + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902aa"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065d"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907ba"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092a"] + ] + }, + { + "_id": "62f32259082fcc3049e9111f", + "username": "some-non-descript-user", + "email": "some-non-descript-user@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 605, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32259082fcc3049e91121", + "username": "Chun Yang", + "email": "Chun Yang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2351, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc1"]] + }, + { + "_id": "62f32259082fcc3049e91122", + "username": "MEC", + "email": "MEC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1542, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc3"]] + }, + { + "_id": "62f32259082fcc3049e91123", + "username": "Kamuran Sönecek", + "email": "Kamuran Sönecek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3302, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc4"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ef"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b3"] + ] + }, + { + "_id": "62f32259082fcc3049e91125", + "username": "bjfletcher", + "email": "bjfletcher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10608, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc6"]] + }, + { + "_id": "62f32259082fcc3049e91127", + "username": "jadinerky", + "email": "jadinerky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32259082fcc3049e91128", + "username": "Lars Gyrup Brink Nielsen", + "email": "Lars Gyrup Brink Nielsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3691, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc5"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b02"] + ] + }, + { + "_id": "62f32259082fcc3049e9112b", + "username": "Heretic Monkey", + "email": "Heretic Monkey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11257, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa6"]] + }, + { + "_id": "62f32259082fcc3049e9112c", + "username": "user4109793", + "email": "user4109793@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc8"]] + }, + { + "_id": "62f3225a082fcc3049e9112e", + "username": "ankhzet", + "email": "ankhzet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225a082fcc3049e9112f", + "username": "Shawn Deprey", + "email": "Shawn Deprey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 417, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffca"]] + }, + { + "_id": "62f3225a082fcc3049e91131", + "username": "codingsplash", + "email": "codingsplash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4340, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225a082fcc3049e91132", + "username": "Mahendra Kulkarni", + "email": "Mahendra Kulkarni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1357, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffc9"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a42"] + ] + }, + { + "_id": "62f3225a082fcc3049e91134", + "username": "Claudio Holanda", + "email": "Claudio Holanda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2235, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225a082fcc3049e91135", + "username": "rajat44", + "email": "rajat44@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4681, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffcb"]] + }, + { + "_id": "62f3225a082fcc3049e91137", + "username": "Abdennour TOUMI", + "email": "Abdennour TOUMI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 78473, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffcc"], + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90132"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e1"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074e"], + ["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9b"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eab"] + ] + }, + { + "_id": "62f3225a082fcc3049e91139", + "username": "Jamiec", + "email": "Jamiec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 130072, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225a082fcc3049e9113a", + "username": "Ali Akram", + "email": "Ali Akram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4061, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffce"]] + }, + { + "_id": "62f3225a082fcc3049e9113b", + "username": "Stelios Voskos", + "email": "Stelios Voskos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 526, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffcd"]] + }, + { + "_id": "62f3225a082fcc3049e9113d", + "username": "darmis", + "email": "darmis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2099, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225a082fcc3049e9113e", + "username": "alejandro", + "email": "alejandro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2744, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd0"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902df"] + ] + }, + { + "_id": "62f3225a082fcc3049e9113f", + "username": "Aidan Hoolachan", + "email": "Aidan Hoolachan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2145, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd1"]] + }, + { + "_id": "62f3225b082fcc3049e91140", + "username": "Partial Science", + "email": "Partial Science@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 320, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd4"]] + }, + { + "_id": "62f3225b082fcc3049e91141", + "username": "nsantana", + "email": "nsantana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd3"]] + }, + { + "_id": "62f3225b082fcc3049e91142", + "username": "hailong", + "email": "hailong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd5"]] + }, + { + "_id": "62f3225b082fcc3049e91143", + "username": "cjjenkinson", + "email": "cjjenkinson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd6"]] + }, + { + "_id": "62f3225b082fcc3049e91144", + "username": "Adeel Imran", + "email": "Adeel Imran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11444, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd7"], + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a3"] + ] + }, + { + "_id": "62f3225b082fcc3049e91146", + "username": "pwilcox", + "email": "pwilcox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5279, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225b082fcc3049e91147", + "username": "Sujith S", + "email": "Sujith S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 505, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffd8"]] + }, + { + "_id": "62f3225b082fcc3049e91148", + "username": "Gaurang Patel", + "email": "Gaurang Patel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 429, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffda"]] + }, + { + "_id": "62f3225b082fcc3049e9114b", + "username": "Sanya Kravchuk", + "email": "Sanya Kravchuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffdb"]] + }, + { + "_id": "62f3225b082fcc3049e9114c", + "username": "Steven Spungin", + "email": "Steven Spungin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23335, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffdc"]] + }, + { + "_id": "62f3225c082fcc3049e9114d", + "username": "Aram Grigoryan", + "email": "Aram Grigoryan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 716, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffdd"]] + }, + { + "_id": "62f3225c082fcc3049e9114e", + "username": "Thilina Sampath", + "email": "Thilina Sampath@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe0"]] + }, + { + "_id": "62f3225c082fcc3049e91150", + "username": "mekbib.awoke", + "email": "mekbib.awoke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 764, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffec"]] + }, + { + "_id": "62f3225c082fcc3049e91151", + "username": "Zibri", + "email": "Zibri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8224, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffdf"], + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90292"] + ] + }, + { + "_id": "62f3225c082fcc3049e91152", + "username": "AmerllicA", + "email": "AmerllicA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24440, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe2"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906cd"] + ] + }, + { + "_id": "62f3225c082fcc3049e91153", + "username": "Phil", + "email": "Phil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1072, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe1"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd3"] + ] + }, + { + "_id": "62f3225c082fcc3049e91154", + "username": "Sebastien H.", + "email": "Sebastien H.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6189, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe3"]] + }, + { + "_id": "62f3225c082fcc3049e91155", + "username": "Shahriar Ahmad", + "email": "Shahriar Ahmad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe4"]] + }, + { + "_id": "62f3225c082fcc3049e91157", + "username": "jdavid05", + "email": "jdavid05@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 235, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225c082fcc3049e91159", + "username": "Fusion", + "email": "Fusion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4596, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225c082fcc3049e9115a", + "username": "Max Alexander Hanna", + "email": "Max Alexander Hanna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2955, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe5"]] + }, + { + "_id": "62f3225c082fcc3049e9115c", + "username": "Chang", + "email": "Chang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 407, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffe6"]] + }, + { + "_id": "62f3225c082fcc3049e9115e", + "username": "Little Brain", + "email": "Little Brain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2238, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225d082fcc3049e91160", + "username": "MHOOS", + "email": "MHOOS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4918, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225d082fcc3049e91162", + "username": "eightyfive", + "email": "eightyfive@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4392, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3225d082fcc3049e91163", + "username": "codepleb", + "email": "codepleb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9396, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffea"]] + }, + { + "_id": "62f3225d082fcc3049e91164", + "username": "Atchutha rama reddy Karri", + "email": "Atchutha rama reddy Karri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1279, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffeb"]] + }, + { + "_id": "62f3225d082fcc3049e91165", + "username": "Konrad Borowski", + "email": "Konrad Borowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10885, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffee"]] + }, + { + "_id": "62f3229a082fcc3049e91166", + "username": "Ajay Thakur", + "email": "Ajay Thakur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1048, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff2"]] + }, + { + "_id": "62f3229a082fcc3049e91167", + "username": "pritam", + "email": "pritam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2267, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff1"]] + }, + { + "_id": "62f3229a082fcc3049e91168", + "username": "Taylor Hawkes", + "email": "Taylor Hawkes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff3"]] + }, + { + "_id": "62f3229a082fcc3049e9116a", + "username": "Josh", + "email": "Josh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 648, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff4"]] + }, + { + "_id": "62f3229a082fcc3049e9116b", + "username": "Arun s", + "email": "Arun s@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 627, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff5"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90537"] + ] + }, + { + "_id": "62f3229a082fcc3049e9116d", + "username": "Daniel Waltrip", + "email": "Daniel Waltrip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2440, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3229a082fcc3049e91170", + "username": "majid savalanpour", + "email": "majid savalanpour@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1400, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3229a082fcc3049e91171", + "username": "Janardan Prajapati", + "email": "Janardan Prajapati@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff7"]] + }, + { + "_id": "62f3229a082fcc3049e91172", + "username": "ifelse.codes", + "email": "ifelse.codes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2029, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff8"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dde"] + ] + }, + { + "_id": "62f3229a082fcc3049e91173", + "username": "Ahmad", + "email": "Ahmad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1292, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff9"]] + }, + { + "_id": "62f3229a082fcc3049e91174", + "username": "katma", + "email": "katma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 291, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fffa"]] + }, + { + "_id": "62f3229b082fcc3049e91175", + "username": "Abdelrhman Arnos", + "email": "Abdelrhman Arnos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fffc"]] + }, + { + "_id": "62f3229b082fcc3049e91176", + "username": "karthicvel", + "email": "karthicvel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fffb"]] + }, + { + "_id": "62f3229b082fcc3049e91177", + "username": "Coding", + "email": "Coding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 325, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fffd"]] + }, + { + "_id": "62f3229b082fcc3049e91178", + "username": "MSA", + "email": "MSA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 209, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fffe"]] + }, + { + "_id": "62f3229b082fcc3049e91179", + "username": "Tijo John", + "email": "Tijo John@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 666, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffff"]] + }, + { + "_id": "62f3229b082fcc3049e9117a", + "username": "Konrad Rudolph", + "email": "Konrad Rudolph@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 509948, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90000"]] + }, + { + "_id": "62f3229b082fcc3049e9117b", + "username": "dlaliberte", + "email": "dlaliberte@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3192, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90002"], + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000b"] + ] + }, + { + "_id": "62f3229b082fcc3049e9117c", + "username": "Rakesh Pai", + "email": "Rakesh Pai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90001"]] + }, + { + "_id": "62f3229b082fcc3049e9117d", + "username": "Chris S", + "email": "Chris S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63772, + "questionIds": ["62f321bb082fcc3049e8ff05"], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90004"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92add"] + ] + }, + { + "_id": "62f3229b082fcc3049e9117e", + "username": "someisaac", + "email": "someisaac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 957, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90003"]] + }, + { + "_id": "62f3229c082fcc3049e9117f", + "username": "Nathan Long", + "email": "Nathan Long@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119286, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90005"]] + }, + { + "_id": "62f3229c082fcc3049e91180", + "username": "John Pick", + "email": "John Pick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5476, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90006"]] + }, + { + "_id": "62f3229c082fcc3049e91181", + "username": "StewShack", + "email": "StewShack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90008"]] + }, + { + "_id": "62f3229c082fcc3049e91183", + "username": "Tiago Martins Peres", + "email": "Tiago Martins Peres@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12914, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3229c082fcc3049e91184", + "username": "Jacob Swartwood", + "email": "Jacob Swartwood@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4075, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90007"]] + }, + { + "_id": "62f3229c082fcc3049e91185", + "username": "Nathan Whitehead", + "email": "Nathan Whitehead@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1882, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90009"]] + }, + { + "_id": "62f3229c082fcc3049e91186", + "username": "mykhal", + "email": "mykhal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18405, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000a"]] + }, + { + "_id": "62f3229c082fcc3049e91187", + "username": "Gerardo Lima", + "email": "Gerardo Lima@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000c"]] + }, + { + "_id": "62f3229c082fcc3049e91188", + "username": "goonerify", + "email": "goonerify@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000d"]] + }, + { + "_id": "62f3229c082fcc3049e91189", + "username": "ketan", + "email": "ketan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 392, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000e"]] + }, + { + "_id": "62f3229d082fcc3049e9118a", + "username": "mjmoody383", + "email": "mjmoody383@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 348, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9000f"]] + }, + { + "_id": "62f3229d082fcc3049e9118b", + "username": "Jean-Baptiste Yunès", + "email": "Jean-Baptiste Yunès@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33048, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90010"]] + }, + { + "_id": "62f3229d082fcc3049e9118c", + "username": "Jim", + "email": "Jim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90011"]] + }, + { + "_id": "62f3229d082fcc3049e9118d", + "username": "moha297", + "email": "moha297@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90012"]] + }, + { + "_id": "62f3229d082fcc3049e9118e", + "username": "Charlie", + "email": "Charlie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4023, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90013"], + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043e"] + ] + }, + { + "_id": "62f3229d082fcc3049e9118f", + "username": "Stupid Stupid", + "email": "Stupid Stupid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90014"]] + }, + { + "_id": "62f3229d082fcc3049e91190", + "username": "Arman", + "email": "Arman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4876, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90015"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd4"] + ] + }, + { + "_id": "62f3229d082fcc3049e91191", + "username": "Max Tkachenko", + "email": "Max Tkachenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 504, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90016"]] + }, + { + "_id": "62f3229d082fcc3049e91192", + "username": "Vitim.us", + "email": "Vitim.us@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18871, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90017"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901ff"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90833"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085f"] + ] + }, + { + "_id": "62f3229d082fcc3049e91193", + "username": "Raul Martins", + "email": "Raul Martins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 375, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90018"]] + }, + { + "_id": "62f3229e082fcc3049e91194", + "username": "Taye", + "email": "Taye@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 797, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90019"]] + }, + { + "_id": "62f3229e082fcc3049e91195", + "username": "cube", + "email": "cube@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1724, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001a"]] + }, + { + "_id": "62f3229e082fcc3049e91196", + "username": "roland", + "email": "roland@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7425, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001b"]] + }, + { + "_id": "62f3229e082fcc3049e91197", + "username": "Magne", + "email": "Magne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15625, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001c"], + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90022"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a3"] + ] + }, + { + "_id": "62f3229e082fcc3049e91198", + "username": "Juan Garcia", + "email": "Juan Garcia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 706, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001e"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089d"] + ] + }, + { + "_id": "62f3229e082fcc3049e91199", + "username": "Pawel Furmaniak", + "email": "Pawel Furmaniak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4402, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90020"], + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90097"] + ] + }, + { + "_id": "62f3229e082fcc3049e9119a", + "username": "Nick Manning", + "email": "Nick Manning@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2727, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001d"], + ["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90409"] + ] + }, + { + "_id": "62f3229e082fcc3049e9119b", + "username": "Bhojendra Rauniyar", + "email": "Bhojendra Rauniyar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79742, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9001f"]] + }, + { + "_id": "62f3229e082fcc3049e9119c", + "username": "nomen", + "email": "nomen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3536, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90021"]] + }, + { + "_id": "62f3229f082fcc3049e9119d", + "username": "b_dev", + "email": "b_dev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2548, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90023"]] + }, + { + "_id": "62f3229f082fcc3049e9119e", + "username": "Mayur Randive", + "email": "Mayur Randive@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90024"]] + }, + { + "_id": "62f3229f082fcc3049e9119f", + "username": "PsyChip", + "email": "PsyChip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90026"]] + }, + { + "_id": "62f3229f082fcc3049e911a0", + "username": "Shushanth Pallegar", + "email": "Shushanth Pallegar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2724, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90025"]] + }, + { + "_id": "62f3229f082fcc3049e911a1", + "username": "Rafael Eyng", + "email": "Rafael Eyng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4342, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90028"]] + }, + { + "_id": "62f3229f082fcc3049e911a2", + "username": "floribon", + "email": "floribon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19057, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90027"]] + }, + { + "_id": "62f3229f082fcc3049e911a3", + "username": "Dinesh Kanivu", + "email": "Dinesh Kanivu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002a"]] + }, + { + "_id": "62f3229f082fcc3049e911a4", + "username": "Andy", + "email": "Andy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7084, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90029"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf4"] + ] + }, + { + "_id": "62f3229f082fcc3049e911a5", + "username": "Mike Robinson", + "email": "Mike Robinson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8150, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002c"]] + }, + { + "_id": "62f3229f082fcc3049e911a6", + "username": "Javier La Banca", + "email": "Javier La Banca@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002b"]] + }, + { + "_id": "62f322a0082fcc3049e911a7", + "username": "enb081", + "email": "enb081@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3661, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002d"]] + }, + { + "_id": "62f322a0082fcc3049e911a8", + "username": "Tero Tolonen", + "email": "Tero Tolonen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3989, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002e"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90596"] + ] + }, + { + "_id": "62f322a0082fcc3049e911a9", + "username": "TastyCode", + "email": "TastyCode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5489, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90030"]] + }, + { + "_id": "62f322a0082fcc3049e911aa", + "username": "Harry Robbins", + "email": "Harry Robbins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 514, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9002f"]] + }, + { + "_id": "62f322a0082fcc3049e911ab", + "username": "Dmitry Frank", + "email": "Dmitry Frank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9979, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90032"]] + }, + { + "_id": "62f322a0082fcc3049e911ac", + "username": "Mohammed Safeer", + "email": "Mohammed Safeer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19503, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90031"], + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b8"], + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027b"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9f"] + ] + }, + { + "_id": "62f322a0082fcc3049e911ad", + "username": "Gerard ONeill", + "email": "Gerard ONeill@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3636, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90034"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074b"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac3"], + ["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fef"] + ] + }, + { + "_id": "62f322a0082fcc3049e911ae", + "username": "Premraj", + "email": "Premraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 68550, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90035"]] + }, + { + "_id": "62f322a0082fcc3049e911af", + "username": "ejectamenta", + "email": "ejectamenta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1013, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90036"]] + }, + { + "_id": "62f322a1082fcc3049e911b0", + "username": "NinjaBeetle", + "email": "NinjaBeetle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90037"]] + }, + { + "_id": "62f322a1082fcc3049e911b1", + "username": "Pao Im", + "email": "Pao Im@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 317, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90038"]] + }, + { + "_id": "62f322a1082fcc3049e911b2", + "username": "devlighted", + "email": "devlighted@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90039"]] + }, + { + "_id": "62f322a1082fcc3049e911b3", + "username": "soundyogi", + "email": "soundyogi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 359, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003a"]] + }, + { + "_id": "62f322a1082fcc3049e911b4", + "username": "Brandon Kent", + "email": "Brandon Kent@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003b"]] + }, + { + "_id": "62f322a1082fcc3049e911b5", + "username": "christian Nguyen", + "email": "christian Nguyen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1382, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003c"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a43"] + ] + }, + { + "_id": "62f322a1082fcc3049e911b6", + "username": "Alexis", + "email": "Alexis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5511, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003d"]] + }, + { + "_id": "62f322a1082fcc3049e911b7", + "username": "zak.http", + "email": "zak.http@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003f"]] + }, + { + "_id": "62f322a1082fcc3049e911b9", + "username": "Robse", + "email": "Robse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 835, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a1082fcc3049e911ba", + "username": "poushy", + "email": "poushy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1054, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e9003e"]] + }, + { + "_id": "62f322a1082fcc3049e911bb", + "username": "Shivprasad Koirala", + "email": "Shivprasad Koirala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26081, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb0", "62f321bc082fcc3049e90040"]] + }, + { + "_id": "62f322a1082fcc3049e911bc", + "username": "e-satis", + "email": "e-satis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 556397, + "questionIds": ["62f321bb082fcc3049e8feb0"], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911be", + "username": "Ry-", + "email": "Ry-@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 210508, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911c0", + "username": "Experimenter", + "email": "Experimenter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1667, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911c2", + "username": "KWallace", + "email": "KWallace@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 562, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904db"]] + }, + { + "_id": "62f322a2082fcc3049e911c4", + "username": "Mitul", + "email": "Mitul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911c6", + "username": "gman", + "email": "gman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93722, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911c7", + "username": "eliocs", + "email": "eliocs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17935, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb2", "62f321bc082fcc3049e90086"]] + }, + { + "_id": "62f322a2082fcc3049e911c9", + "username": "wz366", + "email": "wz366@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2600, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb2", "62f321bc082fcc3049e90087"]] + }, + { + "_id": "62f322a2082fcc3049e911cb", + "username": "TheLebDev", + "email": "TheLebDev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 479, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911cd", + "username": "dandavis", + "email": "dandavis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15787, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911ce", + "username": "gramm", + "email": "gramm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18675, + "questionIds": ["62f321bb082fcc3049e8feb2"], + "answerIds": [] + }, + { + "_id": "62f322a2082fcc3049e911d0", + "username": "vanowm", + "email": "vanowm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8061, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90396"]] + }, + { + "_id": "62f322a2082fcc3049e911d1", + "username": "Kafka", + "email": "Kafka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 482, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008b"]] + }, + { + "_id": "62f322a2082fcc3049e911d2", + "username": "Sasha Firsov", + "email": "Sasha Firsov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 689, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008c"]] + }, + { + "_id": "62f322a2082fcc3049e911d3", + "username": "thomasrutter", + "email": "thomasrutter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 110484, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008d"]] + }, + { + "_id": "62f322a2082fcc3049e911d4", + "username": "Christian C. Salvadó", + "email": "Christian C. Salvadó@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 776561, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008e"], + ["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909c9"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ab9"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc5"] + ] + }, + { + "_id": "62f322a2082fcc3049e911d5", + "username": "Rob", + "email": "Rob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5445, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9008f"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b68"] + ] + }, + { + "_id": "62f322a3082fcc3049e911d6", + "username": "Joel Purra", + "email": "Joel Purra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22884, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90091"]] + }, + { + "_id": "62f322a3082fcc3049e911d7", + "username": "Sean McMillan", + "email": "Sean McMillan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9856, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90090"]] + }, + { + "_id": "62f322a3082fcc3049e911d8", + "username": "Ingo Kegel", + "email": "Ingo Kegel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45110, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90092"]] + }, + { + "_id": "62f322a3082fcc3049e911d9", + "username": "Herc", + "email": "Herc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 527, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90093"]] + }, + { + "_id": "62f322a3082fcc3049e911da", + "username": "NullPoiиteя", + "email": "NullPoiиteя@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90094"]] + }, + { + "_id": "62f322a3082fcc3049e911db", + "username": "eljenso", + "email": "eljenso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90095"]] + }, + { + "_id": "62f322a3082fcc3049e911dc", + "username": "Mbengue Assane", + "email": "Mbengue Assane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2795, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90096"]] + }, + { + "_id": "62f322a3082fcc3049e911dd", + "username": "sla55er", + "email": "sla55er@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 781, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90098"]] + }, + { + "_id": "62f322a3082fcc3049e911de", + "username": "suhailvs", + "email": "suhailvs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17971, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e90099"]] + }, + { + "_id": "62f322a3082fcc3049e911ed", + "username": "Aaron Maenpaa", + "email": "Aaron Maenpaa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114963, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911ec"]] + }, + { + "_id": "62f322a3082fcc3049e911ef", + "username": "Pat Notz", + "email": "Pat Notz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 199870, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911ee"]] + }, + { + "_id": "62f322a3082fcc3049e911f1", + "username": "Asaph", + "email": "Asaph@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 155114, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911f0"]] + }, + { + "_id": "62f322a3082fcc3049e911f3", + "username": "Ates Goral", + "email": "Ates Goral@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 133722, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911f2"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90539"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905bb"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1d"] + ] + }, + { + "_id": "62f322a3082fcc3049e911f5", + "username": "Wogan", + "email": "Wogan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65119, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911f4"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90583"] + ] + }, + { + "_id": "62f322a3082fcc3049e911f7", + "username": "John Feminella", + "email": "John Feminella@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 295773, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911f6"]] + }, + { + "_id": "62f322a3082fcc3049e911f9", + "username": "codeape", + "email": "codeape@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94921, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911f8"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90334"] + ] + }, + { + "_id": "62f322a3082fcc3049e911fb", + "username": "jop", + "email": "jop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80793, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911fa"]] + }, + { + "_id": "62f322a3082fcc3049e911fd", + "username": "broofa", + "email": "broofa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36563, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911fc"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90369"] + ] + }, + { + "_id": "62f322a3082fcc3049e911ff", + "username": "Jistanidiot", + "email": "Jistanidiot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51588, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e911fe"]] + }, + { + "_id": "62f322a3082fcc3049e91201", + "username": "Ben Hoffstein", + "email": "Ben Hoffstein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 100537, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91200"]] + }, + { + "_id": "62f322a3082fcc3049e91203", + "username": "Mark Longair", + "email": "Mark Longair@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 420251, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91202"]] + }, + { + "_id": "62f322a3082fcc3049e91205", + "username": "Daniel G", + "email": "Daniel G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63536, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91204"]] + }, + { + "_id": "62f322a3082fcc3049e91207", + "username": "Brandon Clapp", + "email": "Brandon Clapp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 62013, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91206"]] + }, + { + "_id": "62f322a3082fcc3049e91209", + "username": "Sean Bright", + "email": "Sean Bright@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 115543, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91208"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901fb"] + ] + }, + { + "_id": "62f322a3082fcc3049e9120b", + "username": "bdonlan", + "email": "bdonlan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 216343, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9120a"], + ["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e903fc"] + ] + }, + { + "_id": "62f322a3082fcc3049e9120d", + "username": "gahooa", + "email": "gahooa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 124061, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9120c"]] + }, + { + "_id": "62f322a3082fcc3049e9120f", + "username": "emk", + "email": "emk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58602, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9120e"]] + }, + { + "_id": "62f322a3082fcc3049e91211", + "username": "Tom", + "email": "Tom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54654, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91210"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f2"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3d"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93203"] + ] + }, + { + "_id": "62f322a3082fcc3049e91213", + "username": "levik", + "email": "levik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 110105, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91212"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ab"] + ] + }, + { + "_id": "62f322a3082fcc3049e91215", + "username": "B Bycroft", + "email": "B Bycroft@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3480, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91214"]] + }, + { + "_id": "62f322a3082fcc3049e91217", + "username": "Paul Stephenson", + "email": "Paul Stephenson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65022, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91216"]] + }, + { + "_id": "62f322a3082fcc3049e91219", + "username": "Chris Johnsen", + "email": "Chris Johnsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 203267, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91218"]] + }, + { + "_id": "62f322a3082fcc3049e9121b", + "username": "Brian Ustas", + "email": "Brian Ustas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58139, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9121a"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905e9"] + ] + }, + { + "_id": "62f322a3082fcc3049e9121d", + "username": "Trevor", + "email": "Trevor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49622, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9121c"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91047"] + ] + }, + { + "_id": "62f322a3082fcc3049e9121f", + "username": "JPReddy", + "email": "JPReddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60463, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9121e"]] + }, + { + "_id": "62f322a3082fcc3049e91221", + "username": "Jamie Flournoy", + "email": "Jamie Flournoy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50174, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91220"]] + }, + { + "_id": "62f322a3082fcc3049e91223", + "username": "Chris B.", + "email": "Chris B.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79838, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91222"]] + }, + { + "_id": "62f322a3082fcc3049e91225", + "username": "mechanical_meat", + "email": "mechanical_meat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 157000, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91224"]] + }, + { + "_id": "62f322a3082fcc3049e91229", + "username": "bansi", + "email": "bansi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53220, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91228"]] + }, + { + "_id": "62f322a3082fcc3049e9122b", + "username": "Reto Meier", + "email": "Reto Meier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 96005, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9122a"]] + }, + { + "_id": "62f322a3082fcc3049e9122f", + "username": "waitingkuo", + "email": "waitingkuo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 82396, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9122e"]] + }, + { + "_id": "62f322a3082fcc3049e91231", + "username": "dustbuster", + "email": "dustbuster@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 70944, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91230"]] + }, + { + "_id": "62f322a3082fcc3049e91233", + "username": "quickshiftin", + "email": "quickshiftin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61723, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91232"]] + }, + { + "_id": "62f322a3082fcc3049e91235", + "username": "guinaps", + "email": "guinaps@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4051, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91234"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90825"] + ] + }, + { + "_id": "62f322a3082fcc3049e91237", + "username": "Amber", + "email": "Amber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 483213, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91236"]] + }, + { + "_id": "62f322a3082fcc3049e91239", + "username": "trobrock", + "email": "trobrock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45509, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91238"]] + }, + { + "_id": "62f322a3082fcc3049e9123b", + "username": "Daniel Fischer", + "email": "Daniel Fischer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179088, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9123a"]] + }, + { + "_id": "62f322a3082fcc3049e9123e", + "username": "rjh", + "email": "rjh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47736, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9123d"], + ["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e903ff"] + ] + }, + { + "_id": "62f322a3082fcc3049e91240", + "username": "Adam Bellaire", + "email": "Adam Bellaire@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104695, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9123f"]] + }, + { + "_id": "62f322a3082fcc3049e91242", + "username": "bdukes", + "email": "bdukes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 146063, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91241"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905b8"] + ] + }, + { + "_id": "62f322a3082fcc3049e91244", + "username": "cloudhead", + "email": "cloudhead@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15105, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91243"]] + }, + { + "_id": "62f322a3082fcc3049e91248", + "username": "FlySwat", + "email": "FlySwat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 167767, + "questionIds": ["62f321bb082fcc3049e8ff02"], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91247"]] + }, + { + "_id": "62f322a3082fcc3049e9124a", + "username": "Rob Hruska", + "email": "Rob Hruska@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 115801, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e91249"]] + }, + { + "_id": "62f322a3082fcc3049e9124c", + "username": "RichieHindle", + "email": "RichieHindle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261125, + "questionIds": [], + "answerIds": [["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9124b"]] + }, + { + "_id": "62f322a3082fcc3049e9124e", + "username": "Steve", + "email": "Steve@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49080, + "questionIds": [], + "answerIds": [ + ["62f321b9082fcc3049e8feab", "62f322a3082fcc3049e9124d"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d1"], + ["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909ed"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb3"] + ] + }, + { + "_id": "62f322a4082fcc3049e9124f", + "username": "Tao", + "email": "Tao@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 531, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009b"]] + }, + { + "_id": "62f322a4082fcc3049e91251", + "username": "d9k", + "email": "d9k@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1163, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a4082fcc3049e91252", + "username": "Leon Gaban", + "email": "Leon Gaban@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32835, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009a"]] + }, + { + "_id": "62f322a4082fcc3049e91253", + "username": "varna", + "email": "varna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 931, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009d"]] + }, + { + "_id": "62f322a4082fcc3049e91254", + "username": "Rohan", + "email": "Rohan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009c"]] + }, + { + "_id": "62f322a4082fcc3049e91255", + "username": "SuperNova", + "email": "SuperNova@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22286, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009f"]] + }, + { + "_id": "62f322a4082fcc3049e91256", + "username": "Nitin9791", + "email": "Nitin9791@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1054, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e9009e"]] + }, + { + "_id": "62f322a4082fcc3049e91257", + "username": "Anoop Rai", + "email": "Anoop Rai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 429, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a0"]] + }, + { + "_id": "62f322a4082fcc3049e91258", + "username": "user2693928", + "email": "user2693928@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3581, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a3"]] + }, + { + "_id": "62f322a4082fcc3049e9125a", + "username": "Ronny Sherer", + "email": "Ronny Sherer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7951, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a4082fcc3049e9125c", + "username": "Panos Kal.", + "email": "Panos Kal.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12232, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a2"]] + }, + { + "_id": "62f322a5082fcc3049e9125e", + "username": "Kean Amaral", + "email": "Kean Amaral@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4815, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a5"]] + }, + { + "_id": "62f322a5082fcc3049e9125f", + "username": "Santosh Pillai", + "email": "Santosh Pillai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7463, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a4"]] + }, + { + "_id": "62f322a5082fcc3049e91260", + "username": "Shakespear", + "email": "Shakespear@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1424, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a7"]] + }, + { + "_id": "62f322a5082fcc3049e91261", + "username": "Nitesh Ranjan", + "email": "Nitesh Ranjan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1063, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a6"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90357"] + ] + }, + { + "_id": "62f322a5082fcc3049e91262", + "username": "shreyasm-dev", + "email": "shreyasm-dev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2546, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900ab"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e46"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91043"] + ] + }, + { + "_id": "62f322a5082fcc3049e91263", + "username": "Robin Hossain", + "email": "Robin Hossain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900aa"]] + }, + { + "_id": "62f322a5082fcc3049e91264", + "username": "Willem van der Veen", + "email": "Willem van der Veen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28833, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a9"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900fc"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9069a"], + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90729"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907bd"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac4"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c28"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320e"] + ] + }, + { + "_id": "62f322a5082fcc3049e91265", + "username": "root", + "email": "root@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1165, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900ac"]] + }, + { + "_id": "62f322a5082fcc3049e91266", + "username": "S.G", + "email": "S.G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 129, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900ad"]] + }, + { + "_id": "62f322a5082fcc3049e91267", + "username": "H.Ostwal", + "email": "H.Ostwal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 313, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb1", "62f321bd082fcc3049e900a8"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae7"] + ] + }, + { + "_id": "62f322a5082fcc3049e91268", + "username": "Richard Garside", + "email": "Richard Garside@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 86069, + "questionIds": ["62f321bb082fcc3049e8feb1"], + "answerIds": [] + }, + { + "_id": "62f322a6082fcc3049e91269", + "username": "redsquare", + "email": "redsquare@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77298, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900af"]] + }, + { + "_id": "62f322a6082fcc3049e9126c", + "username": "Leonardo Rick", + "email": "Leonardo Rick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 500, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a6082fcc3049e9126e", + "username": "Paveloosha", + "email": "Paveloosha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 503, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a6082fcc3049e9126f", + "username": "Thaddeus Albers", + "email": "Thaddeus Albers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3916, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b1"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d5"] + ] + }, + { + "_id": "62f322a6082fcc3049e91270", + "username": "Willem", + "email": "Willem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1042, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b2"]] + }, + { + "_id": "62f322a6082fcc3049e91271", + "username": "talsibony", + "email": "talsibony@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8078, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b3"]] + }, + { + "_id": "62f322a6082fcc3049e91272", + "username": "emil", + "email": "emil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5684, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b5"]] + }, + { + "_id": "62f322a6082fcc3049e91273", + "username": "madox2", + "email": "madox2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46233, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b4"]] + }, + { + "_id": "62f322a6082fcc3049e91274", + "username": "John Slegers", + "email": "John Slegers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42323, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b6"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90697"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d9"], + ["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908d2"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a40"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6d"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103c"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320a"] + ] + }, + { + "_id": "62f322a6082fcc3049e91275", + "username": "ayushgp", + "email": "ayushgp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4482, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b7"]] + }, + { + "_id": "62f322a7082fcc3049e91277", + "username": "Ceres", + "email": "Ceres@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2315, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a7082fcc3049e91279", + "username": "Koen.", + "email": "Koen.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23472, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900b9"]] + }, + { + "_id": "62f322a7082fcc3049e9127b", + "username": "Amio.io", + "email": "Amio.io@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19467, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900ba"]] + }, + { + "_id": "62f322a7082fcc3049e9127c", + "username": "Chong Lip Phang", + "email": "Chong Lip Phang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7893, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900bd"]] + }, + { + "_id": "62f322a7082fcc3049e9127d", + "username": "user8629798", + "email": "user8629798@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900bf"]] + }, + { + "_id": "62f322a7082fcc3049e9127e", + "username": "johndavedecano", + "email": "johndavedecano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 502, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900be"]] + }, + { + "_id": "62f322a7082fcc3049e9127f", + "username": "BEJGAM SHIVA PRASAD", + "email": "BEJGAM SHIVA PRASAD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1902, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c0"], + ["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f8b"] + ] + }, + { + "_id": "62f322a7082fcc3049e91280", + "username": "hygull", + "email": "hygull@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7998, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c1"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906fa"] + ] + }, + { + "_id": "62f322a8082fcc3049e91281", + "username": "Lior Elrom", + "email": "Lior Elrom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18163, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c2"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90996"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e76"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90edb"] + ] + }, + { + "_id": "62f322a8082fcc3049e91282", + "username": "B''H Bi'ezras -- Boruch Hashem", + "email": "B''H Bi'ezras -- Boruch Hashem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c5"]] + }, + { + "_id": "62f322a8082fcc3049e91283", + "username": "YairTawil", + "email": "YairTawil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3688, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c3"]] + }, + { + "_id": "62f322a8082fcc3049e91285", + "username": "ANIK ISLAM SHOJIB", + "email": "ANIK ISLAM SHOJIB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2712, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c4"]] + }, + { + "_id": "62f322a8082fcc3049e91286", + "username": "akhtarvahid", + "email": "akhtarvahid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8366, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c6"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906cc"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90939"] + ] + }, + { + "_id": "62f322a8082fcc3049e91287", + "username": "John Doe", + "email": "John Doe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 872, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900c7"]] + }, + { + "_id": "62f322a8082fcc3049e91289", + "username": "mickmackusa", + "email": "mickmackusa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38488, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322a8082fcc3049e9128a", + "username": "Uchiha Madara", + "email": "Uchiha Madara@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 134, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900cb"]] + }, + { + "_id": "62f322a9082fcc3049e9128b", + "username": "rohithpoya", + "email": "rohithpoya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 518, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb3", "62f321bd082fcc3049e900cc"]] + }, + { + "_id": "62f322a9082fcc3049e9128c", + "username": "johnstok", + "email": "johnstok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 92632, + "questionIds": ["62f321bb082fcc3049e8feb3"], + "answerIds": [] + }, + { + "_id": "62f322aa082fcc3049e9128f", + "username": "Duncan", + "email": "Duncan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1510, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900db"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e98"] + ] + }, + { + "_id": "62f322aa082fcc3049e91290", + "username": "tggagne", + "email": "tggagne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2814, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900dd"]] + }, + { + "_id": "62f322aa082fcc3049e91291", + "username": "Robert A", + "email": "Robert A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 327, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900dc"]] + }, + { + "_id": "62f322aa082fcc3049e91292", + "username": "Dmitry Sheiko", + "email": "Dmitry Sheiko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2040, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900df"], + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f5"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904fe"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b5"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cde"] + ] + }, + { + "_id": "62f322aa082fcc3049e91293", + "username": "Alexis Dumas", + "email": "Alexis Dumas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1260, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900de"]] + }, + { + "_id": "62f322ab082fcc3049e91294", + "username": "Vicky Gonsalves", + "email": "Vicky Gonsalves@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11306, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e0"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903ae"] + ] + }, + { + "_id": "62f322ab082fcc3049e91295", + "username": "Marcin", + "email": "Marcin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e1"]] + }, + { + "_id": "62f322ab082fcc3049e91296", + "username": "Venu immadi", + "email": "Venu immadi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1585, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e2"]] + }, + { + "_id": "62f322ab082fcc3049e91297", + "username": "Ale", + "email": "Ale@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1990, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e3"]] + }, + { + "_id": "62f322ab082fcc3049e91298", + "username": "Isaac Gregson", + "email": "Isaac Gregson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1625, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e5"]] + }, + { + "_id": "62f322ab082fcc3049e91299", + "username": "heinob", + "email": "heinob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18725, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e4"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90648"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90649"] + ] + }, + { + "_id": "62f322ab082fcc3049e9129b", + "username": "tfont", + "email": "tfont@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10408, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e7"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d6"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907bf"] + ] + }, + { + "_id": "62f322ab082fcc3049e9129c", + "username": "Turnerj", + "email": "Turnerj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e6"]] + }, + { + "_id": "62f322ab082fcc3049e9129d", + "username": "Dan Dascalescu", + "email": "Dan Dascalescu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 129868, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e9"]] + }, + { + "_id": "62f322ab082fcc3049e9129e", + "username": "draupnie", + "email": "draupnie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1090, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900e8"]] + }, + { + "_id": "62f322ac082fcc3049e9129f", + "username": "Manohar Reddy Poreddy", + "email": "Manohar Reddy Poreddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21665, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ea"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea4"] + ] + }, + { + "_id": "62f322ac082fcc3049e912a0", + "username": "Adem İlhan", + "email": "Adem İlhan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1410, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900eb"]] + }, + { + "_id": "62f322ac082fcc3049e912a1", + "username": "MiBol", + "email": "MiBol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1793, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ed"]] + }, + { + "_id": "62f322ac082fcc3049e912a2", + "username": "Rahul Srivastava", + "email": "Rahul Srivastava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 160, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ec"]] + }, + { + "_id": "62f322e9082fcc3049e912a4", + "username": "Akshay Vijay Jain", + "email": "Akshay Vijay Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10090, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f4"]] + }, + { + "_id": "62f322e9082fcc3049e912a7", + "username": "KthProg", + "email": "KthProg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1910, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f6"]] + }, + { + "_id": "62f322e9082fcc3049e912a8", + "username": "Adam111p", + "email": "Adam111p@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3141, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f7"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b5"] + ] + }, + { + "_id": "62f322e9082fcc3049e912a9", + "username": "eQ19", + "email": "eQ19@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9102, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f8"]] + }, + { + "_id": "62f322e9082fcc3049e912ac", + "username": "chickens", + "email": "chickens@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15916, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900fa"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031f"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90681"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c3"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cec"] + ] + }, + { + "_id": "62f322e9082fcc3049e912ad", + "username": "jasonleonhard", + "email": "jasonleonhard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9644, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900fb"]] + }, + { + "_id": "62f322e9082fcc3049e912ae", + "username": "MishkuMoss", + "email": "MishkuMoss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900fd"]] + }, + { + "_id": "62f322ea082fcc3049e912af", + "username": "Andrej", + "email": "Andrej@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 629, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900fe"]] + }, + { + "_id": "62f322ea082fcc3049e912b0", + "username": "Kamil Dąbrowski", + "email": "Kamil Dąbrowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ff"]] + }, + { + "_id": "62f322ea082fcc3049e912b1", + "username": "Torxed", + "email": "Torxed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90101"]] + }, + { + "_id": "62f322ea082fcc3049e912b2", + "username": "Hmerman6006", + "email": "Hmerman6006@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1046, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90102"]] + }, + { + "_id": "62f322ea082fcc3049e912b3", + "username": "Dangerousgame", + "email": "Dangerousgame@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90103"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa5"] + ] + }, + { + "_id": "62f322ea082fcc3049e912b4", + "username": "9pfs supports Ukraine", + "email": "9pfs supports Ukraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90104"]] + }, + { + "_id": "62f322ea082fcc3049e912b5", + "username": "smallscript", + "email": "smallscript@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 595, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90105"]] + }, + { + "_id": "62f322ea082fcc3049e912b6", + "username": "Nirvana", + "email": "Nirvana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 256, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90106"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d32"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e8d"] + ] + }, + { + "_id": "62f322eb082fcc3049e912b8", + "username": "the_nuts", + "email": "the_nuts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5266, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322eb082fcc3049e912b9", + "username": "Mount Mario", + "email": "Mount Mario@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e90109"]] + }, + { + "_id": "62f322eb082fcc3049e912ba", + "username": "Hemant Bavle", + "email": "Hemant Bavle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3267, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9010b"]] + }, + { + "_id": "62f322eb082fcc3049e912bb", + "username": "aderchox", + "email": "aderchox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1912, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e9010a"]] + }, + { + "_id": "62f322eb082fcc3049e912bc", + "username": "Nic", + "email": "Nic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2753, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9010c"]] + }, + { + "_id": "62f322eb082fcc3049e912be", + "username": "David R Tribble", + "email": "David R Tribble@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11402, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9010f"]] + }, + { + "_id": "62f322eb082fcc3049e912bf", + "username": "jsbisht", + "email": "jsbisht@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8293, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9010e"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90816"] + ] + }, + { + "_id": "62f322eb082fcc3049e912c1", + "username": "Benjamin Gruenbaum", + "email": "Benjamin Gruenbaum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 262392, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322eb082fcc3049e912c2", + "username": "Maleen Abewardana", + "email": "Maleen Abewardana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12532, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9010d"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90573"] + ] + }, + { + "_id": "62f322eb082fcc3049e912c3", + "username": "Pablo Matias Gomez", + "email": "Pablo Matias Gomez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6246, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90111"]] + }, + { + "_id": "62f322eb082fcc3049e912c5", + "username": "Aluan Haddad", + "email": "Aluan Haddad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26958, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322eb082fcc3049e912c6", + "username": "rohithpr", + "email": "rohithpr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5732, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90110"]] + }, + { + "_id": "62f322ec082fcc3049e912c7", + "username": "Vinoth Rajendran", + "email": "Vinoth Rajendran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1141, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90112"]] + }, + { + "_id": "62f322ec082fcc3049e912c8", + "username": "Francisco Carmona", + "email": "Francisco Carmona@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1641, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90113"]] + }, + { + "_id": "62f322ec082fcc3049e912ca", + "username": "Hassan Baig", + "email": "Hassan Baig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13833, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ec082fcc3049e912cb", + "username": "Johannes Fahrenkrug", + "email": "Johannes Fahrenkrug@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40829, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90114"]] + }, + { + "_id": "62f322ec082fcc3049e912cd", + "username": "Henke", + "email": "Henke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077a"]] + }, + { + "_id": "62f322ec082fcc3049e912ce", + "username": "Mohan Dere", + "email": "Mohan Dere@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4147, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90115"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90540"] + ] + }, + { + "_id": "62f322ec082fcc3049e912cf", + "username": "Mahfuzur Rahman", + "email": "Mahfuzur Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1448, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90116"]] + }, + { + "_id": "62f322ec082fcc3049e912d1", + "username": "Matthew Brent", + "email": "Matthew Brent@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1306, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ec082fcc3049e912d3", + "username": "Juan Mendes", + "email": "Juan Mendes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 86541, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ec082fcc3049e912d4", + "username": "mikemaccana", + "email": "mikemaccana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 96989, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90119"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ee"], + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071d"] + ] + }, + { + "_id": "62f322ec082fcc3049e912d5", + "username": "amaksr", + "email": "amaksr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90118"]] + }, + { + "_id": "62f322ec082fcc3049e912d8", + "username": "Khoa Bui", + "email": "Khoa Bui@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 703, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011a"]] + }, + { + "_id": "62f322ec082fcc3049e912d9", + "username": "Haim Zamir", + "email": "Haim Zamir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 351, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011b"]] + }, + { + "_id": "62f322ed082fcc3049e912da", + "username": "Pieter Jan Bonestroo", + "email": "Pieter Jan Bonestroo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 563, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011c"]] + }, + { + "_id": "62f322ed082fcc3049e912dc", + "username": "Zum Dummi", + "email": "Zum Dummi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 243, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ed082fcc3049e912de", + "username": "Ken Ingram", + "email": "Ken Ingram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1462, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ed082fcc3049e912e0", + "username": "RAUSHAN KUMAR", + "email": "RAUSHAN KUMAR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ed082fcc3049e912e2", + "username": "CherryDT", + "email": "CherryDT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21573, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ed082fcc3049e912e3", + "username": "Fernando Carvajal", + "email": "Fernando Carvajal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1705, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011d"]] + }, + { + "_id": "62f322ed082fcc3049e912e4", + "username": "James", + "email": "James@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5169, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011e"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b53"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef7"] + ] + }, + { + "_id": "62f322ed082fcc3049e912e5", + "username": "Alex Montoya", + "email": "Alex Montoya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4161, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e9011f"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f4"] + ] + }, + { + "_id": "62f322ed082fcc3049e912e7", + "username": "Sumer", + "email": "Sumer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2462, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90120"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90359"] + ] + }, + { + "_id": "62f322ed082fcc3049e912e9", + "username": "Alaska", + "email": "Alaska@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ed082fcc3049e912ea", + "username": "Amir Fo", + "email": "Amir Fo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4248, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90121"]] + }, + { + "_id": "62f322ed082fcc3049e912eb", + "username": "Murtaza Hussain", + "email": "Murtaza Hussain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3019, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90122"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4f"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90822"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b06"] + ] + }, + { + "_id": "62f322ed082fcc3049e912ed", + "username": "nonopolarity", + "email": "nonopolarity@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 140523, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90124"]] + }, + { + "_id": "62f322ed082fcc3049e912ee", + "username": "Philipp Claßen", + "email": "Philipp Claßen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37906, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90125"]] + }, + { + "_id": "62f322ee082fcc3049e912ef", + "username": "Abd Abughazaleh", + "email": "Abd Abughazaleh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90127"]] + }, + { + "_id": "62f322ee082fcc3049e912f1", + "username": "Shapon Pal", + "email": "Shapon Pal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 960, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ee082fcc3049e912f2", + "username": "Ben S", + "email": "Ben S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67425, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9012c"]] + }, + { + "_id": "62f322ee082fcc3049e912f3", + "username": "Rudresh Oza", + "email": "Rudresh Oza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb4", "62f321bd082fcc3049e90128"]] + }, + { + "_id": "62f322ee082fcc3049e912f4", + "username": "vlio20", + "email": "vlio20@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8426, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9012e"]] + }, + { + "_id": "62f322ee082fcc3049e912f5", + "username": "olliej", + "email": "olliej@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34549, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9012d"]] + }, + { + "_id": "62f322ee082fcc3049e912f7", + "username": "Dave Newton", + "email": "Dave Newton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 156989, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ee082fcc3049e912f9", + "username": "Jon Davis", + "email": "Jon Davis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6372, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ee082fcc3049e912fa", + "username": "Lcf.vs", + "email": "Lcf.vs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1762, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90130"]] + }, + { + "_id": "62f322ee082fcc3049e912fc", + "username": "Marie", + "email": "Marie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2001, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ee082fcc3049e912fe", + "username": "Gurpreet Singh", + "email": "Gurpreet Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9012f"]] + }, + { + "_id": "62f322ee082fcc3049e912ff", + "username": "RDoc", + "email": "RDoc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 195, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90131"]] + }, + { + "_id": "62f322ef082fcc3049e91301", + "username": "N-ate", + "email": "N-ate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4986, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ef082fcc3049e91303", + "username": "Gurucharan M K", + "email": "Gurucharan M K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 781, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90134"]] + }, + { + "_id": "62f322ef082fcc3049e91304", + "username": "swaraj patil", + "email": "swaraj patil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 209, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90135"]] + }, + { + "_id": "62f322ef082fcc3049e91306", + "username": "Rehan Haider", + "email": "Rehan Haider@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 863, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ef082fcc3049e91308", + "username": "TylerH", + "email": "TylerH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21024, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ef082fcc3049e91309", + "username": "zloctb", + "email": "zloctb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9698, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90137"]] + }, + { + "_id": "62f322ef082fcc3049e9130b", + "username": "Bekim Bacaj", + "email": "Bekim Bacaj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5364, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90176"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f8"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90510"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a7"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe3"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103e"] + ] + }, + { + "_id": "62f322ef082fcc3049e9130c", + "username": "Dmitry", + "email": "Dmitry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4890, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90136"]] + }, + { + "_id": "62f322ef082fcc3049e9130d", + "username": "anandharshan", + "email": "anandharshan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5425, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90139"]] + }, + { + "_id": "62f322ef082fcc3049e9130f", + "username": "Robert Siemer", + "email": "Robert Siemer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29870, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322ef082fcc3049e91310", + "username": "Daniel Sokolowski", + "email": "Daniel Sokolowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11379, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90138"]] + }, + { + "_id": "62f322ef082fcc3049e91311", + "username": "Moslem Shahsavan", + "email": "Moslem Shahsavan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1091, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013b"]] + }, + { + "_id": "62f322ef082fcc3049e91312", + "username": "Vipul Jain", + "email": "Vipul Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013c"]] + }, + { + "_id": "62f322f0082fcc3049e91313", + "username": "Ankur Soni", + "email": "Ankur Soni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5317, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013e"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050f"] + ] + }, + { + "_id": "62f322f0082fcc3049e91315", + "username": "pushkin", + "email": "pushkin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8655, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f0082fcc3049e91316", + "username": "Nurlan", + "email": "Nurlan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013d"]] + }, + { + "_id": "62f322f0082fcc3049e91317", + "username": "N Randhawa", + "email": "N Randhawa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7863, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e9013f"]] + }, + { + "_id": "62f322f0082fcc3049e91318", + "username": "Daniel Viglione", + "email": "Daniel Viglione@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6999, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90140"]] + }, + { + "_id": "62f322f0082fcc3049e9131a", + "username": "Mile Mijatović", + "email": "Mile Mijatović@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2555, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90141"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904dd"] + ] + }, + { + "_id": "62f322f0082fcc3049e9131b", + "username": "Lucian", + "email": "Lucian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 682, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90142"]] + }, + { + "_id": "62f322f0082fcc3049e9131c", + "username": "daCoda", + "email": "daCoda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3129, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90143"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91046"] + ] + }, + { + "_id": "62f322f0082fcc3049e9131d", + "username": "Rafael Herscovici", + "email": "Rafael Herscovici@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15893, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90144"]] + }, + { + "_id": "62f322f0082fcc3049e9131e", + "username": "Piklu Dey", + "email": "Piklu Dey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 200, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90145"]] + }, + { + "_id": "62f322f1082fcc3049e91320", + "username": "Sarvar N", + "email": "Sarvar N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11003, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90148"]] + }, + { + "_id": "62f322f1082fcc3049e91321", + "username": "Hasan Sefa Ozalp", + "email": "Hasan Sefa Ozalp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4814, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90147"]] + }, + { + "_id": "62f322f1082fcc3049e91322", + "username": "Taib Islam Dipu", + "email": "Taib Islam Dipu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 308, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb6", "62f321be082fcc3049e90149"]] + }, + { + "_id": "62f322f1082fcc3049e91323", + "username": "TM.", + "email": "TM.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103786, + "questionIds": ["62f321bb082fcc3049e8feb6"], + "answerIds": [] + }, + { + "_id": "62f322f1082fcc3049e91324", + "username": "Andreas Grech", + "email": "Andreas Grech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103397, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014a"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ac"] + ] + }, + { + "_id": "62f322f1082fcc3049e91325", + "username": "Sean", + "email": "Sean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2175, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014b"]] + }, + { + "_id": "62f322f1082fcc3049e91326", + "username": "Doctor Jones", + "email": "Doctor Jones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20616, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014c"]] + }, + { + "_id": "62f322f1082fcc3049e91327", + "username": "Constantin", + "email": "Constantin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26788, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014d"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a86"] + ] + }, + { + "_id": "62f322f1082fcc3049e91328", + "username": "Simon Scarfe", + "email": "Simon Scarfe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9100, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014e"]] + }, + { + "_id": "62f322f1082fcc3049e9132a", + "username": "Garrett", + "email": "Garrett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2856, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043b"], + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90793"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90968"] + ] + }, + { + "_id": "62f322f1082fcc3049e9132b", + "username": "Thomas Hansen", + "email": "Thomas Hansen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5523, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9014f"]] + }, + { + "_id": "62f322f1082fcc3049e9132c", + "username": "Philippe Leybaert", + "email": "Philippe Leybaert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 163959, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90150"]] + }, + { + "_id": "62f322f2082fcc3049e9132d", + "username": "Dimitar", + "email": "Dimitar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90152"]] + }, + { + "_id": "62f322f2082fcc3049e9132e", + "username": "Pop Catalin", + "email": "Pop Catalin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60013, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90154"]] + }, + { + "_id": "62f322f2082fcc3049e9132f", + "username": "Shiki", + "email": "Shiki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16578, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90153"]] + }, + { + "_id": "62f322f2082fcc3049e91330", + "username": "Niraj CHoubey", + "email": "Niraj CHoubey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 457, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90156"]] + }, + { + "_id": "62f322f2082fcc3049e91331", + "username": "Paul Butcher", + "email": "Paul Butcher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6824, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90155"]] + }, + { + "_id": "62f322f2082fcc3049e91333", + "username": "Daniel", + "email": "Daniel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 880, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90157"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91040"] + ] + }, + { + "_id": "62f322f2082fcc3049e91334", + "username": "CuongHuyTo", + "email": "CuongHuyTo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90158"]] + }, + { + "_id": "62f322f2082fcc3049e91335", + "username": "mar10", + "email": "mar10@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13465, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015a"], + ["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d4"] + ] + }, + { + "_id": "62f322f2082fcc3049e91336", + "username": "ashes", + "email": "ashes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 611, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90159"]] + }, + { + "_id": "62f322f3082fcc3049e91337", + "username": "Harry He", + "email": "Harry He@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1745, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015c"]] + }, + { + "_id": "62f322f3082fcc3049e91338", + "username": "user2601995", + "email": "user2601995@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6035, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015d"]] + }, + { + "_id": "62f322f3082fcc3049e91339", + "username": "Mr.G", + "email": "Mr.G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3263, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015e"]] + }, + { + "_id": "62f322f3082fcc3049e9133a", + "username": "vivek_nk", + "email": "vivek_nk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1580, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90160"]] + }, + { + "_id": "62f322f3082fcc3049e9133b", + "username": "Christian Hagelid", + "email": "Christian Hagelid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8187, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9015f"]] + }, + { + "_id": "62f322f3082fcc3049e9133c", + "username": "garakchy", + "email": "garakchy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 514, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90162"]] + }, + { + "_id": "62f322f3082fcc3049e9133e", + "username": "imkzh", + "email": "imkzh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 201, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f3082fcc3049e91340", + "username": "SNag", + "email": "SNag@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16901, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90161"]] + }, + { + "_id": "62f322f3082fcc3049e91342", + "username": "Feuermurmel", + "email": "Feuermurmel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f3082fcc3049e91343", + "username": "Aniket Thakur", + "email": "Aniket Thakur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64129, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90163"]] + }, + { + "_id": "62f322f3082fcc3049e91344", + "username": "Sake Salverda", + "email": "Sake Salverda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 575, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90164"]] + }, + { + "_id": "62f322f4082fcc3049e91345", + "username": "Amit", + "email": "Amit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 797, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90165"], + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908de"] + ] + }, + { + "_id": "62f322f4082fcc3049e91346", + "username": "hopper", + "email": "hopper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90166"]] + }, + { + "_id": "62f322f4082fcc3049e91347", + "username": "Vikas", + "email": "Vikas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4183, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90167"], + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90978"] + ] + }, + { + "_id": "62f322f4082fcc3049e91348", + "username": "Akshay Khale", + "email": "Akshay Khale@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7829, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90168"]] + }, + { + "_id": "62f322f4082fcc3049e91349", + "username": "Samar Panda", + "email": "Samar Panda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3946, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016a"]] + }, + { + "_id": "62f322f4082fcc3049e9134a", + "username": "CodeFarmer", + "email": "CodeFarmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2528, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90169"]] + }, + { + "_id": "62f322f4082fcc3049e9134b", + "username": "Alex Gray", + "email": "Alex Gray@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016c"]] + }, + { + "_id": "62f322f4082fcc3049e9134c", + "username": "Dmitri Pavlutin", + "email": "Dmitri Pavlutin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16785, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016b"]] + }, + { + "_id": "62f322f4082fcc3049e9134d", + "username": "Alexandr", + "email": "Alexandr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5120, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016d"]] + }, + { + "_id": "62f322f4082fcc3049e9134e", + "username": "yanguya995", + "email": "yanguya995@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 145, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016e"]] + }, + { + "_id": "62f322f5082fcc3049e9134f", + "username": "Orri Scott", + "email": "Orri Scott@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e9016f"]] + }, + { + "_id": "62f322f5082fcc3049e91350", + "username": "Rotimi", + "email": "Rotimi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4720, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90170"]] + }, + { + "_id": "62f322f5082fcc3049e91352", + "username": "mrmr68", + "email": "mrmr68@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 178, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e91353", + "username": "Sharad Kale", + "email": "Sharad Kale@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90171"]] + }, + { + "_id": "62f322f5082fcc3049e91354", + "username": "RïshïKêsh Kümar", + "email": "RïshïKêsh Kümar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4466, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90173"]] + }, + { + "_id": "62f322f5082fcc3049e91355", + "username": "Narendra Kalekar", + "email": "Narendra Kalekar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 110, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90174"]] + }, + { + "_id": "62f322f5082fcc3049e91356", + "username": "Neil Meyer", + "email": "Neil Meyer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb7", "62f321be082fcc3049e90175"]] + }, + { + "_id": "62f322f5082fcc3049e91357", + "username": "bcasp", + "email": "bcasp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57417, + "questionIds": ["62f321bb082fcc3049e8feb7"], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e91359", + "username": "iPzard", + "email": "iPzard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1811, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e9135a", + "username": "Ben Scheirman", + "email": "Ben Scheirman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39962, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90177"]] + }, + { + "_id": "62f322f5082fcc3049e9135d", + "username": "Gautam Parmar", + "email": "Gautam Parmar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 563, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e9135f", + "username": "Iasmini Gomes", + "email": "Iasmini Gomes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e91361", + "username": "Code Cooker", + "email": "Code Cooker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 773, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f5082fcc3049e91363", + "username": "Gabriel Chaves Becchi", + "email": "Gabriel Chaves Becchi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 412, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e91364", + "username": "Paolo Bergantino", + "email": "Paolo Bergantino@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 469628, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017a"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e33"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f8c"] + ] + }, + { + "_id": "62f322f6082fcc3049e91365", + "username": "Adam McKee", + "email": "Adam McKee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 792, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017b"]] + }, + { + "_id": "62f322f6082fcc3049e91367", + "username": "tchrist", + "email": "tchrist@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 76946, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e91368", + "username": "Vikram", + "email": "Vikram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6807, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017c"]] + }, + { + "_id": "62f322f6082fcc3049e91369", + "username": "jacobangel", + "email": "jacobangel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6748, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017d"]] + }, + { + "_id": "62f322f6082fcc3049e9136b", + "username": "Esteban Küber", + "email": "Esteban Küber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017e"]] + }, + { + "_id": "62f322f6082fcc3049e9136c", + "username": "Félix Saparelli", + "email": "Félix Saparelli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8170, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90180"]] + }, + { + "_id": "62f322f6082fcc3049e9136e", + "username": "Slbox", + "email": "Slbox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8091, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e9136f", + "username": "Eric Schoonover", + "email": "Eric Schoonover@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45890, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9017f"]] + }, + { + "_id": "62f322f6082fcc3049e91370", + "username": "Miloš Rašić", + "email": "Miloš Rašić@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90181"]] + }, + { + "_id": "62f322f6082fcc3049e91372", + "username": "KNP", + "email": "KNP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e91373", + "username": "Anoop", + "email": "Anoop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22614, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90182"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90894"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b59"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b69"] + ] + }, + { + "_id": "62f322f6082fcc3049e91375", + "username": "Mohit Atray", + "email": "Mohit Atray@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 441, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e91377", + "username": "Merc", + "email": "Merc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15394, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f6082fcc3049e91379", + "username": "Jaymon", + "email": "Jaymon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4768, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90179"]] + }, + { + "_id": "62f322f7082fcc3049e9137b", + "username": "cazlab", + "email": "cazlab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 788, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e9137c", + "username": "Zo72", + "email": "Zo72@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13983, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90183"]] + }, + { + "_id": "62f322f7082fcc3049e9137d", + "username": "Steve C", + "email": "Steve C@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2448, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90185"]] + }, + { + "_id": "62f322f7082fcc3049e9137f", + "username": "ruohola", + "email": "ruohola@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19659, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91381", + "username": "Jose G.", + "email": "Jose G.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91383", + "username": "C. Lee", + "email": "C. Lee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90184"]] + }, + { + "_id": "62f322f7082fcc3049e91385", + "username": "Ikrom", + "email": "Ikrom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4335, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91386", + "username": "Darren Cato", + "email": "Darren Cato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1374, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90186"]] + }, + { + "_id": "62f322f7082fcc3049e91389", + "username": "Ryan Taylor", + "email": "Ryan Taylor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11321, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90188"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d5"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90abc"] + ] + }, + { + "_id": "62f322f7082fcc3049e9138a", + "username": "amit kate", + "email": "amit kate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90187"]] + }, + { + "_id": "62f322f7082fcc3049e9138b", + "username": "Tugrul", + "email": "Tugrul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018a"]] + }, + { + "_id": "62f322f7082fcc3049e9138d", + "username": "Boldewyn", + "email": "Boldewyn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79222, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90189"]] + }, + { + "_id": "62f322f7082fcc3049e9138f", + "username": "marcb", + "email": "marcb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 142, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91392", + "username": "oelna", + "email": "oelna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1976, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91393", + "username": "Linkmichiel", + "email": "Linkmichiel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2090, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018b"]] + }, + { + "_id": "62f322f7082fcc3049e91395", + "username": "D.A.H", + "email": "D.A.H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 824, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e91397", + "username": "pmiranda", + "email": "pmiranda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6411, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f7082fcc3049e9139b", + "username": "Bob van Luijt", + "email": "Bob van Luijt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018c"]] + }, + { + "_id": "62f322f8082fcc3049e9139d", + "username": "iwazovsky", + "email": "iwazovsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1890, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f8082fcc3049e9139f", + "username": "Aravin", + "email": "Aravin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6012, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f8082fcc3049e913a1", + "username": "Colonel Panic", + "email": "Colonel Panic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127544, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018e"]] + }, + { + "_id": "62f322f8082fcc3049e913a2", + "username": "Simon Steele", + "email": "Simon Steele@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11478, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9018f"]] + }, + { + "_id": "62f322f8082fcc3049e913a3", + "username": "pera", + "email": "pera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 858, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90190"]] + }, + { + "_id": "62f322f8082fcc3049e913a4", + "username": "Orchid", + "email": "Orchid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 213, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90192"]] + }, + { + "_id": "62f322f8082fcc3049e913a5", + "username": "Calahad", + "email": "Calahad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1308, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90191"]] + }, + { + "_id": "62f322f8082fcc3049e913a6", + "username": "Ferrakkem Bhuiyan", + "email": "Ferrakkem Bhuiyan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2601, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90194"]] + }, + { + "_id": "62f322f8082fcc3049e913a8", + "username": "Paul Go", + "email": "Paul Go@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2462, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f8082fcc3049e913a9", + "username": "Neil Thompson", + "email": "Neil Thompson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6316, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90193"]] + }, + { + "_id": "62f322f8082fcc3049e913ab", + "username": "kjpires", + "email": "kjpires@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 710, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f8082fcc3049e913ac", + "username": "Yogesh Jindal", + "email": "Yogesh Jindal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 582, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90196"]] + }, + { + "_id": "62f322f9082fcc3049e913ad", + "username": "zeros-and-ones", + "email": "zeros-and-ones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90198"]] + }, + { + "_id": "62f322f9082fcc3049e913af", + "username": "Michael Schilling", + "email": "Michael Schilling@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 332, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f9082fcc3049e913b0", + "username": "Racoon", + "email": "Racoon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90197"]] + }, + { + "_id": "62f322f9082fcc3049e913b3", + "username": "Eric Bishard", + "email": "Eric Bishard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5041, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f322f9082fcc3049e913b4", + "username": "Vitalii Fedorenko", + "email": "Vitalii Fedorenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105526, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e90199"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065b"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a0"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf8"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea3"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f8f"] + ] + }, + { + "_id": "62f322f9082fcc3049e913b5", + "username": "Anil Singhania", + "email": "Anil Singhania@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 729, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019a"]] + }, + { + "_id": "62f322f9082fcc3049e913b6", + "username": "user2081554", + "email": "user2081554@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019b"]] + }, + { + "_id": "62f322f9082fcc3049e913b7", + "username": "jaydev thakkar", + "email": "jaydev thakkar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019e"]] + }, + { + "_id": "62f322f9082fcc3049e913b8", + "username": "DestyNova", + "email": "DestyNova@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 730, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e9019f"]] + }, + { + "_id": "62f322f9082fcc3049e913b9", + "username": "Dinesh Devkota", + "email": "Dinesh Devkota@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a0"]] + }, + { + "_id": "62f32337082fcc3049e913ba", + "username": "bman", + "email": "bman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ab"]] + }, + { + "_id": "62f32337082fcc3049e913bb", + "username": "Negin", + "email": "Negin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2262, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ac"]] + }, + { + "_id": "62f32337082fcc3049e913bc", + "username": "Jangli Coder", + "email": "Jangli Coder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ae"]] + }, + { + "_id": "62f32337082fcc3049e913bd", + "username": "Himanshu Teotia", + "email": "Himanshu Teotia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1896, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ad"]] + }, + { + "_id": "62f32337082fcc3049e913bf", + "username": "jcollum", + "email": "jcollum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40767, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32337082fcc3049e913c0", + "username": "takatan", + "email": "takatan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b0"]] + }, + { + "_id": "62f32337082fcc3049e913c2", + "username": "Agi Hammerthief", + "email": "Agi Hammerthief@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2023, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e2"]] + }, + { + "_id": "62f32337082fcc3049e913c3", + "username": "Abhay Shiro", + "email": "Abhay Shiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2940, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901af"]] + }, + { + "_id": "62f32337082fcc3049e913c4", + "username": "Liberateur", + "email": "Liberateur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b2"]] + }, + { + "_id": "62f32337082fcc3049e913c6", + "username": "Kirit Modi", + "email": "Kirit Modi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22559, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b4"]] + }, + { + "_id": "62f32337082fcc3049e913c7", + "username": "Behnam Mohammadi", + "email": "Behnam Mohammadi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5906, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b3"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90387"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d16"] + ] + }, + { + "_id": "62f32338082fcc3049e913c8", + "username": "hmharsh3", + "email": "hmharsh3@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 185, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b5"]] + }, + { + "_id": "62f32338082fcc3049e913ca", + "username": "Agung Sudrajat Supriatna", + "email": "Agung Sudrajat Supriatna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32338082fcc3049e913cb", + "username": "Juan Pablo", + "email": "Juan Pablo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b6"]] + }, + { + "_id": "62f32338082fcc3049e913cc", + "username": "Mizanur Rahaman", + "email": "Mizanur Rahaman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b7"]] + }, + { + "_id": "62f32338082fcc3049e913cd", + "username": "Menelaos Kotsollaris", + "email": "Menelaos Kotsollaris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5264, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b8"]] + }, + { + "_id": "62f32338082fcc3049e913ce", + "username": "Sbbs", + "email": "Sbbs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1500, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ba"]] + }, + { + "_id": "62f32338082fcc3049e913d0", + "username": "gre_gor", + "email": "gre_gor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6258, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32338082fcc3049e913d1", + "username": "Prince Dholakiya", + "email": "Prince Dholakiya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3028, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901b9"]] + }, + { + "_id": "62f32338082fcc3049e913d2", + "username": "aabiro", + "email": "aabiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3148, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901bb"]] + }, + { + "_id": "62f32338082fcc3049e913d3", + "username": "mpyw", + "email": "mpyw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5353, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901bc"]] + }, + { + "_id": "62f32338082fcc3049e913d4", + "username": "egor518", + "email": "egor518@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2552, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901bd"]] + }, + { + "_id": "62f32338082fcc3049e913d5", + "username": "Kavan Fatehi", + "email": "Kavan Fatehi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901be"]] + }, + { + "_id": "62f32339082fcc3049e913d6", + "username": "Khaliq Izrail", + "email": "Khaliq Izrail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901bf"]] + }, + { + "_id": "62f32339082fcc3049e913d8", + "username": "David Mårtensson", + "email": "David Mårtensson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7440, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32339082fcc3049e913d9", + "username": "tecnocrata", + "email": "tecnocrata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 881, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c0"]] + }, + { + "_id": "62f32339082fcc3049e913da", + "username": "isapir", + "email": "isapir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18108, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c1"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc0"] + ] + }, + { + "_id": "62f32339082fcc3049e913db", + "username": "ranieribt", + "email": "ranieribt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1220, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c2"]] + }, + { + "_id": "62f32339082fcc3049e913dd", + "username": "Iman", + "email": "Iman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 533, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32339082fcc3049e913de", + "username": "karlzafiris", + "email": "karlzafiris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c3"]] + }, + { + "_id": "62f32339082fcc3049e913df", + "username": "B. Bohdan", + "email": "B. Bohdan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 462, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c4"]] + }, + { + "_id": "62f32339082fcc3049e913e0", + "username": "Antonio", + "email": "Antonio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c6"]] + }, + { + "_id": "62f32339082fcc3049e913e1", + "username": "Raheel", + "email": "Raheel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8154, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c5"]] + }, + { + "_id": "62f32339082fcc3049e913e2", + "username": "Pax", + "email": "Pax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c7"]] + }, + { + "_id": "62f32339082fcc3049e913e3", + "username": "Nicolas Zozol", + "email": "Nicolas Zozol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6925, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c8"]] + }, + { + "_id": "62f3233a082fcc3049e913e4", + "username": "Idan", + "email": "Idan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2661, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901c9"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb5"] + ] + }, + { + "_id": "62f3233a082fcc3049e913e5", + "username": "jimmont", + "email": "jimmont@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1999, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ca"], + ["62f321bb082fcc3049e8feb9", "62f32a0b082fcc3049e92f8d"] + ] + }, + { + "_id": "62f3233a082fcc3049e913e7", + "username": "Eboubaker", + "email": "Eboubaker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901cb"]] + }, + { + "_id": "62f3233a082fcc3049e913e9", + "username": "David Wheatley", + "email": "David Wheatley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233a082fcc3049e913ea", + "username": "Rajib Chy", + "email": "Rajib Chy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 668, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901cc"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066d"] + ] + }, + { + "_id": "62f3233a082fcc3049e913eb", + "username": "Renish Gotecha", + "email": "Renish Gotecha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1802, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901cd"]] + }, + { + "_id": "62f3233a082fcc3049e913ed", + "username": "Xizam", + "email": "Xizam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233a082fcc3049e913ef", + "username": "Mattia Rasulo", + "email": "Mattia Rasulo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 928, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901ce"]] + }, + { + "_id": "62f3233a082fcc3049e913f0", + "username": "user1843640", + "email": "user1843640@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d0"]] + }, + { + "_id": "62f3233a082fcc3049e913f1", + "username": "endyourif", + "email": "endyourif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2088, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901cf"]] + }, + { + "_id": "62f3233a082fcc3049e913f2", + "username": "Jaskaran Singh", + "email": "Jaskaran Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 854, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d1"]] + }, + { + "_id": "62f3233a082fcc3049e913f3", + "username": "Ebrahim", + "email": "Ebrahim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1574, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d2"]] + }, + { + "_id": "62f3233b082fcc3049e913f4", + "username": "FDisk", + "email": "FDisk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7569, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d4"], + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2a"] + ] + }, + { + "_id": "62f3233b082fcc3049e913f5", + "username": "Nazmul Haque", + "email": "Nazmul Haque@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d3"]] + }, + { + "_id": "62f3233b082fcc3049e913f6", + "username": "Force Bolt", + "email": "Force Bolt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 931, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d6"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023c"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b4"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90705"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087e"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90941"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a89"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f97"] + ] + }, + { + "_id": "62f3233b082fcc3049e913f7", + "username": "danday74", + "email": "danday74@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46841, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d5"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90636"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90863"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd6"] + ] + }, + { + "_id": "62f3233b082fcc3049e913f8", + "username": "Roman Bondar", + "email": "Roman Bondar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d8"]] + }, + { + "_id": "62f3233b082fcc3049e913f9", + "username": "Guilherme Santana", + "email": "Guilherme Santana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901d7"]] + }, + { + "_id": "62f3233b082fcc3049e913fb", + "username": "PatrikAkerstrand", + "email": "PatrikAkerstrand@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44779, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901d9"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e34"] + ] + }, + { + "_id": "62f3233b082fcc3049e913fc", + "username": "Quentin", + "email": "Quentin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 867879, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901da"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b8d"] + ] + }, + { + "_id": "62f3233b082fcc3049e913fd", + "username": "joidegn", + "email": "joidegn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1058, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901db"]] + }, + { + "_id": "62f3233c082fcc3049e913fe", + "username": "Poonam", + "email": "Poonam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4508, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901dd"]] + }, + { + "_id": "62f3233c082fcc3049e913ff", + "username": "Micka", + "email": "Micka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e0"]] + }, + { + "_id": "62f3233c082fcc3049e91400", + "username": "nmoliveira", + "email": "nmoliveira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1709, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901df"]] + }, + { + "_id": "62f3233c082fcc3049e91401", + "username": "Federico Piragua", + "email": "Federico Piragua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 667, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e1"]] + }, + { + "_id": "62f3233c082fcc3049e91402", + "username": "Rajesh Paul", + "email": "Rajesh Paul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6462, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e2"]] + }, + { + "_id": "62f3233c082fcc3049e91403", + "username": "Daniel W.", + "email": "Daniel W.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29522, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e4"], + ["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ebf"] + ] + }, + { + "_id": "62f3233c082fcc3049e91404", + "username": "Priyanshu Chauhan", + "email": "Priyanshu Chauhan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4949, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e5"]] + }, + { + "_id": "62f3233c082fcc3049e91405", + "username": "Volkan Seçkin Akbayır", + "email": "Volkan Seçkin Akbayır@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 654, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e6"]] + }, + { + "_id": "62f3233c082fcc3049e91406", + "username": "gaby de wilde", + "email": "gaby de wilde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1253, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901de"]] + }, + { + "_id": "62f3233d082fcc3049e91407", + "username": "Anil Arya", + "email": "Anil Arya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3020, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e8"]] + }, + { + "_id": "62f3233d082fcc3049e91408", + "username": "Zaz", + "email": "Zaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43807, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e7"]] + }, + { + "_id": "62f3233d082fcc3049e91409", + "username": "anteAdamovic", + "email": "anteAdamovic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1452, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901e9"]] + }, + { + "_id": "62f3233d082fcc3049e9140a", + "username": "Murtuza Husain", + "email": "Murtuza Husain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 176, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901ea"]] + }, + { + "_id": "62f3233d082fcc3049e9140b", + "username": "John", + "email": "John@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901eb"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cdd"] + ] + }, + { + "_id": "62f3233d082fcc3049e9140c", + "username": "Harun Or Rashid", + "email": "Harun Or Rashid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4829, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901ec"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a3"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90997"] + ] + }, + { + "_id": "62f3233d082fcc3049e9140d", + "username": "Nouman Dilshad", + "email": "Nouman Dilshad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 854, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901ed"], + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90229"], + ["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa4"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081f"] + ] + }, + { + "_id": "62f3233d082fcc3049e9140e", + "username": "Peko Chan", + "email": "Peko Chan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 259, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901ee"]] + }, + { + "_id": "62f3233d082fcc3049e9140f", + "username": "arul prince", + "email": "arul prince@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 193, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901ef"]] + }, + { + "_id": "62f3233e082fcc3049e91410", + "username": "subhashish negi", + "email": "subhashish negi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f1"]] + }, + { + "_id": "62f3233e082fcc3049e91411", + "username": "antelove", + "email": "antelove@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2696, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f2"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906d0"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f4"] + ] + }, + { + "_id": "62f3233e082fcc3049e91412", + "username": "Mustafa Kunwa", + "email": "Mustafa Kunwa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 813, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f3"]] + }, + { + "_id": "62f3233e082fcc3049e91413", + "username": "ankitkanojia", + "email": "ankitkanojia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2972, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f5"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae8"] + ] + }, + { + "_id": "62f3233e082fcc3049e91414", + "username": "mdmundo", + "email": "mdmundo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 946, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f8"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df7"] + ] + }, + { + "_id": "62f3233e082fcc3049e91415", + "username": "Felipe Chernicharo", + "email": "Felipe Chernicharo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2344, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feb8", "62f321be082fcc3049e901f7"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087d"] + ] + }, + { + "_id": "62f3233e082fcc3049e91416", + "username": "Dante1986", + "email": "Dante1986@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56233, + "questionIds": ["62f321bb082fcc3049e8feb8"], + "answerIds": [] + }, + { + "_id": "62f3233e082fcc3049e91417", + "username": "scronide", + "email": "scronide@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11766, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901fa"]] + }, + { + "_id": "62f3233e082fcc3049e91418", + "username": "Adam A", + "email": "Adam A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14240, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901f9"]] + }, + { + "_id": "62f3233f082fcc3049e91419", + "username": "SolutionYogi", + "email": "SolutionYogi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31102, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901fc"]] + }, + { + "_id": "62f3233f082fcc3049e9141a", + "username": "Donnie DeBoer", + "email": "Donnie DeBoer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901fd"]] + }, + { + "_id": "62f3233f082fcc3049e9141c", + "username": "Abion47", + "email": "Abion47@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19327, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233f082fcc3049e9141e", + "username": "Kunal Tanwar", + "email": "Kunal Tanwar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1047, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233f082fcc3049e9141f", + "username": "Matthew Crumley", + "email": "Matthew Crumley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99413, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e901fe"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068a"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fd8"] + ] + }, + { + "_id": "62f3233f082fcc3049e91420", + "username": "jesal", + "email": "jesal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7644, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90200"]] + }, + { + "_id": "62f3233f082fcc3049e91423", + "username": "user1002973", + "email": "user1002973@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2020, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233f082fcc3049e91424", + "username": "Owen", + "email": "Owen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4009, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90201"]] + }, + { + "_id": "62f3233f082fcc3049e91425", + "username": "Raseela", + "email": "Raseela@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90202"]] + }, + { + "_id": "62f3233f082fcc3049e91426", + "username": "rakslice", + "email": "rakslice@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8444, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90203"], + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e58"] + ] + }, + { + "_id": "62f3233f082fcc3049e91428", + "username": "Sunil Garg", + "email": "Sunil Garg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12965, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3233f082fcc3049e91429", + "username": "Cory Gross", + "email": "Cory Gross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36353, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90204"]] + }, + { + "_id": "62f32340082fcc3049e9142a", + "username": "SiwachGaurav", + "email": "SiwachGaurav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1858, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90206"]] + }, + { + "_id": "62f32340082fcc3049e9142b", + "username": "pkdkk", + "email": "pkdkk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3697, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90205"]] + }, + { + "_id": "62f32340082fcc3049e9142d", + "username": "Jonathan ANTOINE", + "email": "Jonathan ANTOINE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8792, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32340082fcc3049e9142e", + "username": "Antonio Mirarchi", + "email": "Antonio Mirarchi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90207"]] + }, + { + "_id": "62f3237d082fcc3049e9142f", + "username": "Termininja", + "email": "Termininja@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6257, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020f"]] + }, + { + "_id": "62f3237d082fcc3049e91430", + "username": "tk_", + "email": "tk_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15039, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90210"]] + }, + { + "_id": "62f3237d082fcc3049e91431", + "username": "Nivesh Saharan", + "email": "Nivesh Saharan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90212"]] + }, + { + "_id": "62f3237d082fcc3049e91432", + "username": "Sandeep Gantait", + "email": "Sandeep Gantait@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 801, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90211"]] + }, + { + "_id": "62f3237d082fcc3049e91434", + "username": "theWalker", + "email": "theWalker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2024, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90213"]] + }, + { + "_id": "62f3237d082fcc3049e91435", + "username": "Cheezy Code", + "email": "Cheezy Code@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1625, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90214"]] + }, + { + "_id": "62f3237d082fcc3049e91436", + "username": "C. Morgan", + "email": "C. Morgan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90216"]] + }, + { + "_id": "62f3237d082fcc3049e91437", + "username": "Emilio Grisolía", + "email": "Emilio Grisolía@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90215"]] + }, + { + "_id": "62f3237d082fcc3049e91438", + "username": "User", + "email": "User@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90217"]] + }, + { + "_id": "62f3237e082fcc3049e91439", + "username": "KARTHIKEYAN.A", + "email": "KARTHIKEYAN.A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15136, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021a"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905da"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907fb"], + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c78"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93149"] + ] + }, + { + "_id": "62f3237e082fcc3049e9143a", + "username": "mostafa elmadany", + "email": "mostafa elmadany@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90219"]] + }, + { + "_id": "62f3237e082fcc3049e9143b", + "username": "csomakk", + "email": "csomakk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5161, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021b"]] + }, + { + "_id": "62f3237e082fcc3049e9143c", + "username": "Andrés", + "email": "Andrés@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 679, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021c"]] + }, + { + "_id": "62f3237e082fcc3049e9143e", + "username": "TheAivis", + "email": "TheAivis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 181, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021d"]] + }, + { + "_id": "62f3237e082fcc3049e91440", + "username": "rak007", + "email": "rak007@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 945, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3237e082fcc3049e91441", + "username": "Ferie", + "email": "Ferie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9021e"]] + }, + { + "_id": "62f3237e082fcc3049e91442", + "username": "Arian Saputra", + "email": "Arian Saputra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90220"]] + }, + { + "_id": "62f3237e082fcc3049e91443", + "username": "Brijesh Kumar Kushwaha", + "email": "Brijesh Kumar Kushwaha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90222"]] + }, + { + "_id": "62f3237f082fcc3049e91444", + "username": "prime", + "email": "prime@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90223"]] + }, + { + "_id": "62f3237f082fcc3049e91445", + "username": "Adnan Toky", + "email": "Adnan Toky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1582, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90225"]] + }, + { + "_id": "62f3237f082fcc3049e91446", + "username": "Rinold", + "email": "Rinold@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 323, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90224"]] + }, + { + "_id": "62f3237f082fcc3049e91448", + "username": "MMMahdy-PAPION", + "email": "MMMahdy-PAPION@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 557, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3237f082fcc3049e91449", + "username": "VhsPiceros", + "email": "VhsPiceros@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 405, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90226"]] + }, + { + "_id": "62f3237f082fcc3049e9144c", + "username": "Luiz Felipe", + "email": "Luiz Felipe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1021, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3237f082fcc3049e9144d", + "username": "Jessie Lesbian", + "email": "Jessie Lesbian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1008, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90227"]] + }, + { + "_id": "62f3237f082fcc3049e9144e", + "username": "sajadre", + "email": "sajadre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1075, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90228"]] + }, + { + "_id": "62f3237f082fcc3049e9144f", + "username": "Indrajeet Singh", + "email": "Indrajeet Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2840, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022a"]] + }, + { + "_id": "62f3237f082fcc3049e91450", + "username": "Raghavendra S", + "email": "Raghavendra S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022b"]] + }, + { + "_id": "62f3237f082fcc3049e91451", + "username": "Stefan Steiger", + "email": "Stefan Steiger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 74353, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022c"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90395"], + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90722"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90848"] + ] + }, + { + "_id": "62f32380082fcc3049e91452", + "username": "Rakib Uddin", + "email": "Rakib Uddin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 802, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022d"]] + }, + { + "_id": "62f32380082fcc3049e91453", + "username": "CertainPerformance", + "email": "CertainPerformance@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 324202, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022e"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90824"] + ] + }, + { + "_id": "62f32380082fcc3049e91454", + "username": "Thomas Orlita", + "email": "Thomas Orlita@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9022f"]] + }, + { + "_id": "62f32380082fcc3049e91455", + "username": "Ws Memon", + "email": "Ws Memon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90230"]] + }, + { + "_id": "62f32380082fcc3049e91457", + "username": "Ali", + "email": "Ali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 251854, + "questionIds": ["62f321bb082fcc3049e8feba"], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d23"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dcb"] + ] + }, + { + "_id": "62f32380082fcc3049e91458", + "username": "y.kaf.", + "email": "y.kaf.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1197, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90231"]] + }, + { + "_id": "62f32380082fcc3049e91459", + "username": "Lova Chittumuri", + "email": "Lova Chittumuri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2487, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90232"]] + }, + { + "_id": "62f32380082fcc3049e9145a", + "username": "e102", + "email": "e102@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90233"]] + }, + { + "_id": "62f32380082fcc3049e9145b", + "username": "Shavais", + "email": "Shavais@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2342, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90234"]] + }, + { + "_id": "62f32380082fcc3049e9145c", + "username": "Matěj Štágl", + "email": "Matěj Štágl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 840, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90235"]] + }, + { + "_id": "62f32380082fcc3049e9145d", + "username": "Iftikhar Hussain", + "email": "Iftikhar Hussain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 183, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90236"]] + }, + { + "_id": "62f32381082fcc3049e9145e", + "username": "Asakkour Soufiane", + "email": "Asakkour Soufiane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90237"]] + }, + { + "_id": "62f32381082fcc3049e91460", + "username": "Khalid Khan", + "email": "Khalid Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2720, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32381082fcc3049e91462", + "username": "Mohit Yadav", + "email": "Mohit Yadav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 453, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90238"]] + }, + { + "_id": "62f32381082fcc3049e91463", + "username": "Chungmin Park", + "email": "Chungmin Park@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023a"]] + }, + { + "_id": "62f32381082fcc3049e91465", + "username": "raven", + "email": "raven@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2321, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32381082fcc3049e91467", + "username": "Nisharg Shah", + "email": "Nisharg Shah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90239"]] + }, + { + "_id": "62f32381082fcc3049e91468", + "username": "Satish Chandra Gupta", + "email": "Satish Chandra Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2300, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023b"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90534"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b1"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b10"] + ] + }, + { + "_id": "62f32381082fcc3049e91469", + "username": "francis", + "email": "francis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2429, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023d"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90361"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3d"] + ] + }, + { + "_id": "62f32381082fcc3049e9146a", + "username": "Oliver M Grech", + "email": "Oliver M Grech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2894, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023e"]] + }, + { + "_id": "62f32381082fcc3049e9146c", + "username": "pedro casas", + "email": "pedro casas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9023f"]] + }, + { + "_id": "62f32382082fcc3049e9146d", + "username": "karim79", + "email": "karim79@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 335370, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9024c"], + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90792"] + ] + }, + { + "_id": "62f32382082fcc3049e9146e", + "username": "Alecx", + "email": "Alecx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90241"]] + }, + { + "_id": "62f32382082fcc3049e91470", + "username": "Kevin Dark", + "email": "Kevin Dark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 449, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32382082fcc3049e91471", + "username": "xenon", + "email": "xenon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9024d"]] + }, + { + "_id": "62f32382082fcc3049e91473", + "username": "Kemuel Sanchez", + "email": "Kemuel Sanchez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 570, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32382082fcc3049e91474", + "username": "Prasad", + "email": "Prasad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57983, + "questionIds": ["62f321bb082fcc3049e8febc"], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9024e"]] + }, + { + "_id": "62f32382082fcc3049e91475", + "username": "tsw_mik", + "email": "tsw_mik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90250"]] + }, + { + "_id": "62f32382082fcc3049e91476", + "username": "Pradeep", + "email": "Pradeep@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 417, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9024f"]] + }, + { + "_id": "62f32382082fcc3049e91477", + "username": "Nertim", + "email": "Nertim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 380, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90251"]] + }, + { + "_id": "62f32382082fcc3049e91478", + "username": "ashish amatya", + "email": "ashish amatya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90252"]] + }, + { + "_id": "62f32382082fcc3049e91479", + "username": "Tim Abell", + "email": "Tim Abell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10323, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90253"]] + }, + { + "_id": "62f32382082fcc3049e9147b", + "username": "Kai Neuwerth", + "email": "Kai Neuwerth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 148, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32382082fcc3049e9147d", + "username": "Jean-Roch B.", + "email": "Jean-Roch B.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 407, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32382082fcc3049e9147e", + "username": "Bhanu Krishnan", + "email": "Bhanu Krishnan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3716, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90254"]] + }, + { + "_id": "62f32383082fcc3049e9147f", + "username": "SeanDowney", + "email": "SeanDowney@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16868, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90255"]] + }, + { + "_id": "62f32383082fcc3049e91480", + "username": "arviman", + "email": "arviman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4847, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90256"]] + }, + { + "_id": "62f32383082fcc3049e91481", + "username": "Dalius I", + "email": "Dalius I@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90257"]] + }, + { + "_id": "62f32383082fcc3049e91482", + "username": "Joshua Harris", + "email": "Joshua Harris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 399, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90258"]] + }, + { + "_id": "62f32383082fcc3049e91483", + "username": "fe_lix_", + "email": "fe_lix_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 928, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90259"]] + }, + { + "_id": "62f32383082fcc3049e91484", + "username": "Octavian A. Damiean", + "email": "Octavian A. Damiean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39167, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025a"]] + }, + { + "_id": "62f32383082fcc3049e91485", + "username": "Salman A", + "email": "Salman A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 250730, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025b"], + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a8"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b6"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909aa"], + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b08"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c41"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904cc"], + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a01"], + ["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be9"] + ] + }, + { + "_id": "62f32383082fcc3049e91486", + "username": "MDMoore313", + "email": "MDMoore313@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3056, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025c"]] + }, + { + "_id": "62f32383082fcc3049e91487", + "username": "Abdul Hamid", + "email": "Abdul Hamid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3202, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025d"]] + }, + { + "_id": "62f32383082fcc3049e91488", + "username": "user1996628", + "email": "user1996628@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025e"]] + }, + { + "_id": "62f32384082fcc3049e91489", + "username": "cauleyfj", + "email": "cauleyfj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90260"]] + }, + { + "_id": "62f32384082fcc3049e9148a", + "username": "Richard Maxwell", + "email": "Richard Maxwell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 498, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9025f"]] + }, + { + "_id": "62f32384082fcc3049e9148b", + "username": "Madan Sapkota", + "email": "Madan Sapkota@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23575, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90261"]] + }, + { + "_id": "62f32384082fcc3049e9148c", + "username": "ijarlax", + "email": "ijarlax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 515, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90262"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e3"] + ] + }, + { + "_id": "62f32384082fcc3049e9148d", + "username": "Rajesh Omanakuttan", + "email": "Rajesh Omanakuttan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6720, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90264"]] + }, + { + "_id": "62f32384082fcc3049e9148e", + "username": "Daryl H", + "email": "Daryl H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 606, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90263"]] + }, + { + "_id": "62f32384082fcc3049e9148f", + "username": "Nishant Kumar", + "email": "Nishant Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5907, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90265"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092d"] + ] + }, + { + "_id": "62f32384082fcc3049e91490", + "username": "Udit Bhardwaj", + "email": "Udit Bhardwaj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1761, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90266"]] + }, + { + "_id": "62f32384082fcc3049e91491", + "username": "subham.saha1004", + "email": "subham.saha1004@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 754, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90267"]] + }, + { + "_id": "62f32384082fcc3049e91492", + "username": "Somnath Kharat", + "email": "Somnath Kharat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3560, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90268"]] + }, + { + "_id": "62f32385082fcc3049e91493", + "username": "usayee", + "email": "usayee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026a"]] + }, + { + "_id": "62f32385082fcc3049e91494", + "username": "Tsonev", + "email": "Tsonev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 435, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90269"]] + }, + { + "_id": "62f32385082fcc3049e91495", + "username": "Khaled.K", + "email": "Khaled.K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5728, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026c"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc1"] + ] + }, + { + "_id": "62f32385082fcc3049e91496", + "username": "JJ_Coder4Hire", + "email": "JJ_Coder4Hire@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4498, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026b"]] + }, + { + "_id": "62f32385082fcc3049e91497", + "username": "Tarion", + "email": "Tarion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15295, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026e"]] + }, + { + "_id": "62f32385082fcc3049e91499", + "username": "vapcguy", + "email": "vapcguy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6651, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32385082fcc3049e9149a", + "username": "ungalcrys", + "email": "ungalcrys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4950, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026d"]] + }, + { + "_id": "62f32385082fcc3049e9149b", + "username": "Parth Chavda", + "email": "Parth Chavda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1791, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9026f"]] + }, + { + "_id": "62f32385082fcc3049e9149c", + "username": "NoWar", + "email": "NoWar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34955, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90270"], + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ec"] + ] + }, + { + "_id": "62f32385082fcc3049e9149d", + "username": "Jasper", + "email": "Jasper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 347, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90271"]] + }, + { + "_id": "62f32385082fcc3049e9149f", + "username": "bronze man", + "email": "bronze man@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32385082fcc3049e914a0", + "username": "Sudha", + "email": "Sudha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90272"]] + }, + { + "_id": "62f32386082fcc3049e914a1", + "username": "Bram", + "email": "Bram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90273"]] + }, + { + "_id": "62f32386082fcc3049e914a2", + "username": "zamoldar", + "email": "zamoldar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 520, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90274"]] + }, + { + "_id": "62f32386082fcc3049e914a3", + "username": "Ferhat KOÇER", + "email": "Ferhat KOÇER@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3464, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90275"]] + }, + { + "_id": "62f32386082fcc3049e914a4", + "username": "Jitendra Damor", + "email": "Jitendra Damor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 776, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90276"], + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b25"] + ] + }, + { + "_id": "62f32386082fcc3049e914a5", + "username": "argie cruz", + "email": "argie cruz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90277"]] + }, + { + "_id": "62f32386082fcc3049e914a6", + "username": "Vajira Lasantha", + "email": "Vajira Lasantha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90278"]] + }, + { + "_id": "62f32386082fcc3049e914a7", + "username": "Ormoz", + "email": "Ormoz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90279"]] + }, + { + "_id": "62f32386082fcc3049e914a8", + "username": "Muhammad Awais", + "email": "Muhammad Awais@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3877, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027c"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c20"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea7"] + ] + }, + { + "_id": "62f32387082fcc3049e914a9", + "username": "Vikash", + "email": "Vikash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3263, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027d"]] + }, + { + "_id": "62f32387082fcc3049e914aa", + "username": "Hamid N K", + "email": "Hamid N K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027e"]] + }, + { + "_id": "62f32387082fcc3049e914ab", + "username": "Lalji Dhameliya", + "email": "Lalji Dhameliya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1673, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9027f"]] + }, + { + "_id": "62f32387082fcc3049e914ac", + "username": "Iter Ator", + "email": "Iter Ator@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90280"]] + }, + { + "_id": "62f32387082fcc3049e914ad", + "username": "Sandeep Sherpur", + "email": "Sandeep Sherpur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1955, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90281"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90960"] + ] + }, + { + "_id": "62f32387082fcc3049e914ae", + "username": "Jacob", + "email": "Jacob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3382, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90282"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077e"] + ] + }, + { + "_id": "62f32387082fcc3049e914af", + "username": "Himanshu Upadhyay", + "email": "Himanshu Upadhyay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 713, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90283"]] + }, + { + "_id": "62f32387082fcc3049e914b0", + "username": "Ir Calif", + "email": "Ir Calif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 466, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90284"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9099a"] + ] + }, + { + "_id": "62f32387082fcc3049e914b1", + "username": "sedhal", + "email": "sedhal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 322, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90285"]] + }, + { + "_id": "62f32387082fcc3049e914b2", + "username": "abrahamcalf", + "email": "abrahamcalf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6455, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90286"]] + }, + { + "_id": "62f32388082fcc3049e914b3", + "username": "Javed Khan", + "email": "Javed Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 275, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90288"], + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90441"], + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90964"] + ] + }, + { + "_id": "62f32388082fcc3049e914b4", + "username": "Praneeth Madush", + "email": "Praneeth Madush@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 156, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e90287"]] + }, + { + "_id": "62f32388082fcc3049e914b5", + "username": "Ajay Katariya", + "email": "Ajay Katariya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 419, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9028a"]] + }, + { + "_id": "62f32388082fcc3049e914b6", + "username": "rcoro", + "email": "rcoro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 326, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9028c"]] + }, + { + "_id": "62f32388082fcc3049e914b7", + "username": "Dan Walters", + "email": "Dan Walters@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1078, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9028b"]] + }, + { + "_id": "62f32388082fcc3049e914b9", + "username": "era s'q", + "email": "era s'q@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 499, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32388082fcc3049e914ba", + "username": "ConroyP", + "email": "ConroyP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40072, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9028e"]] + }, + { + "_id": "62f32388082fcc3049e914bb", + "username": "Sarvesh Patel", + "email": "Sarvesh Patel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 133, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febc", "62f321bf082fcc3049e9028d"]] + }, + { + "_id": "62f32388082fcc3049e914bd", + "username": "Gershom Maes", + "email": "Gershom Maes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6471, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32388082fcc3049e914be", + "username": "Kamarey", + "email": "Kamarey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10588, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90290"]] + }, + { + "_id": "62f32388082fcc3049e914bf", + "username": "Mark Cidade", + "email": "Mark Cidade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 96494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9028f"]] + }, + { + "_id": "62f32389082fcc3049e914c1", + "username": "iMartin", + "email": "iMartin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32389082fcc3049e914c4", + "username": "Nate Levin", + "email": "Nate Levin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 773, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32389082fcc3049e914c5", + "username": "Alan", + "email": "Alan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 241, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90291"]] + }, + { + "_id": "62f32389082fcc3049e914c6", + "username": "Chris Broski", + "email": "Chris Broski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2261, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90294"]] + }, + { + "_id": "62f32389082fcc3049e914c7", + "username": "Dima", + "email": "Dima@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90293"]] + }, + { + "_id": "62f32389082fcc3049e914c8", + "username": "Page Notes", + "email": "Page Notes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90295"]] + }, + { + "_id": "62f32389082fcc3049e914cb", + "username": "Koushik Shom Choudhury", + "email": "Koushik Shom Choudhury@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 718, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32389082fcc3049e914cd", + "username": "Andy Carlson", + "email": "Andy Carlson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3219, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32389082fcc3049e914ce", + "username": "Sultan Shakir", + "email": "Sultan Shakir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90296"]] + }, + { + "_id": "62f32389082fcc3049e914cf", + "username": "gion_13", + "email": "gion_13@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90298"]] + }, + { + "_id": "62f32389082fcc3049e914d0", + "username": "Corban Brook", + "email": "Corban Brook@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21150, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90297"]] + }, + { + "_id": "62f32389082fcc3049e914d1", + "username": "Steve Tomlin", + "email": "Steve Tomlin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029a"]] + }, + { + "_id": "62f32389082fcc3049e914d2", + "username": "neatonk", + "email": "neatonk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e90299"]] + }, + { + "_id": "62f3238a082fcc3049e914d3", + "username": "Joe", + "email": "Joe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 187, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029b"], + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068d"] + ] + }, + { + "_id": "62f3238a082fcc3049e914d5", + "username": "Diederik", + "email": "Diederik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238a082fcc3049e914d6", + "username": "itsadok", + "email": "itsadok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029c"]] + }, + { + "_id": "62f3238a082fcc3049e914d8", + "username": "Danubian Sailor", + "email": "Danubian Sailor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238a082fcc3049e914d9", + "username": "user1547016", + "email": "user1547016@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029e"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064b"] + ] + }, + { + "_id": "62f3238a082fcc3049e914da", + "username": "Maël Nison", + "email": "Maël Nison@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6835, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029d"]] + }, + { + "_id": "62f3238a082fcc3049e914db", + "username": "Michael Uzquiano", + "email": "Michael Uzquiano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 297, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a0"]] + }, + { + "_id": "62f3238a082fcc3049e914dc", + "username": "opensas", + "email": "opensas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57286, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a1"]] + }, + { + "_id": "62f3238a082fcc3049e914dd", + "username": "pvorb", + "email": "pvorb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6895, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e9029f"]] + }, + { + "_id": "62f3238a082fcc3049e914e0", + "username": "Robin Whittleton", + "email": "Robin Whittleton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5982, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a4"]] + }, + { + "_id": "62f3238a082fcc3049e914e2", + "username": "user420667", + "email": "user420667@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6372, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238a082fcc3049e914e3", + "username": "Cody", + "email": "Cody@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9389, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a3"], + ["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d81"] + ] + }, + { + "_id": "62f3238a082fcc3049e914e4", + "username": "Daniel Lorenz", + "email": "Daniel Lorenz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3946, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a2"]] + }, + { + "_id": "62f3238b082fcc3049e914e6", + "username": "Steven Vachon", + "email": "Steven Vachon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3624, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a5"]] + }, + { + "_id": "62f3238b082fcc3049e914e8", + "username": "Josh from Qaribou", + "email": "Josh from Qaribou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6576, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914e9", + "username": "Tristian", + "email": "Tristian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a6"]] + }, + { + "_id": "62f3238b082fcc3049e914ea", + "username": "nathan rogers", + "email": "nathan rogers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 197, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a7"]] + }, + { + "_id": "62f3238b082fcc3049e914ec", + "username": "Zortext", + "email": "Zortext@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 481, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914ed", + "username": "andrew", + "email": "andrew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 420, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a8"]] + }, + { + "_id": "62f3238b082fcc3049e914ef", + "username": "mwhite", + "email": "mwhite@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1991, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914f1", + "username": "Meirion Hughes", + "email": "Meirion Hughes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23577, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914f3", + "username": "johannes_lalala", + "email": "johannes_lalala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 554, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914f5", + "username": "Buzinas", + "email": "Buzinas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902a9"]] + }, + { + "_id": "62f3238b082fcc3049e914f7", + "username": "basickarl", + "email": "basickarl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914f8", + "username": "Barry Staes", + "email": "Barry Staes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3758, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ac"]] + }, + { + "_id": "62f3238b082fcc3049e914fa", + "username": "rich remer", + "email": "rich remer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3271, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914fc", + "username": "Yichong", + "email": "Yichong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 679, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e914fd", + "username": "Bodhi Hu", + "email": "Bodhi Hu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 196, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ab"]] + }, + { + "_id": "62f3238b082fcc3049e914fe", + "username": "Shishir Arora", + "email": "Shishir Arora@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5053, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ae"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce0"] + ] + }, + { + "_id": "62f3238b082fcc3049e91500", + "username": "Galvani", + "email": "Galvani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 240, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3238b082fcc3049e91501", + "username": "Dan Atkinson", + "email": "Dan Atkinson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11080, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ad"]] + }, + { + "_id": "62f3238c082fcc3049e91502", + "username": "user3071643", + "email": "user3071643@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1246, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902af"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90660"] + ] + }, + { + "_id": "62f3238c082fcc3049e91503", + "username": "azerafati", + "email": "azerafati@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17585, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b0"]] + }, + { + "_id": "62f3238c082fcc3049e91505", + "username": "Soldeplata Saketos", + "email": "Soldeplata Saketos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2781, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90609"]] + }, + { + "_id": "62f3238c082fcc3049e91506", + "username": "Ashutosh Jha", + "email": "Ashutosh Jha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 151, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b2"]] + }, + { + "_id": "62f3238c082fcc3049e91508", + "username": "SAlidadi", + "email": "SAlidadi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b1"]] + }, + { + "_id": "62f3238c082fcc3049e91509", + "username": "Ihor Pavlyk", + "email": "Ihor Pavlyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 931, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b3"]] + }, + { + "_id": "62f3238c082fcc3049e9150b", + "username": "Harry", + "email": "Harry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90769"]] + }, + { + "_id": "62f3238c082fcc3049e9150c", + "username": "prograhammer", + "email": "prograhammer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19196, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b7"]] + }, + { + "_id": "62f323c9082fcc3049e9150d", + "username": "shobhit1", + "email": "shobhit1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 594, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b9"]] + }, + { + "_id": "62f323c9082fcc3049e9150f", + "username": "Antoniossss", + "email": "Antoniossss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29238, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91511", + "username": "SPG", + "email": "SPG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6001, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91513", + "username": "Phoenix", + "email": "Phoenix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 414, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91515", + "username": "Aykut Kllic", + "email": "Aykut Kllic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 858, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91516", + "username": "Mayur Agarwal", + "email": "Mayur Agarwal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 849, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ba"]] + }, + { + "_id": "62f323c9082fcc3049e91517", + "username": "Julez", + "email": "Julez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902bb"]] + }, + { + "_id": "62f323c9082fcc3049e9151b", + "username": "Steve Griffith", + "email": "Steve Griffith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902bd"]] + }, + { + "_id": "62f323c9082fcc3049e9151d", + "username": "Nikita Malyschkin", + "email": "Nikita Malyschkin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 624, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e9151e", + "username": "codeMonkey", + "email": "codeMonkey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3444, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902be"]] + }, + { + "_id": "62f323c9082fcc3049e91520", + "username": "Taugenichts", + "email": "Taugenichts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1287, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91522", + "username": "Vikram K", + "email": "Vikram K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2367, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c0"]] + }, + { + "_id": "62f323c9082fcc3049e91524", + "username": "zenw0lf", + "email": "zenw0lf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1185, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91526", + "username": "medBouzid", + "email": "medBouzid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6824, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e91527", + "username": "Parabolord", + "email": "Parabolord@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 192, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902bf"]] + }, + { + "_id": "62f323c9082fcc3049e91529", + "username": "kenanyildiz90", + "email": "kenanyildiz90@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e9152b", + "username": "Toivo Säwén", + "email": "Toivo Säwén@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1503, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323c9082fcc3049e9152c", + "username": "Tính Ngô Quang", + "email": "Tính Ngô Quang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4018, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c2"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90935"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c25"] + ] + }, + { + "_id": "62f323c9082fcc3049e9152d", + "username": "Jinu Joseph Daniel", + "email": "Jinu Joseph Daniel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5447, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c1"]] + }, + { + "_id": "62f323ca082fcc3049e9152e", + "username": "shunryu111", + "email": "shunryu111@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5367, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c4"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd3"] + ] + }, + { + "_id": "62f323ca082fcc3049e9152f", + "username": "Prasanth Jaya", + "email": "Prasanth Jaya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4077, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c3"]] + }, + { + "_id": "62f323ca082fcc3049e91530", + "username": "Mystical", + "email": "Mystical@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2151, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c6"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a44"] + ] + }, + { + "_id": "62f323ca082fcc3049e91531", + "username": "chandan gupta", + "email": "chandan gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1069, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c5"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a5"] + ] + }, + { + "_id": "62f323ca082fcc3049e91532", + "username": "shakthi nagaraj", + "email": "shakthi nagaraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c7"]] + }, + { + "_id": "62f323ca082fcc3049e91533", + "username": "Shidersz", + "email": "Shidersz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16371, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c8"]] + }, + { + "_id": "62f323ca082fcc3049e91534", + "username": "Kamyar", + "email": "Kamyar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 355, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902c9"]] + }, + { + "_id": "62f323ca082fcc3049e91536", + "username": "Ian", + "email": "Ian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5276, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323ca082fcc3049e91537", + "username": "Ankur Kedia", + "email": "Ankur Kedia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902cb"]] + }, + { + "_id": "62f323ca082fcc3049e91539", + "username": "KRIPA SHANKAR JHA", + "email": "KRIPA SHANKAR JHA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902ca"]] + }, + { + "_id": "62f323cb082fcc3049e9153a", + "username": "Robert Wills", + "email": "Robert Wills@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47943, + "questionIds": ["62f321bb082fcc3049e8febe"], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902cd"]] + }, + { + "_id": "62f323cb082fcc3049e9153b", + "username": "Steve Hansell", + "email": "Steve Hansell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16969, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ce"]] + }, + { + "_id": "62f323cb082fcc3049e9153f", + "username": "Przemek", + "email": "Przemek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3641, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90301"]] + }, + { + "_id": "62f323cb082fcc3049e91542", + "username": "raphie", + "email": "raphie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3269, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902cf"]] + }, + { + "_id": "62f323cb082fcc3049e91544", + "username": "tomdemuyt", + "email": "tomdemuyt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4482, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e91546", + "username": "Benubird", + "email": "Benubird@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17439, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e91548", + "username": "Travis Webb", + "email": "Travis Webb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14254, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e91549", + "username": "Murat Kucukosman", + "email": "Murat Kucukosman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 627, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d0"]] + }, + { + "_id": "62f323cb082fcc3049e9154a", + "username": "Jakub Januszkiewicz", + "email": "Jakub Januszkiewicz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d2"]] + }, + { + "_id": "62f323cb082fcc3049e9154c", + "username": "JimmyPena", + "email": "JimmyPena@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8660, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e9154e", + "username": "Malovich", + "email": "Malovich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 911, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e91550", + "username": "Magico", + "email": "Magico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2805, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e91552", + "username": "longda", + "email": "longda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9945, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902db"]] + }, + { + "_id": "62f323cb082fcc3049e91553", + "username": "Łukasz Kurowski", + "email": "Łukasz Kurowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 342, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d5"]] + }, + { + "_id": "62f323cb082fcc3049e91555", + "username": "monokrome", + "email": "monokrome@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1332, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d3"]] + }, + { + "_id": "62f323cb082fcc3049e91559", + "username": "Martijn Scheffer", + "email": "Martijn Scheffer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 664, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e9155b", + "username": "Voxlinou", + "email": "Voxlinou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1261, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cb082fcc3049e9155c", + "username": "joelvh", + "email": "joelvh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16052, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d4"]] + }, + { + "_id": "62f323cc082fcc3049e9155e", + "username": "Adam Hepton", + "email": "Adam Hepton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 674, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91560", + "username": "DDK", + "email": "DDK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 970, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91562", + "username": "Simon East", + "email": "Simon East@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 52121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91564", + "username": "SoreThumb", + "email": "SoreThumb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 530, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91566", + "username": "Holf", + "email": "Holf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5307, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91568", + "username": "Ali Gangji", + "email": "Ali Gangji@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1401, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e9156b", + "username": "MKN Web Solutions", + "email": "MKN Web Solutions@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2694, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e9156d", + "username": "ivkremer", + "email": "ivkremer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1173, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e9156f", + "username": "DonMB", + "email": "DonMB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2407, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91571", + "username": "Alesh Houdek", + "email": "Alesh Houdek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 818, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91572", + "username": "sam6ber", + "email": "sam6ber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9391, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902d9"]] + }, + { + "_id": "62f323cc082fcc3049e91573", + "username": "Pnobuts", + "email": "Pnobuts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 147, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902da"]] + }, + { + "_id": "62f323cc082fcc3049e91574", + "username": "Zaheer Ahmed", + "email": "Zaheer Ahmed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27628, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902dc"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051d"] + ] + }, + { + "_id": "62f323cc082fcc3049e91576", + "username": "Cobby", + "email": "Cobby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5232, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91579", + "username": "TaylorMac", + "email": "TaylorMac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8652, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e9157b", + "username": "Simon", + "email": "Simon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1944, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902dd"], + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e56"] + ] + }, + { + "_id": "62f323cc082fcc3049e9157e", + "username": "Big McLargeHuge", + "email": "Big McLargeHuge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13618, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91581", + "username": "Craicerjack", + "email": "Craicerjack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5970, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cc082fcc3049e91582", + "username": "Berezh", + "email": "Berezh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 941, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e0"]] + }, + { + "_id": "62f323cd082fcc3049e91584", + "username": "user28864", + "email": "user28864@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3253, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e2"]] + }, + { + "_id": "62f323cd082fcc3049e91585", + "username": "Gabriel Hautclocq", + "email": "Gabriel Hautclocq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e3"]] + }, + { + "_id": "62f323cd082fcc3049e91586", + "username": "andersh", + "email": "andersh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7649, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e4"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e6"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037e"] + ] + }, + { + "_id": "62f323cd082fcc3049e91587", + "username": "MaxEcho", + "email": "MaxEcho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14039, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e5"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5e"] + ] + }, + { + "_id": "62f323cd082fcc3049e91589", + "username": "Igor Loskutov", + "email": "Igor Loskutov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1943, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cd082fcc3049e9158a", + "username": "a8m", + "email": "a8m@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9098, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e7"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059a"] + ] + }, + { + "_id": "62f323cd082fcc3049e9158c", + "username": "Ahmad Sharif", + "email": "Ahmad Sharif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3879, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cd082fcc3049e9158d", + "username": "AMIC MING", + "email": "AMIC MING@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e9"]] + }, + { + "_id": "62f323cd082fcc3049e9158e", + "username": "kaleazy", + "email": "kaleazy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5478, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902e8"]] + }, + { + "_id": "62f323cd082fcc3049e91590", + "username": "mfq", + "email": "mfq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1375, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ea"]] + }, + { + "_id": "62f323ce082fcc3049e91591", + "username": "Asad Fida", + "email": "Asad Fida@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902eb"]] + }, + { + "_id": "62f323ce082fcc3049e91592", + "username": "Mr. X", + "email": "Mr. X@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 258, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ec"]] + }, + { + "_id": "62f323ce082fcc3049e91594", + "username": "henhen", + "email": "henhen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 950, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323ce082fcc3049e91596", + "username": "Martin Burch", + "email": "Martin Burch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2536, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323ce082fcc3049e91597", + "username": "Nicolò", + "email": "Nicolò@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1699, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ed"]] + }, + { + "_id": "62f323ce082fcc3049e91599", + "username": "pizzamonster", + "email": "pizzamonster@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 921, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ee"]] + }, + { + "_id": "62f323ce082fcc3049e9159b", + "username": "YakovL", + "email": "YakovL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6761, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323ce082fcc3049e9159d", + "username": "ilter", + "email": "ilter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3984, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ef"]] + }, + { + "_id": "62f323ce082fcc3049e9159e", + "username": "maudulus", + "email": "maudulus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9829, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f0"]] + }, + { + "_id": "62f323ce082fcc3049e915a0", + "username": "Fredrik A.", + "email": "Fredrik A.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 922, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f1"]] + }, + { + "_id": "62f323ce082fcc3049e915a3", + "username": "Alfredo Delgado", + "email": "Alfredo Delgado@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323ce082fcc3049e915a4", + "username": "Raju Bera", + "email": "Raju Bera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 952, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f2"]] + }, + { + "_id": "62f323ce082fcc3049e915a5", + "username": "ISFO", + "email": "ISFO@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f3"]] + }, + { + "_id": "62f323ce082fcc3049e915a7", + "username": "Josh Crozier", + "email": "Josh Crozier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 221685, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f4"]] + }, + { + "_id": "62f323cf082fcc3049e915a9", + "username": "GGG", + "email": "GGG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 640, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cf082fcc3049e915ab", + "username": "Hemerson Varela", + "email": "Hemerson Varela@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21838, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cf082fcc3049e915ad", + "username": "chovy", + "email": "chovy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67029, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f5"]] + }, + { + "_id": "62f323cf082fcc3049e915af", + "username": "Harry Svensson", + "email": "Harry Svensson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 321, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cf082fcc3049e915b0", + "username": "szanata", + "email": "szanata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2376, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f6"]] + }, + { + "_id": "62f323cf082fcc3049e915b2", + "username": "Kaiido", + "email": "Kaiido@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 106443, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cf082fcc3049e915b4", + "username": "Irfan Syed", + "email": "Irfan Syed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f7"]] + }, + { + "_id": "62f323cf082fcc3049e915b5", + "username": "TarranJones", + "email": "TarranJones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3858, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902f9"]] + }, + { + "_id": "62f323cf082fcc3049e915b6", + "username": "Hadnazzar", + "email": "Hadnazzar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1125, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902fa"]] + }, + { + "_id": "62f323cf082fcc3049e915b7", + "username": "zianwar", + "email": "zianwar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3381, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902fc"]] + }, + { + "_id": "62f323cf082fcc3049e915b9", + "username": "Avatar", + "email": "Avatar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13103, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323cf082fcc3049e915ba", + "username": "Eduardo Cuomo", + "email": "Eduardo Cuomo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16346, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902fb"], + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90343"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058e"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3c"] + ] + }, + { + "_id": "62f323cf082fcc3049e915bb", + "username": "medik", + "email": "medik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902fe"]] + }, + { + "_id": "62f323cf082fcc3049e915bc", + "username": "Timur Usmanov", + "email": "Timur Usmanov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902fd"]] + }, + { + "_id": "62f323d0082fcc3049e915bd", + "username": "SalmanAA", + "email": "SalmanAA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 556, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e902ff"]] + }, + { + "_id": "62f323d0082fcc3049e915be", + "username": "daronwolff", + "email": "daronwolff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1907, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90300"]] + }, + { + "_id": "62f323d0082fcc3049e915bf", + "username": "BILAL AHMAD", + "email": "BILAL AHMAD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 690, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90303"], + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90304"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052e"] + ] + }, + { + "_id": "62f323d0082fcc3049e915c0", + "username": "puiu", + "email": "puiu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90307"]] + }, + { + "_id": "62f323d0082fcc3049e915c1", + "username": "Abhishek Maurya", + "email": "Abhishek Maurya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90306"]] + }, + { + "_id": "62f323d0082fcc3049e915c3", + "username": "Qwerty", + "email": "Qwerty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1206, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90308"]] + }, + { + "_id": "62f323d1082fcc3049e915c5", + "username": "Aditya Joshi", + "email": "Aditya Joshi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 249, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90309"]] + }, + { + "_id": "62f323d1082fcc3049e915c6", + "username": "Rambabu Bommisetti", + "email": "Rambabu Bommisetti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030a"]] + }, + { + "_id": "62f323d1082fcc3049e915c7", + "username": "Shivam Gupta", + "email": "Shivam Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 145, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030c"]] + }, + { + "_id": "62f323d1082fcc3049e915c8", + "username": "Sterling Bourne", + "email": "Sterling Bourne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2768, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030b"]] + }, + { + "_id": "62f323d1082fcc3049e915c9", + "username": "Ilyas karim", + "email": "Ilyas karim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4238, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030e"]] + }, + { + "_id": "62f323d1082fcc3049e915ca", + "username": "victorhazbun", + "email": "victorhazbun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 723, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030d"]] + }, + { + "_id": "62f323d1082fcc3049e915cb", + "username": "Wolf", + "email": "Wolf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 551, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9030f"]] + }, + { + "_id": "62f323d1082fcc3049e915cd", + "username": "Melih Yıldız'", + "email": "Melih Yıldız'@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d1082fcc3049e915ce", + "username": "bajran", + "email": "bajran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1371, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90310"]] + }, + { + "_id": "62f323d1082fcc3049e915cf", + "username": "Thielicious", + "email": "Thielicious@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3698, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90311"]] + }, + { + "_id": "62f323d1082fcc3049e915d1", + "username": "Aaron Tribou", + "email": "Aaron Tribou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1183, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d1082fcc3049e915d2", + "username": "Benny Powers", + "email": "Benny Powers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4823, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90312"]] + }, + { + "_id": "62f323d2082fcc3049e915d4", + "username": "Wade Hatler", + "email": "Wade Hatler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1725, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d2082fcc3049e915d6", + "username": "codemonkey", + "email": "codemonkey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6689, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d2082fcc3049e915d8", + "username": "msdos", + "email": "msdos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2452, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d2082fcc3049e915d9", + "username": "Little Roys", + "email": "Little Roys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4845, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90314"]] + }, + { + "_id": "62f323d2082fcc3049e915da", + "username": "Mradul Pandey", + "email": "Mradul Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2028, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90316"]] + }, + { + "_id": "62f323d2082fcc3049e915db", + "username": "Akitha_MJ", + "email": "Akitha_MJ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3310, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90315"]] + }, + { + "_id": "62f323d2082fcc3049e915dc", + "username": "Jackkobec", + "email": "Jackkobec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5057, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90318"]] + }, + { + "_id": "62f323d2082fcc3049e915dd", + "username": "Jaydeep Galani", + "email": "Jaydeep Galani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90319"]] + }, + { + "_id": "62f323d2082fcc3049e915df", + "username": "Fappaz", + "email": "Fappaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2215, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d2082fcc3049e915e0", + "username": "justcant", + "email": "justcant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031a"]] + }, + { + "_id": "62f323d2082fcc3049e915e2", + "username": "Tyson Phalp", + "email": "Tyson Phalp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2647, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d2082fcc3049e915e3", + "username": "Mohamed Ben Hartouz", + "email": "Mohamed Ben Hartouz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 144, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031b"]] + }, + { + "_id": "62f323d2082fcc3049e915e4", + "username": "Sapphire_Brick", + "email": "Sapphire_Brick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1429, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031c"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90531"], + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072a"] + ] + }, + { + "_id": "62f323d3082fcc3049e915e6", + "username": "Amit Jamwal", + "email": "Amit Jamwal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 529, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90527"]] + }, + { + "_id": "62f323d3082fcc3049e915e7", + "username": "Rúnar Berg", + "email": "Rúnar Berg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3875, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9031d"]] + }, + { + "_id": "62f323d3082fcc3049e915e8", + "username": "Chukwu3meka", + "email": "Chukwu3meka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4024, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90320"]] + }, + { + "_id": "62f323d3082fcc3049e915e9", + "username": "Omkesh Sajjanwar", + "email": "Omkesh Sajjanwar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 227, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90321"]] + }, + { + "_id": "62f323d3082fcc3049e915ea", + "username": "Christian Matthew", + "email": "Christian Matthew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90322"]] + }, + { + "_id": "62f323d3082fcc3049e915eb", + "username": "Abdulmoiz Ahmer", + "email": "Abdulmoiz Ahmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90324"]] + }, + { + "_id": "62f323d3082fcc3049e915ee", + "username": "whereisanddy", + "email": "whereisanddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90326"]] + }, + { + "_id": "62f323d3082fcc3049e915f0", + "username": "Ruslan Korkin", + "email": "Ruslan Korkin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2598, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90325"]] + }, + { + "_id": "62f323d4082fcc3049e915f2", + "username": "stackoverflow", + "email": "stackoverflow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 824, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d4082fcc3049e915f4", + "username": "Deen John", + "email": "Deen John@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3046, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90327"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093b"] + ] + }, + { + "_id": "62f323d4082fcc3049e915f6", + "username": "Tahseen Alaa", + "email": "Tahseen Alaa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90328"]] + }, + { + "_id": "62f323d4082fcc3049e915f7", + "username": "giovaniZanetti", + "email": "giovaniZanetti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 426, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9032b"]] + }, + { + "_id": "62f323d4082fcc3049e915f8", + "username": "DiaMaBo", + "email": "DiaMaBo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1391, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9032a"], + ["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e05"] + ] + }, + { + "_id": "62f323d4082fcc3049e915fa", + "username": "Dory Daniel", + "email": "Dory Daniel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 735, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e90329"]] + }, + { + "_id": "62f323d4082fcc3049e915fb", + "username": "Satheez", + "email": "Satheez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 500, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9032c"]] + }, + { + "_id": "62f323d4082fcc3049e915fc", + "username": "sun sreng", + "email": "sun sreng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f321bf082fcc3049e9032d"]] + }, + { + "_id": "62f323d4082fcc3049e915fd", + "username": "Andru Luvisi", + "email": "Andru Luvisi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9032e"]] + }, + { + "_id": "62f323d4082fcc3049e915ff", + "username": "orip", + "email": "orip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 70316, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6d"]] + }, + { + "_id": "62f323d4082fcc3049e91600", + "username": "Damir Zekić", + "email": "Damir Zekić@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15208, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90330"]] + }, + { + "_id": "62f323d4082fcc3049e91601", + "username": "cic", + "email": "cic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7138, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9032f"]] + }, + { + "_id": "62f323d5082fcc3049e91602", + "username": "Mason Houtz", + "email": "Mason Houtz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90332"]] + }, + { + "_id": "62f323d5082fcc3049e91604", + "username": "Már Örlygsson", + "email": "Már Örlygsson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13878, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90331"]] + }, + { + "_id": "62f323d5082fcc3049e91605", + "username": "Ken", + "email": "Ken@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90333"]] + }, + { + "_id": "62f323d5082fcc3049e91607", + "username": "Ryan Florence", + "email": "Ryan Florence@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13259, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e91609", + "username": "user102008", + "email": "user102008@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29861, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e9160b", + "username": "Sam Soffes", + "email": "Sam Soffes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14630, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e9160d", + "username": "plus-", + "email": "plus-@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43986, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e9160f", + "username": "Om Shankar", + "email": "Om Shankar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7909, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e91611", + "username": "user254153", + "email": "user254153@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1771, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e91613", + "username": "Timo", + "email": "Timo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2606, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e91614", + "username": "MattMcKnight", + "email": "MattMcKnight@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90335"]] + }, + { + "_id": "62f323d5082fcc3049e91616", + "username": "Yi Jiang", + "email": "Yi Jiang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 48213, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e91618", + "username": "da coconut", + "email": "da coconut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 718, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035f"]] + }, + { + "_id": "62f323d5082fcc3049e91619", + "username": "Dennis Allen", + "email": "Dennis Allen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 404, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90336"]] + }, + { + "_id": "62f323d5082fcc3049e9161a", + "username": "Ekim", + "email": "Ekim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 953, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90338"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e9"] + ] + }, + { + "_id": "62f323d5082fcc3049e9161c", + "username": "joeytwiddle", + "email": "joeytwiddle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27238, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d5082fcc3049e9161d", + "username": "Ztyx", + "email": "Ztyx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12970, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90337"]] + }, + { + "_id": "62f323d5082fcc3049e9161e", + "username": "william malo", + "email": "william malo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2420, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033a"]] + }, + { + "_id": "62f323d5082fcc3049e9161f", + "username": "Carlos A", + "email": "Carlos A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90339"]] + }, + { + "_id": "62f323d6082fcc3049e91620", + "username": "Lemex", + "email": "Lemex@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3752, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033c"]] + }, + { + "_id": "62f323d6082fcc3049e91621", + "username": "ninjagecko", + "email": "ninjagecko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 84479, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033b"]] + }, + { + "_id": "62f323d6082fcc3049e91623", + "username": "Casey Chu", + "email": "Casey Chu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24265, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d6082fcc3049e91624", + "username": "Andy Rohr", + "email": "Andy Rohr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033d"]] + }, + { + "_id": "62f323d6082fcc3049e91625", + "username": "stamat", + "email": "stamat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9033f"]] + }, + { + "_id": "62f323d6082fcc3049e91628", + "username": "Matías Cánepa", + "email": "Matías Cánepa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5468, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90340"]] + }, + { + "_id": "62f323d6082fcc3049e9162c", + "username": "Shadow The Kid Wizard", + "email": "Shadow The Kid Wizard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d6082fcc3049e9162d", + "username": "Mina Gabriel", + "email": "Mina Gabriel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20299, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90341"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c44"] + ] + }, + { + "_id": "62f323d6082fcc3049e9162e", + "username": "Pradeep Mahdevu", + "email": "Pradeep Mahdevu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7563, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90342"]] + }, + { + "_id": "62f323d6082fcc3049e9162f", + "username": "Michael", + "email": "Michael@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21224, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90344"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b93"] + ] + }, + { + "_id": "62f323d7082fcc3049e91631", + "username": "diEcho", + "email": "diEcho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 52391, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d7082fcc3049e91632", + "username": "dansalmo", + "email": "dansalmo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11106, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90346"]] + }, + { + "_id": "62f323d7082fcc3049e91634", + "username": "Apolo", + "email": "Apolo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3686, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d7082fcc3049e91635", + "username": "AlonL", + "email": "AlonL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5752, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90348"]] + }, + { + "_id": "62f323d7082fcc3049e91636", + "username": "l3x", + "email": "l3x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29724, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90349"], + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0a"] + ] + }, + { + "_id": "62f323d7082fcc3049e91638", + "username": "Maciej Bukowski", + "email": "Maciej Bukowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3060, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90661"]] + }, + { + "_id": "62f323d7082fcc3049e9163a", + "username": "Gordon Bean", + "email": "Gordon Bean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3861, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d7082fcc3049e9163b", + "username": "rlib", + "email": "rlib@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6736, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034a"]] + }, + { + "_id": "62f323d7082fcc3049e9163c", + "username": "user2724028", + "email": "user2724028@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 584, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034c"]] + }, + { + "_id": "62f323d7082fcc3049e9163e", + "username": "bryc", + "email": "bryc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10204, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d7082fcc3049e91640", + "username": "sqram", + "email": "sqram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6849, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034b"]] + }, + { + "_id": "62f323d7082fcc3049e91641", + "username": "Maxime Helen", + "email": "Maxime Helen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034e"]] + }, + { + "_id": "62f323d7082fcc3049e91642", + "username": "Igor Barbashin", + "email": "Igor Barbashin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 831, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9034d"]] + }, + { + "_id": "62f323d8082fcc3049e91643", + "username": "KRRySS", + "email": "KRRySS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 181, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90350"]] + }, + { + "_id": "62f323d8082fcc3049e91644", + "username": "Jeeva", + "email": "Jeeva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1689, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90352"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3d"] + ] + }, + { + "_id": "62f323d8082fcc3049e91645", + "username": "Krishna Ganeriwal", + "email": "Krishna Ganeriwal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1773, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90351"], + ["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e90400"] + ] + }, + { + "_id": "62f323d8082fcc3049e91646", + "username": "Neil Girardi", + "email": "Neil Girardi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90354"]] + }, + { + "_id": "62f323d8082fcc3049e91647", + "username": "Mitul Panchal", + "email": "Mitul Panchal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 496, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90353"]] + }, + { + "_id": "62f323d8082fcc3049e91648", + "username": "Shashwat Gupta", + "email": "Shashwat Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4575, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90356"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a51"] + ] + }, + { + "_id": "62f323d8082fcc3049e91649", + "username": "Durgpal Singh", + "email": "Durgpal Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10095, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90355"]] + }, + { + "_id": "62f323d8082fcc3049e9164a", + "username": "Sanjay Magar", + "email": "Sanjay Magar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90358"]] + }, + { + "_id": "62f323d9082fcc3049e9164b", + "username": "Shiva", + "email": "Shiva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 454, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035a"]] + }, + { + "_id": "62f323d9082fcc3049e9164c", + "username": "Majedur", + "email": "Majedur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2570, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035b"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c2"] + ] + }, + { + "_id": "62f323d9082fcc3049e9164e", + "username": "stefanowiczp", + "email": "stefanowiczp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 465, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323d9082fcc3049e9164f", + "username": "Mamunur Rashid", + "email": "Mamunur Rashid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1006, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035c"]] + }, + { + "_id": "62f323d9082fcc3049e91650", + "username": "Riwaj Chalise", + "email": "Riwaj Chalise@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 497, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035d"]] + }, + { + "_id": "62f323d9082fcc3049e91651", + "username": "Md. Harun Or Rashid", + "email": "Md. Harun Or Rashid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1900, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e9035e"]] + }, + { + "_id": "62f323d9082fcc3049e91652", + "username": "MsiLucifer", + "email": "MsiLucifer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 488, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90360"]] + }, + { + "_id": "62f323d9082fcc3049e91653", + "username": "Med Aziz CHETOUI", + "email": "Med Aziz CHETOUI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 138, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90362"]] + }, + { + "_id": "62f323da082fcc3049e91655", + "username": "Pranav Garg", + "email": "Pranav Garg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91656", + "username": "Rohìt Jíndal", + "email": "Rohìt Jíndal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19475, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febf", "62f321bf082fcc3049e90364"]] + }, + { + "_id": "62f323da082fcc3049e91658", + "username": "Sreenikethan I", + "email": "Sreenikethan I@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e9165a", + "username": "brad", + "email": "brad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71558, + "questionIds": ["62f321bb082fcc3049e8febf"], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e9165c", + "username": "Daniel Marschall", + "email": "Daniel Marschall@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3681, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e9165e", + "username": "Prestaul", + "email": "Prestaul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81023, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90366"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084b"] + ] + }, + { + "_id": "62f323da082fcc3049e91661", + "username": "JSON", + "email": "JSON@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1783, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91662", + "username": "John Millikin", + "email": "John Millikin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 192252, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90365"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90903"] + ] + }, + { + "_id": "62f323da082fcc3049e91663", + "username": "Mathieu Pagé", + "email": "Mathieu Pagé@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10224, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90368"]] + }, + { + "_id": "62f323da082fcc3049e91665", + "username": "MgSam", + "email": "MgSam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11443, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91666", + "username": "Kevin Hakanson", + "email": "Kevin Hakanson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40391, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90367"]] + }, + { + "_id": "62f323da082fcc3049e91667", + "username": "jablko", + "email": "jablko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036a"]] + }, + { + "_id": "62f323da082fcc3049e91669", + "username": "Slothario", + "email": "Slothario@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2579, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e9166b", + "username": "manuell", + "email": "manuell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7300, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e9166e", + "username": "Roope Hakulinen", + "email": "Roope Hakulinen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7150, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91670", + "username": "dota2pro", + "email": "dota2pro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6596, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91672", + "username": "PLG", + "email": "PLG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 410, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91674", + "username": "cognophile", + "email": "cognophile@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 632, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f323da082fcc3049e91675", + "username": "sleeplessnerd", + "email": "sleeplessnerd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20703, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036c"]] + }, + { + "_id": "62f323da082fcc3049e91676", + "username": "Jed Schmidt", + "email": "Jed Schmidt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036b"]] + }, + { + "_id": "62f323db082fcc3049e91677", + "username": "ripper234", + "email": "ripper234@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 214058, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036d"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a34"] + ] + }, + { + "_id": "62f323db082fcc3049e91678", + "username": "Tracker1", + "email": "Tracker1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18593, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036e"], + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042d"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aeb"] + ] + }, + { + "_id": "62f323db082fcc3049e91679", + "username": "Wojciech Bednarski", + "email": "Wojciech Bednarski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5558, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90370"]] + }, + { + "_id": "62f323db082fcc3049e9167b", + "username": "Andrea Turri", + "email": "Andrea Turri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9036f"]] + }, + { + "_id": "62f323db082fcc3049e9167c", + "username": "joelpt", + "email": "joelpt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4051, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90371"]] + }, + { + "_id": "62f323db082fcc3049e9167e", + "username": "Anatoly Mironov", + "email": "Anatoly Mironov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7006, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90374"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5b"] + ] + }, + { + "_id": "62f323db082fcc3049e91680", + "username": "kayz1", + "email": "kayz1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6992, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90373"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7a"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bbe"] + ] + }, + { + "_id": "62f323db082fcc3049e91681", + "username": "John Fowler", + "email": "John Fowler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90372"]] + }, + { + "_id": "62f323db082fcc3049e91682", + "username": "ling", + "email": "ling@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8691, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90376"], + ["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d85"] + ] + }, + { + "_id": "62f323db082fcc3049e91683", + "username": "Shital Shah", + "email": "Shital Shah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57096, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90375"], + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e08"] + ] + }, + { + "_id": "62f323dc082fcc3049e91684", + "username": "Giridhar", + "email": "Giridhar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 95, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90377"]] + }, + { + "_id": "62f323dc082fcc3049e91685", + "username": "Rishi", + "email": "Rishi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 268, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90378"]] + }, + { + "_id": "62f323dc082fcc3049e91686", + "username": "mangalbhaskar", + "email": "mangalbhaskar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037a"]] + }, + { + "_id": "62f32419082fcc3049e91687", + "username": "Dustin Poissant", + "email": "Dustin Poissant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2991, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90381"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f11"] + ] + }, + { + "_id": "62f32419082fcc3049e91688", + "username": "lugreen", + "email": "lugreen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 92, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90382"]] + }, + { + "_id": "62f32419082fcc3049e9168a", + "username": "user2006656", + "email": "user2006656@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90383"]] + }, + { + "_id": "62f32419082fcc3049e9168b", + "username": "Pablo Pazos", + "email": "Pablo Pazos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2909, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90384"]] + }, + { + "_id": "62f32419082fcc3049e9168d", + "username": "Jonathan Potter", + "email": "Jonathan Potter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2833, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90385"]] + }, + { + "_id": "62f32419082fcc3049e9168f", + "username": "Marco Kerwitz", + "email": "Marco Kerwitz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4924, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32419082fcc3049e91691", + "username": "Simon Rigét", + "email": "Simon Rigét@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2487, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90386"]] + }, + { + "_id": "62f32419082fcc3049e91693", + "username": "Basj", + "email": "Basj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37401, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e6"]] + }, + { + "_id": "62f32419082fcc3049e91696", + "username": "ceving", + "email": "ceving@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20116, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90388"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d24"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f15"] + ] + }, + { + "_id": "62f32419082fcc3049e91698", + "username": "alek kowalczyk", + "email": "alek kowalczyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4766, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32419082fcc3049e91699", + "username": "Stephen Quan", + "email": "Stephen Quan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16886, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90389"]] + }, + { + "_id": "62f32419082fcc3049e9169a", + "username": "nomadev", + "email": "nomadev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 160, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038a"]] + }, + { + "_id": "62f3241a082fcc3049e9169b", + "username": "amn", + "email": "amn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7876, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038c"], + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039d"] + ] + }, + { + "_id": "62f3241a082fcc3049e9169c", + "username": "Jacob Ochoa", + "email": "Jacob Ochoa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038b"]] + }, + { + "_id": "62f3241a082fcc3049e9169d", + "username": "user9342572809", + "email": "user9342572809@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11569, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038e"]] + }, + { + "_id": "62f3241a082fcc3049e9169e", + "username": "li x", + "email": "li x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3809, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9038f"]] + }, + { + "_id": "62f3241a082fcc3049e9169f", + "username": "dgellow", + "email": "dgellow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 564, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90390"]] + }, + { + "_id": "62f3241a082fcc3049e916a1", + "username": "Paulo Henrique Queiroz", + "email": "Paulo Henrique Queiroz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 612, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241a082fcc3049e916a3", + "username": "Aral Roca", + "email": "Aral Roca@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4938, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90392"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb4"] + ] + }, + { + "_id": "62f3241a082fcc3049e916a5", + "username": "M. Justin", + "email": "M. Justin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10078, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241a082fcc3049e916a6", + "username": "Mohan Ram", + "email": "Mohan Ram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90391"]] + }, + { + "_id": "62f3241a082fcc3049e916a7", + "username": "incureforce", + "email": "incureforce@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90393"]] + }, + { + "_id": "62f3241a082fcc3049e916a9", + "username": "fcdt", + "email": "fcdt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2261, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241a082fcc3049e916ab", + "username": "dbc", + "email": "dbc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93453, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241a082fcc3049e916ad", + "username": "Yudner", + "email": "Yudner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 383, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90394"]] + }, + { + "_id": "62f3241b082fcc3049e916ae", + "username": "Fernando Teles", + "email": "Fernando Teles@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90398"]] + }, + { + "_id": "62f3241b082fcc3049e916b1", + "username": "Charaf", + "email": "Charaf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 158, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241b082fcc3049e916b3", + "username": "Tikolu", + "email": "Tikolu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 183, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90397"]] + }, + { + "_id": "62f3241b082fcc3049e916b5", + "username": "lissajous", + "email": "lissajous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 265, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241b082fcc3049e916b7", + "username": "asduj", + "email": "asduj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241b082fcc3049e916b8", + "username": "unsynchronized", + "email": "unsynchronized@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4759, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90399"]] + }, + { + "_id": "62f3241b082fcc3049e916b9", + "username": "blubberdiblub", + "email": "blubberdiblub@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3955, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039a"]] + }, + { + "_id": "62f3241b082fcc3049e916ba", + "username": "Explosion", + "email": "Explosion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 138, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039b"]] + }, + { + "_id": "62f3241b082fcc3049e916bc", + "username": "Brian Cannard", + "email": "Brian Cannard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 834, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241b082fcc3049e916bd", + "username": "Pier-Luc Gendreau", + "email": "Pier-Luc Gendreau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13015, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039c"]] + }, + { + "_id": "62f3241b082fcc3049e916be", + "username": "Slava", + "email": "Slava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 969, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039e"]] + }, + { + "_id": "62f3241c082fcc3049e916c2", + "username": "Rambler", + "email": "Rambler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 245, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e903a0"]] + }, + { + "_id": "62f3241c082fcc3049e916c4", + "username": "Hashbrown", + "email": "Hashbrown@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10898, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9039f"]] + }, + { + "_id": "62f3241c082fcc3049e916c5", + "username": "Staale", + "email": "Staale@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26188, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a2"]] + }, + { + "_id": "62f3241c082fcc3049e916c6", + "username": "Tom Viner", + "email": "Tom Viner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a4"]] + }, + { + "_id": "62f3241c082fcc3049e916c7", + "username": "aemkei", + "email": "aemkei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10906, + "questionIds": ["62f321bb082fcc3049e8ff09"], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a3"]] + }, + { + "_id": "62f3241c082fcc3049e916c9", + "username": "Skone", + "email": "Skone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 745, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241c082fcc3049e916cb", + "username": "Peter", + "email": "Peter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27220, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90416"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d4"], + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad6"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aee"] + ] + }, + { + "_id": "62f3241c082fcc3049e916cc", + "username": "Kiragaz", + "email": "Kiragaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a5"]] + }, + { + "_id": "62f3241c082fcc3049e916ce", + "username": "kirb", + "email": "kirb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1996, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241c082fcc3049e916d1", + "username": "Marcus Johansson", + "email": "Marcus Johansson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2585, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241c082fcc3049e916d2", + "username": "xer0x", + "email": "xer0x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12720, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a6"]] + }, + { + "_id": "62f3241c082fcc3049e916d3", + "username": "Daithí", + "email": "Daithí@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4577, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a7"]] + }, + { + "_id": "62f3241d082fcc3049e916d4", + "username": "live-love", + "email": "live-love@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42980, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903a9"]] + }, + { + "_id": "62f3241d082fcc3049e916d6", + "username": "Inaimathi", + "email": "Inaimathi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13558, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241d082fcc3049e916d7", + "username": "deepakssn", + "email": "deepakssn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4981, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903ac"]] + }, + { + "_id": "62f3241d082fcc3049e916d8", + "username": "SBotirov", + "email": "SBotirov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13532, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903ab"]] + }, + { + "_id": "62f3241d082fcc3049e916d9", + "username": "Anoop P S", + "email": "Anoop P S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 694, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903ad"]] + }, + { + "_id": "62f3241d082fcc3049e916db", + "username": "alexventuraio", + "email": "alexventuraio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6008, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241d082fcc3049e916dc", + "username": "Belldandu", + "email": "Belldandu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1948, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903af"]] + }, + { + "_id": "62f3241d082fcc3049e916de", + "username": "EaterOfCode", + "email": "EaterOfCode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2142, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3241d082fcc3049e916df", + "username": "DevC", + "email": "DevC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6575, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b0"]] + }, + { + "_id": "62f3241d082fcc3049e916e0", + "username": "Saucier", + "email": "Saucier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4020, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b1"]] + }, + { + "_id": "62f3241d082fcc3049e916e1", + "username": "Rimian", + "email": "Rimian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b2"]] + }, + { + "_id": "62f3241e082fcc3049e916e2", + "username": "Eugene", + "email": "Eugene@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2849, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b4"], + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d9"] + ] + }, + { + "_id": "62f3241e082fcc3049e916e3", + "username": "georgez", + "email": "georgez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 737, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b3"]] + }, + { + "_id": "62f3241e082fcc3049e916e4", + "username": "user257319", + "email": "user257319@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b5"]] + }, + { + "_id": "62f3241e082fcc3049e916e5", + "username": "Muhammad Reda", + "email": "Muhammad Reda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25701, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b6"]] + }, + { + "_id": "62f3241e082fcc3049e916e6", + "username": "jameslouiz", + "email": "jameslouiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 386, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b7"]] + }, + { + "_id": "62f3241e082fcc3049e916e7", + "username": "Kevin Leary", + "email": "Kevin Leary@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7965, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b8"]] + }, + { + "_id": "62f3241e082fcc3049e916e8", + "username": "iter", + "email": "iter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 388, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903b9"]] + }, + { + "_id": "62f3241e082fcc3049e916e9", + "username": "FullStack", + "email": "FullStack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5654, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903ba"]] + }, + { + "_id": "62f3241e082fcc3049e916ea", + "username": "blueberry0xff", + "email": "blueberry0xff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3549, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903bb"]] + }, + { + "_id": "62f3241e082fcc3049e916eb", + "username": "Ronnie Royston", + "email": "Ronnie Royston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14106, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903bc"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907bb"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907db"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a70"] + ] + }, + { + "_id": "62f3241f082fcc3049e916ec", + "username": "Valentin Podkamennyi", + "email": "Valentin Podkamennyi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6915, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903bd"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0a"] + ] + }, + { + "_id": "62f3241f082fcc3049e916ed", + "username": "Joaquinglezsantos", + "email": "Joaquinglezsantos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1280, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903be"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0b"] + ] + }, + { + "_id": "62f3241f082fcc3049e916ee", + "username": "Jitendra Pawar", + "email": "Jitendra Pawar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903bf"]] + }, + { + "_id": "62f3241f082fcc3049e916ef", + "username": "Olemak", + "email": "Olemak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1806, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c1"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0d"] + ] + }, + { + "_id": "62f3241f082fcc3049e916f0", + "username": "unknown123", + "email": "unknown123@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 404, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c2"]] + }, + { + "_id": "62f3241f082fcc3049e916f1", + "username": "cenkarioz", + "email": "cenkarioz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 549, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c4"]] + }, + { + "_id": "62f3241f082fcc3049e916f2", + "username": "Ganesh", + "email": "Ganesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c6"]] + }, + { + "_id": "62f32420082fcc3049e916f3", + "username": "Flash Noob", + "email": "Flash Noob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 175, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c7"]] + }, + { + "_id": "62f32420082fcc3049e916f6", + "username": "Blazemonger", + "email": "Blazemonger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87243, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32420082fcc3049e916f8", + "username": "Vahid Amiri", + "email": "Vahid Amiri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10161, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32420082fcc3049e916f9", + "username": "Chris Brandsma", + "email": "Chris Brandsma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11521, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d1"]] + }, + { + "_id": "62f32420082fcc3049e916fb", + "username": "mcgrailm", + "email": "mcgrailm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17219, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d4"]] + }, + { + "_id": "62f32420082fcc3049e916fd", + "username": "livefree75", + "email": "livefree75@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 863, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d2"]] + }, + { + "_id": "62f32420082fcc3049e916ff", + "username": "Daniel X Moore", + "email": "Daniel X Moore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14160, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32420082fcc3049e91701", + "username": "cwharris", + "email": "cwharris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17514, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d5"]] + }, + { + "_id": "62f32420082fcc3049e91706", + "username": "gnarf", + "email": "gnarf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103764, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c8c"]] + }, + { + "_id": "62f32420082fcc3049e9170a", + "username": "eomeroff", + "email": "eomeroff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9343, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a58"]] + }, + { + "_id": "62f32420082fcc3049e9170c", + "username": "bchhun", + "email": "bchhun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d0"]] + }, + { + "_id": "62f32420082fcc3049e9170e", + "username": "SineSwiper", + "email": "SineSwiper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1969, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32420082fcc3049e91712", + "username": "RobertPitt", + "email": "RobertPitt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56071, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907c6"]] + }, + { + "_id": "62f32420082fcc3049e91715", + "username": "Micah", + "email": "Micah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107841, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ce"]] + }, + { + "_id": "62f32420082fcc3049e91716", + "username": "Abou-Emish", + "email": "Abou-Emish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d3"]] + }, + { + "_id": "62f32420082fcc3049e9171b", + "username": "dazzafact", + "email": "dazzafact@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2240, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec0", "62f321c0082fcc3049e903c8"], + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90942"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a54"] + ] + }, + { + "_id": "62f32420082fcc3049e9171c", + "username": "pupeno", + "email": "pupeno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 269816, + "questionIds": ["62f321bb082fcc3049e8fec0"], + "answerIds": [] + }, + { + "_id": "62f32421082fcc3049e9171e", + "username": "Prasanth P", + "email": "Prasanth P@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 705, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d6"]] + }, + { + "_id": "62f32421082fcc3049e91720", + "username": "starjahid", + "email": "starjahid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d7"]] + }, + { + "_id": "62f32421082fcc3049e91721", + "username": "Peter Krauss", + "email": "Peter Krauss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d9"]] + }, + { + "_id": "62f32421082fcc3049e91723", + "username": "tpower", + "email": "tpower@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54982, + "questionIds": ["62f321bb082fcc3049e8fec2"], + "answerIds": [] + }, + { + "_id": "62f32421082fcc3049e91725", + "username": "fredcrs", + "email": "fredcrs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3518, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903d8"]] + }, + { + "_id": "62f32421082fcc3049e91726", + "username": "Overbeeke", + "email": "Overbeeke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1974, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903db"]] + }, + { + "_id": "62f32421082fcc3049e91727", + "username": "Clement Ho", + "email": "Clement Ho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 309, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903da"]] + }, + { + "_id": "62f32421082fcc3049e91728", + "username": "prashanth", + "email": "prashanth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 319, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903dc"]] + }, + { + "_id": "62f32421082fcc3049e91729", + "username": "Xitalogy", + "email": "Xitalogy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1542, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903dd"]] + }, + { + "_id": "62f32421082fcc3049e9172a", + "username": "Matt Peacock", + "email": "Matt Peacock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 199, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903de"]] + }, + { + "_id": "62f32421082fcc3049e9172b", + "username": "user1477929", + "email": "user1477929@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903df"]] + }, + { + "_id": "62f32422082fcc3049e9172c", + "username": "Ramon de Jesus", + "email": "Ramon de Jesus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 750, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e0"]] + }, + { + "_id": "62f32422082fcc3049e9172e", + "username": "mathew", + "email": "mathew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e1"]] + }, + { + "_id": "62f32422082fcc3049e91730", + "username": "Samuel Liew", + "email": "Samuel Liew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73284, + "questionIds": ["62f321bb082fcc3049e8ff07"], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91030"]] + }, + { + "_id": "62f32422082fcc3049e91734", + "username": "naor", + "email": "naor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3180, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e2"], + ["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9c"] + ] + }, + { + "_id": "62f32422082fcc3049e91737", + "username": "Mark Amery", + "email": "Mark Amery@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 129742, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32422082fcc3049e91738", + "username": "mahmoh", + "email": "mahmoh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 792, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e4"]] + }, + { + "_id": "62f32422082fcc3049e91739", + "username": "tamilmani", + "email": "tamilmani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 571, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e5"]] + }, + { + "_id": "62f32422082fcc3049e9173a", + "username": "eggshot", + "email": "eggshot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 190, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e6"]] + }, + { + "_id": "62f32422082fcc3049e9173c", + "username": "Alex W", + "email": "Alex W@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35622, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e7"]] + }, + { + "_id": "62f32422082fcc3049e9173d", + "username": "Muhammad Aamir Ali", + "email": "Muhammad Aamir Ali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19865, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e8"]] + }, + { + "_id": "62f32422082fcc3049e9173f", + "username": "jj2422", + "email": "jj2422@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903e9"]] + }, + { + "_id": "62f32423082fcc3049e91740", + "username": "anusha", + "email": "anusha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2051, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ea"]] + }, + { + "_id": "62f32423082fcc3049e91742", + "username": "Serhat Koroglu", + "email": "Serhat Koroglu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ed"]] + }, + { + "_id": "62f32423082fcc3049e91743", + "username": "Ardalan Shahgholi", + "email": "Ardalan Shahgholi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ef"]] + }, + { + "_id": "62f32423082fcc3049e91744", + "username": "logan", + "email": "logan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7496, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903ee"]] + }, + { + "_id": "62f32423082fcc3049e91745", + "username": "SedJ601", + "email": "SedJ601@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f0"]] + }, + { + "_id": "62f32423082fcc3049e91746", + "username": "Kenmeister", + "email": "Kenmeister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 504, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f1"]] + }, + { + "_id": "62f32423082fcc3049e91747", + "username": "Gaurang Sondagar", + "email": "Gaurang Sondagar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 707, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f3"]] + }, + { + "_id": "62f32423082fcc3049e91748", + "username": "David Arul", + "email": "David Arul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 161, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f2"]] + }, + { + "_id": "62f32424082fcc3049e9174a", + "username": "pangyuteng", + "email": "pangyuteng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1679, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32424082fcc3049e9174c", + "username": "BingLi224", + "email": "BingLi224@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 402, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32424082fcc3049e9174d", + "username": "Omar Odeh", + "email": "Omar Odeh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f7"]] + }, + { + "_id": "62f32424082fcc3049e9174e", + "username": "vnapastiuk", + "email": "vnapastiuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 429, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec2", "62f321c0082fcc3049e903f8"]] + }, + { + "_id": "62f32424082fcc3049e9174f", + "username": "Pointy", + "email": "Pointy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 391914, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e903fd"]] + }, + { + "_id": "62f32424082fcc3049e91750", + "username": "Daniel Vassallo", + "email": "Daniel Vassallo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 328810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e903fe"]] + }, + { + "_id": "62f32424082fcc3049e91752", + "username": "Jakub P.", + "email": "Jakub P.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4716, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32424082fcc3049e91754", + "username": "Pedro Felix", + "email": "Pedro Felix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1056, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32424082fcc3049e91756", + "username": "abraham", + "email": "abraham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44621, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32424082fcc3049e91758", + "username": "PhistucK", + "email": "PhistucK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2376, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32425082fcc3049e91759", + "username": "JSON C11", + "email": "JSON C11@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10254, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec3", "62f321c0082fcc3049e90401"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fcd"] + ] + }, + { + "_id": "62f32425082fcc3049e9175a", + "username": "Jess", + "email": "Jess@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41278, + "questionIds": ["62f321bb082fcc3049e8fec3"], + "answerIds": [] + }, + { + "_id": "62f32425082fcc3049e9175b", + "username": "Mark Rajcok", + "email": "Mark Rajcok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 358080, + "questionIds": ["62f321bb082fcc3049e8fec1"], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90402"]] + }, + { + "_id": "62f32425082fcc3049e9175c", + "username": "Ulises", + "email": "Ulises@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13071, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90403"]] + }, + { + "_id": "62f32425082fcc3049e9175d", + "username": "Jin", + "email": "Jin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1056, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90404"]] + }, + { + "_id": "62f32425082fcc3049e9175e", + "username": "Evan Zamir", + "email": "Evan Zamir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7483, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90405"]] + }, + { + "_id": "62f32425082fcc3049e9175f", + "username": "Samuel", + "email": "Samuel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1275, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90407"]] + }, + { + "_id": "62f32425082fcc3049e91760", + "username": "Anand", + "email": "Anand@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4483, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90406"]] + }, + { + "_id": "62f32425082fcc3049e91761", + "username": "codevinsky", + "email": "codevinsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1273, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e90408"]] + }, + { + "_id": "62f32425082fcc3049e91762", + "username": "Scott Rippey", + "email": "Scott Rippey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15274, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e9040a"]] + }, + { + "_id": "62f32426082fcc3049e91763", + "username": "webketje", + "email": "webketje@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e9040c"]] + }, + { + "_id": "62f32426082fcc3049e91764", + "username": "Huy Tran", + "email": "Huy Tran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 765, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec1", "62f321c0082fcc3049e9040d"]] + }, + { + "_id": "62f32426082fcc3049e91766", + "username": "Guvante", + "email": "Guvante@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18335, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e91769", + "username": "scunliffe", + "email": "scunliffe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60817, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e9176b", + "username": "user692942", + "email": "user692942@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15738, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e9176d", + "username": "Neel", + "email": "Neel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 597, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e9176f", + "username": "Ilya Streltsyn", + "email": "Ilya Streltsyn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12671, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e91770", + "username": "Adam Tuttle", + "email": "Adam Tuttle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9040f"]] + }, + { + "_id": "62f32426082fcc3049e91771", + "username": "nathaniel", + "email": "nathaniel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90411"]] + }, + { + "_id": "62f32426082fcc3049e91772", + "username": "Simon Forrest", + "email": "Simon Forrest@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90412"]] + }, + { + "_id": "62f32426082fcc3049e91773", + "username": "Zach", + "email": "Zach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23994, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90410"]] + }, + { + "_id": "62f32426082fcc3049e91776", + "username": "mohamed tebry", + "email": "mohamed tebry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 141, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e91777", + "username": "Aaron Wagner", + "email": "Aaron Wagner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5689, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90413"]] + }, + { + "_id": "62f32426082fcc3049e91778", + "username": "Steve Paulo", + "email": "Steve Paulo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17616, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90414"]] + }, + { + "_id": "62f32426082fcc3049e9177a", + "username": "AnthonyWJones", + "email": "AnthonyWJones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 183816, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041b"]] + }, + { + "_id": "62f32426082fcc3049e9177d", + "username": "Scott Schupbach", + "email": "Scott Schupbach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1244, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32426082fcc3049e9177e", + "username": "mmacaulay", + "email": "mmacaulay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3001, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90415"]] + }, + { + "_id": "62f32427082fcc3049e91780", + "username": "Hosam Aly", + "email": "Hosam Aly@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40439, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32427082fcc3049e91782", + "username": "redShadow", + "email": "redShadow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6539, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32427082fcc3049e91784", + "username": "apnerve", + "email": "apnerve@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4600, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32427082fcc3049e91785", + "username": "fijter", + "email": "fijter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17217, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90417"]] + }, + { + "_id": "62f32427082fcc3049e91787", + "username": "AndFisher", + "email": "AndFisher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 623, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32427082fcc3049e91788", + "username": "CleverPatrick", + "email": "CleverPatrick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9078, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90419"]] + }, + { + "_id": "62f32427082fcc3049e91789", + "username": "Will Read", + "email": "Will Read@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90418"]] + }, + { + "_id": "62f32427082fcc3049e9178b", + "username": "Nathan", + "email": "Nathan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11554, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32464082fcc3049e9178e", + "username": "Matt Kantor", + "email": "Matt Kantor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1684, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32464082fcc3049e9178f", + "username": "Matt Goddard", + "email": "Matt Goddard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 909, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90421"]] + }, + { + "_id": "62f32464082fcc3049e91791", + "username": "mtyaka", + "email": "mtyaka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8514, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32464082fcc3049e91793", + "username": "naveen", + "email": "naveen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90420"]] + }, + { + "_id": "62f32464082fcc3049e91794", + "username": "Lukom", + "email": "Lukom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90422"]] + }, + { + "_id": "62f32464082fcc3049e91795", + "username": "se_pavel", + "email": "se_pavel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90423"]] + }, + { + "_id": "62f32464082fcc3049e91796", + "username": "Free Consulting", + "email": "Free Consulting@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4240, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90424"]] + }, + { + "_id": "62f32464082fcc3049e91798", + "username": "Justin Johnson", + "email": "Justin Johnson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30412, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90425"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c8"] + ] + }, + { + "_id": "62f32464082fcc3049e91799", + "username": "rcof", + "email": "rcof@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90427"]] + }, + { + "_id": "62f32464082fcc3049e9179a", + "username": "user394888", + "email": "user394888@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90426"]] + }, + { + "_id": "62f32464082fcc3049e9179b", + "username": "user564706", + "email": "user564706@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90428"]] + }, + { + "_id": "62f32464082fcc3049e9179d", + "username": "syahid246", + "email": "syahid246@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32464082fcc3049e9179e", + "username": "Spammer Joe", + "email": "Spammer Joe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90429"]] + }, + { + "_id": "62f32465082fcc3049e9179f", + "username": "Jesse Atkinson", + "email": "Jesse Atkinson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9606, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042a"]] + }, + { + "_id": "62f32465082fcc3049e917a1", + "username": "Josh Simerman", + "email": "Josh Simerman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 404, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042b"]] + }, + { + "_id": "62f32465082fcc3049e917a2", + "username": "Berker Yüceer", + "email": "Berker Yüceer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6766, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042c"]] + }, + { + "_id": "62f32465082fcc3049e917a4", + "username": "Y. Gherbi", + "email": "Y. Gherbi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 661, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32465082fcc3049e917a6", + "username": "E. Williams", + "email": "E. Williams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 377, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32465082fcc3049e917a7", + "username": "Eric Yin", + "email": "Eric Yin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8387, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042e"]] + }, + { + "_id": "62f32465082fcc3049e917a8", + "username": "Timo Huovinen", + "email": "Timo Huovinen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50515, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9042f"], + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90514"] + ] + }, + { + "_id": "62f32465082fcc3049e917a9", + "username": "Stacks on Stacks on Stacks", + "email": "Stacks on Stacks on Stacks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90431"]] + }, + { + "_id": "62f32465082fcc3049e917aa", + "username": "dazbradbury", + "email": "dazbradbury@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5660, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90430"]] + }, + { + "_id": "62f32465082fcc3049e917ac", + "username": "ryabenko-pro", + "email": "ryabenko-pro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 710, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32465082fcc3049e917ad", + "username": "G. Ghez", + "email": "G. Ghez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3248, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90433"]] + }, + { + "_id": "62f32465082fcc3049e917ae", + "username": "vol7ron", + "email": "vol7ron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38751, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90432"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b60"] + ] + }, + { + "_id": "62f32466082fcc3049e917b0", + "username": "Henry Hu", + "email": "Henry Hu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 524, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32466082fcc3049e917b2", + "username": "whirlwin", + "email": "whirlwin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15400, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90434"]] + }, + { + "_id": "62f32466082fcc3049e917b3", + "username": "Ashish Kumar", + "email": "Ashish Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2939, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90435"]] + }, + { + "_id": "62f32466082fcc3049e917b4", + "username": "Vinny Fonseca", + "email": "Vinny Fonseca@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 450, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90436"]] + }, + { + "_id": "62f32466082fcc3049e917b5", + "username": "Anders M.", + "email": "Anders M.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 199, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90437"]] + }, + { + "_id": "62f32466082fcc3049e917b7", + "username": "Martin", + "email": "Martin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2193, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060b"]] + }, + { + "_id": "62f32466082fcc3049e917b9", + "username": "AlxGol", + "email": "AlxGol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 156, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90439"]] + }, + { + "_id": "62f32466082fcc3049e917ba", + "username": "Milan and Friends", + "email": "Milan and Friends@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5510, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90438"]] + }, + { + "_id": "62f32466082fcc3049e917bc", + "username": "dnetix", + "email": "dnetix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 310, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043a"]] + }, + { + "_id": "62f32466082fcc3049e917bd", + "username": "TomDK", + "email": "TomDK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043d"]] + }, + { + "_id": "62f32466082fcc3049e917bf", + "username": "Jochen Schultz", + "email": "Jochen Schultz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32466082fcc3049e917c0", + "username": "user6460587", + "email": "user6460587@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9043c"]] + }, + { + "_id": "62f32467082fcc3049e917c1", + "username": "SchoolforDesign", + "email": "SchoolforDesign@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90440"]] + }, + { + "_id": "62f32467082fcc3049e917c2", + "username": "Dev pokhariya", + "email": "Dev pokhariya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 196, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90442"]] + }, + { + "_id": "62f32467082fcc3049e917c3", + "username": "Stokely", + "email": "Stokely@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7980, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90443"]] + }, + { + "_id": "62f32467082fcc3049e917c5", + "username": "RozzA", + "email": "RozzA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 571, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32467082fcc3049e917c6", + "username": "bandi", + "email": "bandi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4134, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9047c"]] + }, + { + "_id": "62f32467082fcc3049e917c7", + "username": "Nedim AKAR", + "email": "Nedim AKAR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e90444"]] + }, + { + "_id": "62f32467082fcc3049e917c9", + "username": "fnkr", + "email": "fnkr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8508, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32467082fcc3049e917ca", + "username": "Stormenet", + "email": "Stormenet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24856, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9047b"]] + }, + { + "_id": "62f32468082fcc3049e917cc", + "username": "Mottie", + "email": "Mottie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80872, + "questionIds": ["62f321bb082fcc3049e8fefa"], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dad"]] + }, + { + "_id": "62f32468082fcc3049e917ce", + "username": "TheEmirOfGroofunkistan", + "email": "TheEmirOfGroofunkistan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5376, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917cf", + "username": "DDM", + "email": "DDM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 524, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9047d"]] + }, + { + "_id": "62f32468082fcc3049e917d0", + "username": "Chase Seibert", + "email": "Chase Seibert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15405, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9047e"]] + }, + { + "_id": "62f32468082fcc3049e917d2", + "username": "ProfK", + "email": "ProfK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917d3", + "username": "PhiLho", + "email": "PhiLho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39809, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9047f"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91000"] + ] + }, + { + "_id": "62f32468082fcc3049e917d4", + "username": "matthuhiggins", + "email": "matthuhiggins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90480"]] + }, + { + "_id": "62f32468082fcc3049e917d5", + "username": "xiniu", + "email": "xiniu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90482"]] + }, + { + "_id": "62f32468082fcc3049e917d7", + "username": "Radek", + "email": "Radek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3796, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917d8", + "username": "Brent Matzelle", + "email": "Brent Matzelle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3913, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90481"]] + }, + { + "_id": "62f32468082fcc3049e917db", + "username": "Max Nanasy", + "email": "Max Nanasy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5651, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917dd", + "username": "Orbiting Eden", + "email": "Orbiting Eden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1476, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917e0", + "username": "bhan", + "email": "bhan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2469, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917e2", + "username": "Gui Imamura", + "email": "Gui Imamura@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 546, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917e5", + "username": "MightyPork", + "email": "MightyPork@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17573, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917e6", + "username": "SteamDev", + "email": "SteamDev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 477, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90483"]] + }, + { + "_id": "62f32468082fcc3049e917e7", + "username": "Oliver Bock", + "email": "Oliver Bock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90485"]] + }, + { + "_id": "62f32468082fcc3049e917e8", + "username": "mrBorna", + "email": "mrBorna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1727, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90486"]] + }, + { + "_id": "62f32468082fcc3049e917ea", + "username": "Denilson Sá Maia", + "email": "Denilson Sá Maia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44864, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917ec", + "username": "Aram Kocharyan", + "email": "Aram Kocharyan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19901, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90917"]] + }, + { + "_id": "62f32468082fcc3049e917ee", + "username": "arkon", + "email": "arkon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2243, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f0", + "username": "RasTheDestroyer", + "email": "RasTheDestroyer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1700, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f2", + "username": "Marcus Pope", + "email": "Marcus Pope@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2283, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f4", + "username": "Dibish", + "email": "Dibish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8691, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f6", + "username": "borisdiakur", + "email": "borisdiakur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9157, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f8", + "username": "gab06", + "email": "gab06@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 558, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32468082fcc3049e917f9", + "username": "Jarek Milewski", + "email": "Jarek Milewski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14953, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90484"]] + }, + { + "_id": "62f32469082fcc3049e917fa", + "username": "Matthew Scragg", + "email": "Matthew Scragg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90487"]] + }, + { + "_id": "62f32469082fcc3049e917fc", + "username": "UserBSS1", + "email": "UserBSS1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1924, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90488"], + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90489"] + ] + }, + { + "_id": "62f32469082fcc3049e917fe", + "username": "StanE", + "email": "StanE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2534, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91800", + "username": "Luke A. Leber", + "email": "Luke A. Leber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 667, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91801", + "username": "dvallejo", + "email": "dvallejo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1023, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048a"]] + }, + { + "_id": "62f32469082fcc3049e91802", + "username": "supNate", + "email": "supNate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 412, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048b"]] + }, + { + "_id": "62f32469082fcc3049e91804", + "username": "Raptor", + "email": "Raptor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51430, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91806", + "username": "Justin", + "email": "Justin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25018, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048c"], + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b6"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fcb"] + ] + }, + { + "_id": "62f32469082fcc3049e91809", + "username": "EricP", + "email": "EricP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3311, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d05"]] + }, + { + "_id": "62f32469082fcc3049e9180b", + "username": "drzaus", + "email": "drzaus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23253, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048d"]] + }, + { + "_id": "62f32469082fcc3049e9180d", + "username": "Brock Adams", + "email": "Brock Adams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87539, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e9180f", + "username": "odinho - Velmont", + "email": "odinho - Velmont@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19791, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91810", + "username": "Richard Shurtz", + "email": "Richard Shurtz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1561, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048e"]] + }, + { + "_id": "62f32469082fcc3049e91811", + "username": "sinister", + "email": "sinister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9048f"]] + }, + { + "_id": "62f32469082fcc3049e91813", + "username": "user1108948", + "email": "user1108948@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91815", + "username": "Voyager", + "email": "Voyager@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 606, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32469082fcc3049e91817", + "username": "Bart Burg", + "email": "Bart Burg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4636, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90490"]] + }, + { + "_id": "62f3246a082fcc3049e91818", + "username": "a coder", + "email": "a coder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7214, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90491"]] + }, + { + "_id": "62f3246a082fcc3049e91819", + "username": "kosmos", + "email": "kosmos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4233, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90492"]] + }, + { + "_id": "62f3246a082fcc3049e9181a", + "username": "ryanpcmcquen", + "email": "ryanpcmcquen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5957, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90494"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066a"] + ] + }, + { + "_id": "62f3246a082fcc3049e9181b", + "username": "Yaroslav Yakovlev", + "email": "Yaroslav Yakovlev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5959, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90493"]] + }, + { + "_id": "62f3246a082fcc3049e9181d", + "username": "Jeff Baker", + "email": "Jeff Baker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1450, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90496"]] + }, + { + "_id": "62f3246a082fcc3049e9181f", + "username": "ASHISH KUMAR", + "email": "ASHISH KUMAR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 147, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246a082fcc3049e91821", + "username": "BENARD Patrick", + "email": "BENARD Patrick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29614, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246a082fcc3049e91822", + "username": "CodecPM", + "email": "CodecPM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 303, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90495"]] + }, + { + "_id": "62f3246a082fcc3049e91823", + "username": "callmemath", + "email": "callmemath@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7835, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90497"]] + }, + { + "_id": "62f3246a082fcc3049e91824", + "username": "NewToIOS", + "email": "NewToIOS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e90499"]] + }, + { + "_id": "62f3246a082fcc3049e91825", + "username": "Vasileios Pallas", + "email": "Vasileios Pallas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049a"]] + }, + { + "_id": "62f3246b082fcc3049e91827", + "username": "Khom Nazid", + "email": "Khom Nazid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 456, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91828", + "username": "Dominic", + "email": "Dominic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57405, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049d"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf6"] + ] + }, + { + "_id": "62f3246b082fcc3049e91829", + "username": "Gruffy", + "email": "Gruffy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1343, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049c"]] + }, + { + "_id": "62f3246b082fcc3049e9182a", + "username": "Dino Reic", + "email": "Dino Reic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3161, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049b"]] + }, + { + "_id": "62f3246b082fcc3049e9182c", + "username": "sof-03", + "email": "sof-03@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1855, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e9182e", + "username": "nikksan", + "email": "nikksan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3115, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049f"]] + }, + { + "_id": "62f3246b082fcc3049e91830", + "username": "Honsa Stunna", + "email": "Honsa Stunna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 536, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91832", + "username": "user875234", + "email": "user875234@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2243, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91834", + "username": "Arya", + "email": "Arya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 507, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91836", + "username": "synkro", + "email": "synkro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 357, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91838", + "username": "andreszs", + "email": "andreszs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2806, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e9183a", + "username": "liubiantao", + "email": "liubiantao@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1181, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e9183c", + "username": "mjs", + "email": "mjs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20271, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e9049e"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90602"] + ] + }, + { + "_id": "62f3246b082fcc3049e9183d", + "username": "user2080225", + "email": "user2080225@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a0"]] + }, + { + "_id": "62f3246b082fcc3049e9183e", + "username": "Mau", + "email": "Mau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a1"]] + }, + { + "_id": "62f3246b082fcc3049e9183f", + "username": "Alexander Mills", + "email": "Alexander Mills@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a2"]] + }, + { + "_id": "62f3246b082fcc3049e91841", + "username": "Boaz", + "email": "Boaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19070, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246b082fcc3049e91844", + "username": "Alexandru Sirbu", + "email": "Alexandru Sirbu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a4"]] + }, + { + "_id": "62f3246c082fcc3049e91845", + "username": "Chrillewoodz", + "email": "Chrillewoodz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25410, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a5"]] + }, + { + "_id": "62f3246c082fcc3049e91846", + "username": "Adithya Sai", + "email": "Adithya Sai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1394, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a6"]] + }, + { + "_id": "62f3246c082fcc3049e91848", + "username": "Luke_", + "email": "Luke_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 720, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9184a", + "username": "Grim", + "email": "Grim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4592, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a7"], + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b17"] + ] + }, + { + "_id": "62f3246c082fcc3049e9184c", + "username": "Joe Warner", + "email": "Joe Warner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3217, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9184e", + "username": "arjunattam", + "email": "arjunattam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2419, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91850", + "username": "kano", + "email": "kano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5126, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91852", + "username": "ramin", + "email": "ramin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 878, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91854", + "username": "TimH - Codidact", + "email": "TimH - Codidact@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1421, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91855", + "username": "KhoPhi", + "email": "KhoPhi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8977, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a8"]] + }, + { + "_id": "62f3246c082fcc3049e91856", + "username": "Nishant Singh", + "email": "Nishant Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 688, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904a9"]] + }, + { + "_id": "62f3246c082fcc3049e91857", + "username": "Lars Gross", + "email": "Lars Gross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904ac"]] + }, + { + "_id": "62f3246c082fcc3049e91858", + "username": "Renga", + "email": "Renga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 211, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904ab"]] + }, + { + "_id": "62f3246c082fcc3049e91859", + "username": "Crashalot", + "email": "Crashalot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32483, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904ad"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062c"] + ] + }, + { + "_id": "62f3246c082fcc3049e9185b", + "username": "Umut Can Arda", + "email": "Umut Can Arda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9185c", + "username": "Araston", + "email": "Araston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904ae"]] + }, + { + "_id": "62f3246c082fcc3049e9185e", + "username": "akhil_mittal", + "email": "akhil_mittal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21813, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91860", + "username": "tungd", + "email": "tungd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14097, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91862", + "username": "Salman von Abbas", + "email": "Salman von Abbas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23122, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bfd"]] + }, + { + "_id": "62f3246c082fcc3049e91864", + "username": "Wes McKinney", + "email": "Wes McKinney@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94511, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91866", + "username": "Jez", + "email": "Jez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26524, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91868", + "username": "rmh", + "email": "rmh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4636, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9186a", + "username": "abernier", + "email": "abernier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25480, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9186c", + "username": "drmrbrewer", + "email": "drmrbrewer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9933, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9186e", + "username": "radtek", + "email": "radtek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31556, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91870", + "username": "JaredCubilla", + "email": "JaredCubilla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 498, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91873", + "username": "tandy", + "email": "tandy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1871, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91875", + "username": "Julien", + "email": "Julien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91877", + "username": "Grant Birchmeier", + "email": "Grant Birchmeier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17148, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91879", + "username": "Tom W", + "email": "Tom W@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4718, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9187b", + "username": "sindrenm", + "email": "sindrenm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2924, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9187e", + "username": "Claudiu", + "email": "Claudiu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 217281, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91880", + "username": "thang", + "email": "thang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3324, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91882", + "username": "Synetech", + "email": "Synetech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9363, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91884", + "username": "dwlz", + "email": "dwlz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10424, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909dc"]] + }, + { + "_id": "62f3246c082fcc3049e91887", + "username": "EightyOne Unite", + "email": "EightyOne Unite@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11507, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91889", + "username": "Sina Madani", + "email": "Sina Madani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1169, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9188c", + "username": "tckmn", + "email": "tckmn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55893, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9188e", + "username": "jondavidjohn", + "email": "jondavidjohn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61044, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91891", + "username": "Christoffer", + "email": "Christoffer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12454, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91893", + "username": "nilamo", + "email": "nilamo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1904, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91895", + "username": "Luke", + "email": "Luke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3622, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90716"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90834"] + ] + }, + { + "_id": "62f3246c082fcc3049e91897", + "username": "Brann", + "email": "Brann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30821, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e91899", + "username": "cryss", + "email": "cryss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3930, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9189b", + "username": "Will", + "email": "Will@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c4"]] + }, + { + "_id": "62f3246c082fcc3049e9189d", + "username": "JBentley", + "email": "JBentley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5867, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e9189f", + "username": "Chris Morgan", + "email": "Chris Morgan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80499, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918a1", + "username": "fracz", + "email": "fracz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19523, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918a3", + "username": "FogleBird", + "email": "FogleBird@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71146, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918a5", + "username": "Davor Lucic", + "email": "Davor Lucic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28152, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918a7", + "username": "Jerry Tian", + "email": "Jerry Tian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3420, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918aa", + "username": "Alnitak", + "email": "Alnitak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 327328, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918ad", + "username": "WEFX", + "email": "WEFX@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7982, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918af", + "username": "kolypto", + "email": "kolypto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28192, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918b1", + "username": "David Andres", + "email": "David Andres@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30761, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918b3", + "username": "Drazen Bjelovuk", + "email": "Drazen Bjelovuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4925, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918b5", + "username": "Dave Cousineau", + "email": "Dave Cousineau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11143, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918b7", + "username": "Mathias Bynens", + "email": "Mathias Bynens@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 138950, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90972"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91004"] + ] + }, + { + "_id": "62f3246c082fcc3049e918b9", + "username": "meh", + "email": "meh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21730, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918bb", + "username": "muscardinus", + "email": "muscardinus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246c082fcc3049e918bd", + "username": "Calum", + "email": "Calum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5736, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246d082fcc3049e918bf", + "username": "lance.dolan", + "email": "lance.dolan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2912, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246d082fcc3049e918c0", + "username": "SanTasso", + "email": "SanTasso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 401, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904af"]] + }, + { + "_id": "62f3246d082fcc3049e918c1", + "username": "Игорь Хлебников", + "email": "Игорь Хлебников@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 479, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b0"]] + }, + { + "_id": "62f3246d082fcc3049e918c2", + "username": "YeetYeet", + "email": "YeetYeet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 120, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b1"]] + }, + { + "_id": "62f3246d082fcc3049e918c3", + "username": "Vince", + "email": "Vince@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 524, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b2"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e8a"] + ] + }, + { + "_id": "62f3246d082fcc3049e918c4", + "username": "Ritesh Tiwari", + "email": "Ritesh Tiwari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b3"]] + }, + { + "_id": "62f3246d082fcc3049e918c6", + "username": "Vivaan", + "email": "Vivaan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 140, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246d082fcc3049e918c8", + "username": "Rahul Daksh", + "email": "Rahul Daksh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 272, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b4"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa3"] + ] + }, + { + "_id": "62f3246d082fcc3049e918ca", + "username": "Hannes Schneidermayer", + "email": "Hannes Schneidermayer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3636, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246d082fcc3049e918cb", + "username": "HO LI Pin", + "email": "HO LI Pin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b5"]] + }, + { + "_id": "62f3246d082fcc3049e918cc", + "username": "Ali Yaghoubi", + "email": "Ali Yaghoubi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1158, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b7"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e7"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b7"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e48"] + ] + }, + { + "_id": "62f3246d082fcc3049e918ce", + "username": "PrashSE", + "email": "PrashSE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 167, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246d082fcc3049e918cf", + "username": "Tauseef Arshad", + "email": "Tauseef Arshad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 385, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b8"], + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904ba"] + ] + }, + { + "_id": "62f3246e082fcc3049e918d2", + "username": "Lowis", + "email": "Lowis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 123, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918d4", + "username": "jbyrd", + "email": "jbyrd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4827, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904b9"]] + }, + { + "_id": "62f3246e082fcc3049e918d5", + "username": "Judith lobo", + "email": "Judith lobo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904bb"]] + }, + { + "_id": "62f3246e082fcc3049e918d6", + "username": "NVRM", + "email": "NVRM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9294, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904bc"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d41"] + ] + }, + { + "_id": "62f3246e082fcc3049e918d8", + "username": "clozach", + "email": "clozach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4997, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918d9", + "username": "Royce", + "email": "Royce@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 143, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec5", "62f321c0082fcc3049e904bd"]] + }, + { + "_id": "62f3246e082fcc3049e918db", + "username": "Micah Snyder", + "email": "Micah Snyder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 503, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918dd", + "username": "thetoolman", + "email": "thetoolman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2134, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918df", + "username": "Neurotransmitter", + "email": "Neurotransmitter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5666, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918e1", + "username": "Diego Ponciano", + "email": "Diego Ponciano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 396, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918e3", + "username": "Abel", + "email": "Abel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918e5", + "username": "Dale Harris", + "email": "Dale Harris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 518, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918e6", + "username": "stevehipwell", + "email": "stevehipwell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53588, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904be"]] + }, + { + "_id": "62f3246e082fcc3049e918e7", + "username": "Darren Clark", + "email": "Darren Clark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2863, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c1"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e907ff"] + ] + }, + { + "_id": "62f3246e082fcc3049e918e9", + "username": "Daniel A. White", + "email": "Daniel A. White@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 182448, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918eb", + "username": "CodyBugstein", + "email": "CodyBugstein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19823, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246e082fcc3049e918ec", + "username": "Crescent Fresh", + "email": "Crescent Fresh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 112662, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c2"]] + }, + { + "_id": "62f3246f082fcc3049e918ed", + "username": "Paul McMillan", + "email": "Paul McMillan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c3"]] + }, + { + "_id": "62f3246f082fcc3049e918ef", + "username": "cllpse", + "email": "cllpse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20926, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905ba"]] + }, + { + "_id": "62f3246f082fcc3049e918f1", + "username": "Maik Lowrey", + "email": "Maik Lowrey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11633, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246f082fcc3049e918f3", + "username": "Sam Johnson", + "email": "Sam Johnson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 546, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3246f082fcc3049e918f5", + "username": "Unmitigated", + "email": "Unmitigated@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47924, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae2"]] + }, + { + "_id": "62f3246f082fcc3049e918f6", + "username": "Tom Ritter", + "email": "Tom Ritter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 98264, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c4"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c19"] + ] + }, + { + "_id": "62f3246f082fcc3049e918f7", + "username": "Annika Backstrom", + "email": "Annika Backstrom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13627, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c6"]] + }, + { + "_id": "62f324ac082fcc3049e918f9", + "username": "ruffin", + "email": "ruffin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15185, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ac082fcc3049e918fa", + "username": "Navin Rauniyar", + "email": "Navin Rauniyar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9517, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904cd"]] + }, + { + "_id": "62f324ac082fcc3049e918fb", + "username": "Warren Davis", + "email": "Warren Davis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 313, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904ce"]] + }, + { + "_id": "62f324ac082fcc3049e918fc", + "username": "JeevanReddy Avanaganti", + "email": "JeevanReddy Avanaganti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d0"]] + }, + { + "_id": "62f324ac082fcc3049e918fd", + "username": "user3698272", + "email": "user3698272@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904cf"]] + }, + { + "_id": "62f324ac082fcc3049e918fe", + "username": "Greg Gum", + "email": "Greg Gum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30100, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d2"]] + }, + { + "_id": "62f324ac082fcc3049e918ff", + "username": "GibboK", + "email": "GibboK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 68666, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d1"], + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904fb"], + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c9"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dbc"] + ] + }, + { + "_id": "62f324ac082fcc3049e91900", + "username": "GreQ", + "email": "GreQ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d3"]] + }, + { + "_id": "62f324ac082fcc3049e91901", + "username": "Abhay Dixit", + "email": "Abhay Dixit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 970, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d4"]] + }, + { + "_id": "62f324ac082fcc3049e91902", + "username": "Wouter Vanherck", + "email": "Wouter Vanherck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d6"]] + }, + { + "_id": "62f324ad082fcc3049e91903", + "username": "efkan", + "email": "efkan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d8"]] + }, + { + "_id": "62f324ad082fcc3049e91904", + "username": "Khuong", + "email": "Khuong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10636, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904d9"]] + }, + { + "_id": "62f324ad082fcc3049e91905", + "username": "مهدی عابدی برنامه نویس و مشاور", + "email": "مهدی عابدی برنامه نویس و مشاور@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904da"]] + }, + { + "_id": "62f324ad082fcc3049e91906", + "username": "Lakmal", + "email": "Lakmal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 689, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904dc"]] + }, + { + "_id": "62f324ad082fcc3049e91908", + "username": "Andreas", + "email": "Andreas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21000, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ad082fcc3049e91909", + "username": "Lucky Brain", + "email": "Lucky Brain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1441, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904de"]] + }, + { + "_id": "62f324ad082fcc3049e9190a", + "username": "Umesh Bhutada", + "email": "Umesh Bhutada@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904e0"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904e1"] + ] + }, + { + "_id": "62f324ad082fcc3049e9190b", + "username": "nguyenhoavuong", + "email": "nguyenhoavuong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904df"]] + }, + { + "_id": "62f324ae082fcc3049e9190d", + "username": "niczak", + "email": "niczak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3817, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e9190f", + "username": "namtax", + "email": "namtax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2297, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e91912", + "username": "Michael Buen", + "email": "Michael Buen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37596, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e91913", + "username": "ikettu", + "email": "ikettu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e4"]] + }, + { + "_id": "62f324ae082fcc3049e91916", + "username": "Thevs", + "email": "Thevs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3161, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e3"]] + }, + { + "_id": "62f324ae082fcc3049e9191a", + "username": "Peter Aron Zentai", + "email": "Peter Aron Zentai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10902, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e9191d", + "username": "itdoesntwork", + "email": "itdoesntwork@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4496, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e9191f", + "username": "skierpage", + "email": "skierpage@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2384, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e91920", + "username": "starikovs", + "email": "starikovs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3052, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e5"]] + }, + { + "_id": "62f324ae082fcc3049e91922", + "username": "Michał Miszczyszyn", + "email": "Michał Miszczyszyn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10860, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e91924", + "username": "Paul Sanwald", + "email": "Paul Sanwald@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10461, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e91925", + "username": "Erik Töyrä Silfverswärd", + "email": "Erik Töyrä Silfverswärd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9832, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e6"]] + }, + { + "_id": "62f324ae082fcc3049e91926", + "username": "Anton Danilchenko", + "email": "Anton Danilchenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e7"]] + }, + { + "_id": "62f324ae082fcc3049e91927", + "username": "Baggz", + "email": "Baggz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16835, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904e8"]] + }, + { + "_id": "62f324ae082fcc3049e91929", + "username": "cjbarth", + "email": "cjbarth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3922, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ae082fcc3049e9192a", + "username": "NiKo", + "email": "NiKo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10884, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ea"]] + }, + { + "_id": "62f324af082fcc3049e9192b", + "username": "will Farrell", + "email": "will Farrell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1670, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904eb"]] + }, + { + "_id": "62f324af082fcc3049e9192d", + "username": "user663031", + "email": "user663031@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064f"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc7"] + ] + }, + { + "_id": "62f324af082fcc3049e91930", + "username": "Ikram Khizer", + "email": "Ikram Khizer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324af082fcc3049e91931", + "username": "Erin", + "email": "Erin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1636, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ec"]] + }, + { + "_id": "62f324af082fcc3049e91933", + "username": "davidhadas", + "email": "davidhadas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904fa"]] + }, + { + "_id": "62f324af082fcc3049e91936", + "username": "onestep.ua", + "email": "onestep.ua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324af082fcc3049e91937", + "username": "Ateszki", + "email": "Ateszki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ef"]] + }, + { + "_id": "62f324af082fcc3049e9193a", + "username": "es cologne", + "email": "es cologne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2848, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f1"]] + }, + { + "_id": "62f324af082fcc3049e9193b", + "username": "Jonathan Petitcolas", + "email": "Jonathan Petitcolas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4018, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f0"]] + }, + { + "_id": "62f324af082fcc3049e9193c", + "username": "kiranvj", + "email": "kiranvj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28674, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ed"]] + }, + { + "_id": "62f324af082fcc3049e9193d", + "username": "CatTest", + "email": "CatTest@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f3"]] + }, + { + "_id": "62f324af082fcc3049e9193e", + "username": "chandu", + "email": "chandu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2248, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f2"]] + }, + { + "_id": "62f324af082fcc3049e9193f", + "username": "NiRUS", + "email": "NiRUS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3418, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f4"]] + }, + { + "_id": "62f324b0082fcc3049e91940", + "username": "cwadding", + "email": "cwadding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 840, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f6"]] + }, + { + "_id": "62f324b0082fcc3049e91942", + "username": "Rahul Malhotra", + "email": "Rahul Malhotra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f5"]] + }, + { + "_id": "62f324b0082fcc3049e91943", + "username": "Slavik Meltser", + "email": "Slavik Meltser@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8684, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f7"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cff"] + ] + }, + { + "_id": "62f324b0082fcc3049e91946", + "username": "Nate", + "email": "Nate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18383, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909ee"]] + }, + { + "_id": "62f324b0082fcc3049e91948", + "username": "Đinh Carabus", + "email": "Đinh Carabus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3129, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b0082fcc3049e9194a", + "username": "Dipanshu Mahla", + "email": "Dipanshu Mahla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 154, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b0082fcc3049e9194c", + "username": "Wronski", + "email": "Wronski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1354, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b0082fcc3049e9194e", + "username": "synthet1c", + "email": "synthet1c@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5905, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904f9"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f49"] + ] + }, + { + "_id": "62f324b0082fcc3049e91951", + "username": "Gust van de Wal", + "email": "Gust van de Wal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5076, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b0082fcc3049e91952", + "username": "Igor Kokotko", + "email": "Igor Kokotko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 470, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904fc"]] + }, + { + "_id": "62f324b0082fcc3049e91953", + "username": "Jesse", + "email": "Jesse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6225, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904fd"]] + }, + { + "_id": "62f324b1082fcc3049e91954", + "username": "Jplus2", + "email": "Jplus2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1942, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90500"]] + }, + { + "_id": "62f324b1082fcc3049e91955", + "username": "Jonathan", + "email": "Jonathan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e904ff"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc4"] + ] + }, + { + "_id": "62f324b1082fcc3049e91956", + "username": "Anthony D'Amato", + "email": "Anthony D'Amato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 717, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90502"]] + }, + { + "_id": "62f324b1082fcc3049e91957", + "username": "João Pimentel Ferreira", + "email": "João Pimentel Ferreira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11989, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90501"], + ["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb9"] + ] + }, + { + "_id": "62f324b1082fcc3049e91958", + "username": "Роман Татаринов", + "email": "Роман Татаринов@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 558, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90504"]] + }, + { + "_id": "62f324b1082fcc3049e91959", + "username": "Tarandeep Singh", + "email": "Tarandeep Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1258, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90506"]] + }, + { + "_id": "62f324b1082fcc3049e9195a", + "username": "Ashikur Rahman", + "email": "Ashikur Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90505"]] + }, + { + "_id": "62f324b1082fcc3049e9195b", + "username": "Aakash Handa", + "email": "Aakash Handa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1043, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90508"]] + }, + { + "_id": "62f324b1082fcc3049e9195c", + "username": "Juan Vieira", + "email": "Juan Vieira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90507"]] + }, + { + "_id": "62f324b2082fcc3049e9195e", + "username": "Damien", + "email": "Damien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1422, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b8a"]] + }, + { + "_id": "62f324b2082fcc3049e91960", + "username": "Ivanka Todorova", + "email": "Ivanka Todorova@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9603, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b2082fcc3049e91962", + "username": "Kunal Tyagi", + "email": "Kunal Tyagi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b2082fcc3049e91963", + "username": "Alex Tudor", + "email": "Alex Tudor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e90509"]] + }, + { + "_id": "62f324b2082fcc3049e91964", + "username": "Ericgit", + "email": "Ericgit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4721, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050a"]] + }, + { + "_id": "62f324b2082fcc3049e91965", + "username": "ganesh phirke", + "email": "ganesh phirke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 441, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050b"]] + }, + { + "_id": "62f324b2082fcc3049e91966", + "username": "010011100101", + "email": "010011100101@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1512, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050c"]] + }, + { + "_id": "62f324b2082fcc3049e91967", + "username": "Ron Jonk", + "email": "Ron Jonk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 598, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050d"]] + }, + { + "_id": "62f324b2082fcc3049e91968", + "username": "hardik", + "email": "hardik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec7", "62f321c1082fcc3049e9050e"]] + }, + { + "_id": "62f324b2082fcc3049e9196a", + "username": "Isaac Pak", + "email": "Isaac Pak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3777, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b2082fcc3049e9196c", + "username": "sebarmeli", + "email": "sebarmeli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17679, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90512"]] + }, + { + "_id": "62f324b2082fcc3049e9196e", + "username": "Neil", + "email": "Neil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2624, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf2"]] + }, + { + "_id": "62f324b2082fcc3049e9196f", + "username": "falmp", + "email": "falmp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39367, + "questionIds": ["62f321bb082fcc3049e8fec7"], + "answerIds": [] + }, + { + "_id": "62f324b2082fcc3049e91971", + "username": "hasen", + "email": "hasen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 156427, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90511"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ca"] + ] + }, + { + "_id": "62f324b3082fcc3049e91973", + "username": "Chris Cooper", + "email": "Chris Cooper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 842, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b3082fcc3049e91975", + "username": "Noz", + "email": "Noz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6115, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b3082fcc3049e91976", + "username": "Gabriel", + "email": "Gabriel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17872, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90513"]] + }, + { + "_id": "62f324b3082fcc3049e91978", + "username": "kirilloid", + "email": "kirilloid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90515"]] + }, + { + "_id": "62f324b3082fcc3049e91979", + "username": "Andrew Thomson", + "email": "Andrew Thomson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4332, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90516"]] + }, + { + "_id": "62f324b3082fcc3049e9197a", + "username": "Muhammad Alvin", + "email": "Muhammad Alvin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1150, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90517"]] + }, + { + "_id": "62f324b3082fcc3049e9197c", + "username": "danwellman", + "email": "danwellman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8717, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b3082fcc3049e9197d", + "username": "Phrogz", + "email": "Phrogz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 286756, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90518"]] + }, + { + "_id": "62f324b3082fcc3049e9197e", + "username": "justingordon", + "email": "justingordon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90519"]] + }, + { + "_id": "62f324b3082fcc3049e91980", + "username": "daniel1426", + "email": "daniel1426@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 169, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd8"]] + }, + { + "_id": "62f324b3082fcc3049e91981", + "username": "Marlon Bernardes", + "email": "Marlon Bernardes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12547, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051c"]] + }, + { + "_id": "62f324b3082fcc3049e91983", + "username": "staticd", + "email": "staticd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1194, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051b"]] + }, + { + "_id": "62f324b4082fcc3049e91985", + "username": "Deniz Ozger", + "email": "Deniz Ozger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2515, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b4082fcc3049e91988", + "username": "njzk2", + "email": "njzk2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38236, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b4082fcc3049e91989", + "username": "molokoloco", + "email": "molokoloco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4326, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051e"]] + }, + { + "_id": "62f324b4082fcc3049e9198a", + "username": "RizN81", + "email": "RizN81@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 999, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90520"]] + }, + { + "_id": "62f324b4082fcc3049e9198c", + "username": "Sambhav Sharma", + "email": "Sambhav Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5553, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9051f"]] + }, + { + "_id": "62f324b4082fcc3049e9198e", + "username": "user513951", + "email": "user513951@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11700, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b4082fcc3049e9198f", + "username": "Daniel K.", + "email": "Daniel K.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1236, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90522"]] + }, + { + "_id": "62f324b4082fcc3049e91990", + "username": "Joter", + "email": "Joter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90521"]] + }, + { + "_id": "62f324b4082fcc3049e91991", + "username": "Redoman", + "email": "Redoman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2713, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90524"]] + }, + { + "_id": "62f324b4082fcc3049e91992", + "username": "Juanjo Salvador", + "email": "Juanjo Salvador@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1017, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90523"]] + }, + { + "_id": "62f324b4082fcc3049e91994", + "username": "user6139250", + "email": "user6139250@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90525"]] + }, + { + "_id": "62f324b4082fcc3049e91996", + "username": "brk", + "email": "brk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47090, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b4082fcc3049e91997", + "username": "Shubham Khatri", + "email": "Shubham Khatri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 251204, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90526"]] + }, + { + "_id": "62f324b5082fcc3049e91998", + "username": "Dan Chill", + "email": "Dan Chill@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 466, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90528"]] + }, + { + "_id": "62f324b5082fcc3049e91999", + "username": "bzim", + "email": "bzim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1002, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90529"]] + }, + { + "_id": "62f324b5082fcc3049e9199a", + "username": "Alongkorn", + "email": "Alongkorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3474, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052a"]] + }, + { + "_id": "62f324b5082fcc3049e9199b", + "username": "Espen", + "email": "Espen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2370, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052b"]] + }, + { + "_id": "62f324b5082fcc3049e9199d", + "username": "Michel Jung", + "email": "Michel Jung@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b5082fcc3049e9199e", + "username": "Andrii Starusiev", + "email": "Andrii Starusiev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7068, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052d"]] + }, + { + "_id": "62f324b5082fcc3049e919a0", + "username": "domdambrogia", + "email": "domdambrogia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1907, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b5082fcc3049e919a2", + "username": "Shijo Rs", + "email": "Shijo Rs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90530"]] + }, + { + "_id": "62f324b5082fcc3049e919a4", + "username": "DarckBlezzer", + "email": "DarckBlezzer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4180, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b5082fcc3049e919a6", + "username": "colxi", + "email": "colxi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e9052f"]] + }, + { + "_id": "62f324b6082fcc3049e919ab", + "username": "Arthur S", + "email": "Arthur S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b6082fcc3049e919ad", + "username": "TheComputerWizard", + "email": "TheComputerWizard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 140, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90533"]] + }, + { + "_id": "62f324b6082fcc3049e919af", + "username": "YesItsMe", + "email": "YesItsMe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1659, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b6082fcc3049e919b0", + "username": "Aleksandr Golovatyi", + "email": "Aleksandr Golovatyi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90535"]] + }, + { + "_id": "62f324b6082fcc3049e919b1", + "username": "Alfredo Rahn Linde", + "email": "Alfredo Rahn Linde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec8", "62f321c1082fcc3049e90536"]] + }, + { + "_id": "62f324b6082fcc3049e919b2", + "username": "Mark Szymanski", + "email": "Mark Szymanski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54102, + "questionIds": ["62f321bb082fcc3049e8fec8"], + "answerIds": [] + }, + { + "_id": "62f324b6082fcc3049e919b4", + "username": "Nosredna", + "email": "Nosredna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79380, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90733"]] + }, + { + "_id": "62f324b6082fcc3049e919b7", + "username": "Eli Courtwright", + "email": "Eli Courtwright@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 177613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90538"]] + }, + { + "_id": "62f324b6082fcc3049e919b8", + "username": "user2320522", + "email": "user2320522@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 391, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053a"]] + }, + { + "_id": "62f324b7082fcc3049e919bb", + "username": "ARJUN", + "email": "ARJUN@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 399, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b7082fcc3049e919bc", + "username": "Lavi Avigdor", + "email": "Lavi Avigdor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3872, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053b"]] + }, + { + "_id": "62f324b7082fcc3049e919bd", + "username": "handle", + "email": "handle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6033, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053d"]] + }, + { + "_id": "62f324b7082fcc3049e919be", + "username": "jaredwilli", + "email": "jaredwilli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11194, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053e"]] + }, + { + "_id": "62f324b7082fcc3049e919bf", + "username": "Webeng", + "email": "Webeng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6806, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9053f"]] + }, + { + "_id": "62f324b7082fcc3049e919c0", + "username": "Hajji Tarik", + "email": "Hajji Tarik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1072, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90541"]] + }, + { + "_id": "62f324b7082fcc3049e919c2", + "username": "Sid", + "email": "Sid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 76, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b7082fcc3049e919c5", + "username": "realappie", + "email": "realappie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4285, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90544"]] + }, + { + "_id": "62f324b8082fcc3049e919c6", + "username": "NAVIN", + "email": "NAVIN@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2973, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90546"]] + }, + { + "_id": "62f324b8082fcc3049e919c7", + "username": "Alexander", + "email": "Alexander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6788, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90545"]] + }, + { + "_id": "62f324b8082fcc3049e919c8", + "username": "shekhardtu", + "email": "shekhardtu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3350, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90548"]] + }, + { + "_id": "62f324b8082fcc3049e919ca", + "username": "ken", + "email": "ken@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3548, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b8082fcc3049e919cc", + "username": "Anupam Maurya", + "email": "Anupam Maurya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1585, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90549"], + ["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90553"] + ] + }, + { + "_id": "62f324b8082fcc3049e919ce", + "username": "jerryurenaa", + "email": "jerryurenaa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2796, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054c"]] + }, + { + "_id": "62f324b8082fcc3049e919cf", + "username": "Aditya Rewari", + "email": "Aditya Rewari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054d"]] + }, + { + "_id": "62f324b8082fcc3049e919d0", + "username": "Gel", + "email": "Gel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2634, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054e"]] + }, + { + "_id": "62f324b9082fcc3049e919d1", + "username": "Miki", + "email": "Miki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1357, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e9054f"]] + }, + { + "_id": "62f324b9082fcc3049e919d2", + "username": "Yogesh More", + "email": "Yogesh More@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 83, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90551"]] + }, + { + "_id": "62f324b9082fcc3049e919d3", + "username": "Rupam", + "email": "Rupam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1422, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90550"]] + }, + { + "_id": "62f324b9082fcc3049e919d4", + "username": "Nice Books", + "email": "Nice Books@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90552"]] + }, + { + "_id": "62f324b9082fcc3049e919d5", + "username": "nos nart", + "email": "nos nart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec9", "62f321c1082fcc3049e90554"]] + }, + { + "_id": "62f324b9082fcc3049e919d6", + "username": "Adam Ernst", + "email": "Adam Ernst@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50094, + "questionIds": ["62f321bb082fcc3049e8fec9"], + "answerIds": [] + }, + { + "_id": "62f324b9082fcc3049e919d7", + "username": "Rohan Patil", + "email": "Rohan Patil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1818, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056c"]] + }, + { + "_id": "62f324b9082fcc3049e919da", + "username": "Zanoni", + "email": "Zanoni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28848, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90569"]] + }, + { + "_id": "62f324b9082fcc3049e919dc", + "username": "chendral", + "email": "chendral@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 684, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b9082fcc3049e919de", + "username": "GabrielBB", + "email": "GabrielBB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2228, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b9082fcc3049e919e0", + "username": "wranvaud", + "email": "wranvaud@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1033, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324b9082fcc3049e919e2", + "username": "VolkerK", + "email": "VolkerK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056a"]] + }, + { + "_id": "62f324ba082fcc3049e919e4", + "username": "AJcodez", + "email": "AJcodez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29258, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ba082fcc3049e919e6", + "username": "Apollo Data", + "email": "Apollo Data@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1147, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057e"]] + }, + { + "_id": "62f324ba082fcc3049e919e8", + "username": "madaimartin", + "email": "madaimartin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ba082fcc3049e919ea", + "username": "Radmation", + "email": "Radmation@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1450, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ba082fcc3049e919eb", + "username": "Nikhil Agrawal", + "email": "Nikhil Agrawal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25150, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056d"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5b"] + ] + }, + { + "_id": "62f324ba082fcc3049e919ec", + "username": "Syed Nasir Abbas", + "email": "Syed Nasir Abbas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1554, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056f"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce2"] + ] + }, + { + "_id": "62f324ba082fcc3049e919ed", + "username": "Dorian", + "email": "Dorian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21079, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9056e"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b84"] + ] + }, + { + "_id": "62f324ba082fcc3049e919ef", + "username": "OMGPOP", + "email": "OMGPOP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2387, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ba082fcc3049e919f0", + "username": "kishore", + "email": "kishore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1590, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90570"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5d"] + ] + }, + { + "_id": "62f324ba082fcc3049e919f1", + "username": "Josip Ivic", + "email": "Josip Ivic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3539, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90572"]] + }, + { + "_id": "62f324ba082fcc3049e919f2", + "username": "Bhaskar Bhatt", + "email": "Bhaskar Bhatt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1369, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90574"]] + }, + { + "_id": "62f324ba082fcc3049e919f3", + "username": "زياد", + "email": "زياد@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 882, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90576"]] + }, + { + "_id": "62f324ba082fcc3049e919f4", + "username": "Khaled Harby", + "email": "Khaled Harby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90575"]] + }, + { + "_id": "62f324bb082fcc3049e919f5", + "username": "Tmh", + "email": "Tmh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1191, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90577"]] + }, + { + "_id": "62f324bb082fcc3049e919f6", + "username": "Hitesh Sahu", + "email": "Hitesh Sahu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90579"]] + }, + { + "_id": "62f324bb082fcc3049e919f7", + "username": "Hasib Kamal Chowdhury", + "email": "Hasib Kamal Chowdhury@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2328, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057a"], + ["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c95"] + ] + }, + { + "_id": "62f324bb082fcc3049e919f8", + "username": "Ashish Kamble", + "email": "Ashish Kamble@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057c"]] + }, + { + "_id": "62f324bb082fcc3049e919f9", + "username": "curiosity", + "email": "curiosity@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 804, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057b"]] + }, + { + "_id": "62f324bb082fcc3049e919fa", + "username": "Seph Reed", + "email": "Seph Reed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6753, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e9057d"]] + }, + { + "_id": "62f324bb082fcc3049e919fb", + "username": "Programmer", + "email": "Programmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90580"]] + }, + { + "_id": "62f324f9082fcc3049e919fc", + "username": "Burak Keceli", + "email": "Burak Keceli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 903, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058b"]] + }, + { + "_id": "62f324f9082fcc3049e919fd", + "username": "Jamie Mason", + "email": "Jamie Mason@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3992, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058c"]] + }, + { + "_id": "62f324f9082fcc3049e919fe", + "username": "agershun", + "email": "agershun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3983, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058d"]] + }, + { + "_id": "62f324f9082fcc3049e919ff", + "username": "Morteza Tourani", + "email": "Morteza Tourani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3378, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058f"]] + }, + { + "_id": "62f324f9082fcc3049e91a00", + "username": "Evgenii", + "email": "Evgenii@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3053, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90590"]] + }, + { + "_id": "62f324f9082fcc3049e91a01", + "username": "Caio Ladislau", + "email": "Caio Ladislau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1237, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90592"]] + }, + { + "_id": "62f324f9082fcc3049e91a04", + "username": "Roberto14", + "email": "Roberto14@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 681, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324f9082fcc3049e91a05", + "username": "Gil Epshtain", + "email": "Gil Epshtain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7488, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90591"]] + }, + { + "_id": "62f324f9082fcc3049e91a07", + "username": "Jon Harding", + "email": "Jon Harding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4878, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324f9082fcc3049e91a09", + "username": "Vlad Bezden", + "email": "Vlad Bezden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 74540, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90593"]] + }, + { + "_id": "62f324f9082fcc3049e91a0b", + "username": "José Antonio Postigo", + "email": "José Antonio Postigo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2208, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90991"]] + }, + { + "_id": "62f324f9082fcc3049e91a0d", + "username": "gamelover42", + "email": "gamelover42@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 336, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324f9082fcc3049e91a0f", + "username": "madprops", + "email": "madprops@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3619, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7f"]] + }, + { + "_id": "62f324f9082fcc3049e91a10", + "username": "Roshni Bokade", + "email": "Roshni Bokade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 333, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90594"]] + }, + { + "_id": "62f324fa082fcc3049e91a12", + "username": "Penguin9", + "email": "Penguin9@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 481, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fa082fcc3049e91a13", + "username": "jmwierzbicki", + "email": "jmwierzbicki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90595"]] + }, + { + "_id": "62f324fa082fcc3049e91a15", + "username": "Den Kerny", + "email": "Den Kerny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 523, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fa082fcc3049e91a16", + "username": "ravshansbox", + "email": "ravshansbox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 714, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90597"]] + }, + { + "_id": "62f324fa082fcc3049e91a17", + "username": "Luke Schoen", + "email": "Luke Schoen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3786, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90598"]] + }, + { + "_id": "62f324fa082fcc3049e91a18", + "username": "depiction", + "email": "depiction@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 742, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90599"]] + }, + { + "_id": "62f324fa082fcc3049e91a19", + "username": "Partha Roy", + "email": "Partha Roy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059b"]] + }, + { + "_id": "62f324fa082fcc3049e91a1a", + "username": "Sridhar Sg", + "email": "Sridhar Sg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1356, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059c"]] + }, + { + "_id": "62f324fa082fcc3049e91a1b", + "username": "Nico Van Belle", + "email": "Nico Van Belle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4712, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059e"]] + }, + { + "_id": "62f324fa082fcc3049e91a1c", + "username": "sg28", + "email": "sg28@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1353, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059d"]] + }, + { + "_id": "62f324fb082fcc3049e91a1d", + "username": "Bob Stein", + "email": "Bob Stein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14777, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a0"], + ["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da6"] + ] + }, + { + "_id": "62f324fb082fcc3049e91a1f", + "username": "TitanFighter", + "email": "TitanFighter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4188, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fb082fcc3049e91a21", + "username": "Patrick Roberts", + "email": "Patrick Roberts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45656, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a6"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a6"] + ] + }, + { + "_id": "62f324fb082fcc3049e91a23", + "username": "Jean-François Beauchamp", + "email": "Jean-François Beauchamp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5296, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fb082fcc3049e91a25", + "username": "Ahsan", + "email": "Ahsan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 568, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fb082fcc3049e91a27", + "username": "Kojo", + "email": "Kojo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fb082fcc3049e91a28", + "username": "Damian Pavlica", + "email": "Damian Pavlica@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27183, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9059f"], + ["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6f"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe4"] + ] + }, + { + "_id": "62f324fb082fcc3049e91a29", + "username": "karthik006", + "email": "karthik006@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 716, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a1"]] + }, + { + "_id": "62f324fb082fcc3049e91a2b", + "username": "manjuvreddy", + "email": "manjuvreddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 302, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fb082fcc3049e91a2c", + "username": "Francois Girard", + "email": "Francois Girard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a2"]] + }, + { + "_id": "62f324fb082fcc3049e91a2d", + "username": "0leg", + "email": "0leg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a4"]] + }, + { + "_id": "62f324fb082fcc3049e91a2e", + "username": "Mas", + "email": "Mas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1219, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a8"]] + }, + { + "_id": "62f324fb082fcc3049e91a2f", + "username": "Harshal Y.", + "email": "Harshal Y.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4555, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a7"]] + }, + { + "_id": "62f324fc082fcc3049e91a30", + "username": "Ferrybig", + "email": "Ferrybig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905a9"]] + }, + { + "_id": "62f324fc082fcc3049e91a31", + "username": "Jadli", + "email": "Jadli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 840, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905aa"]] + }, + { + "_id": "62f324fc082fcc3049e91a33", + "username": "Ankesh Pandey", + "email": "Ankesh Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fc082fcc3049e91a35", + "username": "cbdeveloper", + "email": "cbdeveloper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23052, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905ac"]] + }, + { + "_id": "62f324fc082fcc3049e91a37", + "username": "Omar Hasan", + "email": "Omar Hasan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 549, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fc082fcc3049e91a39", + "username": "Thorvald", + "email": "Thorvald@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 431, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fc082fcc3049e91a3a", + "username": "Abhishek", + "email": "Abhishek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 533, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905ad"]] + }, + { + "_id": "62f324fc082fcc3049e91a3b", + "username": "Nur", + "email": "Nur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2156, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905ae"]] + }, + { + "_id": "62f324fc082fcc3049e91a3c", + "username": "SeyyedKhandon", + "email": "SeyyedKhandon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4269, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905af"]] + }, + { + "_id": "62f324fc082fcc3049e91a3d", + "username": "Marinos An", + "email": "Marinos An@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7938, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b0"]] + }, + { + "_id": "62f324fc082fcc3049e91a3e", + "username": "Wallace Sidhrée", + "email": "Wallace Sidhrée@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b2"]] + }, + { + "_id": "62f324fd082fcc3049e91a40", + "username": "Someone Special", + "email": "Someone Special@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10540, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a43", + "username": "Kaleem Elahi", + "email": "Kaleem Elahi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 176, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a45", + "username": "Rustam", + "email": "Rustam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b6"]] + }, + { + "_id": "62f324fd082fcc3049e91a47", + "username": "Gangula", + "email": "Gangula@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3716, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a49", + "username": "muasif80", + "email": "muasif80@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4848, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b5"]] + }, + { + "_id": "62f324fd082fcc3049e91a4c", + "username": "David Scott Kirby", + "email": "David Scott Kirby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 176, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a4d", + "username": "ccpizza", + "email": "ccpizza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e905b7"]] + }, + { + "_id": "62f324fd082fcc3049e91a4f", + "username": "rashidnk", + "email": "rashidnk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3898, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a51", + "username": "Mike Brockington", + "email": "Mike Brockington@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 585, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a54", + "username": "Valentin H", + "email": "Valentin H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7092, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a56", + "username": "Brent Bradburn", + "email": "Brent Bradburn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c8"]] + }, + { + "_id": "62f324fd082fcc3049e91a57", + "username": "Chris Noe", + "email": "Chris Noe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905b9"]] + }, + { + "_id": "62f324fd082fcc3049e91a59", + "username": "Pic Mickael", + "email": "Pic Mickael@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1214, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a5d", + "username": "flash", + "email": "flash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 388, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a5f", + "username": "driAn", + "email": "driAn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3219, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a61", + "username": "Luke101", + "email": "Luke101@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60059, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a63", + "username": "vhanla", + "email": "vhanla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 655, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fd082fcc3049e91a64", + "username": "Sugendran", + "email": "Sugendran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2069, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905bc"]] + }, + { + "_id": "62f324fe082fcc3049e91a65", + "username": "jmc734", + "email": "jmc734@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905bd"]] + }, + { + "_id": "62f324fe082fcc3049e91a67", + "username": "Orpheus", + "email": "Orpheus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 677, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a68", + "username": "oem", + "email": "oem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 211, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905be"]] + }, + { + "_id": "62f324fe082fcc3049e91a6a", + "username": "xarlymg89", + "email": "xarlymg89@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2483, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a6c", + "username": "Boris J.", + "email": "Boris J.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a6e", + "username": "Joseph Hamilton", + "email": "Joseph Hamilton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 420, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a70", + "username": "Scotty Jamison", + "email": "Scotty Jamison@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6994, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a71", + "username": "Jet", + "email": "Jet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1255, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905bf"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b4d"] + ] + }, + { + "_id": "62f324fe082fcc3049e91a73", + "username": "Vincent", + "email": "Vincent@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5884, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a75", + "username": "grandouassou", + "email": "grandouassou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2438, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a77", + "username": "khebbie", + "email": "khebbie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2472, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a79", + "username": "Schadenfreude", + "email": "Schadenfreude@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1135, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a7b", + "username": "Harsh Pandey", + "email": "Harsh Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 821, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a7d", + "username": "Giorgi Moniava", + "email": "Giorgi Moniava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24208, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a80", + "username": "johnw182", + "email": "johnw182@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1248, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a81", + "username": "Jano González", + "email": "Jano González@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14126, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c0"]] + }, + { + "_id": "62f324fe082fcc3049e91a84", + "username": "alexandre-rousseau", + "email": "alexandre-rousseau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2050, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a85", + "username": "Doug", + "email": "Doug@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 95, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c1"]] + }, + { + "_id": "62f324fe082fcc3049e91a88", + "username": "Dario Oddenino", + "email": "Dario Oddenino@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1233, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a8a", + "username": "shinzou", + "email": "shinzou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5560, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a8c", + "username": "Oliver Spryn", + "email": "Oliver Spryn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16226, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a8e", + "username": "karthick.sk", + "email": "karthick.sk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c3"]] + }, + { + "_id": "62f324fe082fcc3049e91a8f", + "username": "Muhammad Salman", + "email": "Muhammad Salman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c2"]] + }, + { + "_id": "62f324fe082fcc3049e91a90", + "username": "mricci", + "email": "mricci@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 953, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c5"]] + }, + { + "_id": "62f324fe082fcc3049e91a93", + "username": "Nicholi", + "email": "Nicholi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1063, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324fe082fcc3049e91a95", + "username": "Bikush", + "email": "Bikush@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 634, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c6"]] + }, + { + "_id": "62f324ff082fcc3049e91a96", + "username": "dkinzer", + "email": "dkinzer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30803, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c7"]] + }, + { + "_id": "62f324ff082fcc3049e91a98", + "username": "devildelta", + "email": "devildelta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ff082fcc3049e91a99", + "username": "Yang Dong", + "email": "Yang Dong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 452, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905c8"]] + }, + { + "_id": "62f324ff082fcc3049e91a9b", + "username": "Bennett McElwee", + "email": "Bennett McElwee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23525, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ff082fcc3049e91a9c", + "username": "Wab_Z", + "email": "Wab_Z@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905cb"]] + }, + { + "_id": "62f324ff082fcc3049e91a9d", + "username": "Mubashar", + "email": "Mubashar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905cc"]] + }, + { + "_id": "62f324ff082fcc3049e91a9f", + "username": "Kev", + "email": "Kev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4735, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905cd"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90878"] + ] + }, + { + "_id": "62f324ff082fcc3049e91aa1", + "username": "Adrian Hope-Bailie", + "email": "Adrian Hope-Bailie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2337, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ff082fcc3049e91aa3", + "username": "Abimael Martell", + "email": "Abimael Martell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f324ff082fcc3049e91aa4", + "username": "user2086641", + "email": "user2086641@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905ce"]] + }, + { + "_id": "62f324ff082fcc3049e91aa6", + "username": "DanV", + "email": "DanV@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3086, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32500082fcc3049e91aa9", + "username": "Sazid", + "email": "Sazid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2597, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d1"]] + }, + { + "_id": "62f32500082fcc3049e91aaa", + "username": "Alban Kaperi", + "email": "Alban Kaperi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 427, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d2"]] + }, + { + "_id": "62f32500082fcc3049e91aab", + "username": "Josef.B", + "email": "Josef.B@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 862, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d3"]] + }, + { + "_id": "62f32500082fcc3049e91aad", + "username": "Timothy Nwanwene", + "email": "Timothy Nwanwene@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 939, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d4"]] + }, + { + "_id": "62f32500082fcc3049e91aaf", + "username": "Agustí Sánchez", + "email": "Agustí Sánchez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9685, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d7"]] + }, + { + "_id": "62f32500082fcc3049e91ab1", + "username": "SvdSinner", + "email": "SvdSinner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 859, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32500082fcc3049e91ab5", + "username": "Praveen M P", + "email": "Praveen M P@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10766, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32500082fcc3049e91ab7", + "username": "Erich", + "email": "Erich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2114, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32500082fcc3049e91ab9", + "username": "Moshi", + "email": "Moshi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1288, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905d8"]] + }, + { + "_id": "62f32500082fcc3049e91abb", + "username": "sean", + "email": "sean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 867, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e2"]] + }, + { + "_id": "62f32501082fcc3049e91abd", + "username": "oviniciusfeitosa", + "email": "oviniciusfeitosa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 695, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905dc"]] + }, + { + "_id": "62f32501082fcc3049e91abe", + "username": "Imran Ahmad", + "email": "Imran Ahmad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2588, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905db"]] + }, + { + "_id": "62f32501082fcc3049e91ac0", + "username": "Japesh", + "email": "Japesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905dd"]] + }, + { + "_id": "62f32501082fcc3049e91ac2", + "username": "nerez", + "email": "nerez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 437, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32501082fcc3049e91ac3", + "username": "Abhishek Luthra", + "email": "Abhishek Luthra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905df"]] + }, + { + "_id": "62f32501082fcc3049e91ac4", + "username": "Davi Daniel Siepmann", + "email": "Davi Daniel Siepmann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 169, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905de"]] + }, + { + "_id": "62f32501082fcc3049e91ac6", + "username": "gfan", + "email": "gfan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32501082fcc3049e91ac7", + "username": "Ibraheem", + "email": "Ibraheem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1982, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e0"], + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90629"] + ] + }, + { + "_id": "62f32501082fcc3049e91ac8", + "username": "trn450", + "email": "trn450@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e1"]] + }, + { + "_id": "62f32501082fcc3049e91aca", + "username": "kip2", + "email": "kip2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5867, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32501082fcc3049e91acb", + "username": "Anastasios Tsournos", + "email": "Anastasios Tsournos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e3"]] + }, + { + "_id": "62f32501082fcc3049e91acd", + "username": "Hexodus", + "email": "Hexodus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11528, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32501082fcc3049e91acf", + "username": "Labham Jain", + "email": "Labham Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 132, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e4"]] + }, + { + "_id": "62f32502082fcc3049e91ad0", + "username": "CrazyStack", + "email": "CrazyStack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e5"]] + }, + { + "_id": "62f32502082fcc3049e91ad1", + "username": "Anis KCHAOU", + "email": "Anis KCHAOU@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 534, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e6"]] + }, + { + "_id": "62f32502082fcc3049e91ad2", + "username": "sMyles", + "email": "sMyles@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecc", "62f321c1082fcc3049e905e8"]] + }, + { + "_id": "62f32502082fcc3049e91ad4", + "username": "casademora", + "email": "casademora@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64761, + "questionIds": ["62f321bb082fcc3049e8fecc"], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ad6", + "username": "Kamlesh", + "email": "Kamlesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3973, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ad7", + "username": "totten", + "email": "totten@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2679, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ea"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3c"] + ] + }, + { + "_id": "62f32502082fcc3049e91ad9", + "username": "Shreedhar", + "email": "Shreedhar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5302, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ec"]] + }, + { + "_id": "62f32502082fcc3049e91adb", + "username": "Yeheshuah", + "email": "Yeheshuah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1055, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91add", + "username": "Manny Alvarado", + "email": "Manny Alvarado@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1105, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ade", + "username": "AceCorban", + "email": "AceCorban@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905eb"]] + }, + { + "_id": "62f32502082fcc3049e91ae0", + "username": "Satish Patro", + "email": "Satish Patro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2910, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ae2", + "username": "Sun", + "email": "Sun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ae4", + "username": "JokerMartini", + "email": "JokerMartini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5297, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ae6", + "username": "Nauraushaun", + "email": "Nauraushaun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1288, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ae8", + "username": "yuyicman", + "email": "yuyicman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91ae9", + "username": "JayDM", + "email": "JayDM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1146, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ed"]] + }, + { + "_id": "62f32502082fcc3049e91aeb", + "username": "Nando", + "email": "Nando@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 458, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32502082fcc3049e91aec", + "username": "A Kunin", + "email": "A Kunin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39858, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ee"]] + }, + { + "_id": "62f32503082fcc3049e91aef", + "username": "user207421", + "email": "user207421@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 299542, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91af0", + "username": "machineaddict", + "email": "machineaddict@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3158, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ef"]] + }, + { + "_id": "62f32503082fcc3049e91af2", + "username": "Ashutosh", + "email": "Ashutosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 980, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91af3", + "username": "Andrei", + "email": "Andrei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 862, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f0"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dcc"] + ] + }, + { + "_id": "62f32503082fcc3049e91af4", + "username": "vsvasya", + "email": "vsvasya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 647, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f1"]] + }, + { + "_id": "62f32503082fcc3049e91af6", + "username": "Antony Ng", + "email": "Antony Ng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 717, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91af8", + "username": "Nabi K.A.Z.", + "email": "Nabi K.A.Z.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8632, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9102a"]] + }, + { + "_id": "62f32503082fcc3049e91af9", + "username": "MarkG", + "email": "MarkG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7192, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f4"]] + }, + { + "_id": "62f32503082fcc3049e91afb", + "username": "Jason Foglia", + "email": "Jason Foglia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2202, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91afd", + "username": "coreyavis", + "email": "coreyavis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f2"]] + }, + { + "_id": "62f32503082fcc3049e91afe", + "username": "arielf", + "email": "arielf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 239, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f3"]] + }, + { + "_id": "62f32503082fcc3049e91b00", + "username": "Daniel De León", + "email": "Daniel De León@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12695, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f5"]] + }, + { + "_id": "62f32503082fcc3049e91b02", + "username": "Learner", + "email": "Learner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2189, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91b04", + "username": "Delight", + "email": "Delight@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 62, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32503082fcc3049e91b05", + "username": "Lavamantis", + "email": "Lavamantis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3586, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f6"]] + }, + { + "_id": "62f32503082fcc3049e91b07", + "username": "Scott Stafford", + "email": "Scott Stafford@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42167, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f8"]] + }, + { + "_id": "62f32503082fcc3049e91b09", + "username": "daviestar", + "email": "daviestar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4333, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dcd"]] + }, + { + "_id": "62f32503082fcc3049e91b0b", + "username": "astorije", + "email": "astorije@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2616, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f7"]] + }, + { + "_id": "62f32503082fcc3049e91b0d", + "username": "Jorge Sampayo", + "email": "Jorge Sampayo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 796, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b0e", + "username": "user3447070", + "email": "user3447070@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905fa"]] + }, + { + "_id": "62f32504082fcc3049e91b10", + "username": "user3711536", + "email": "user3711536@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905f9"]] + }, + { + "_id": "62f32504082fcc3049e91b11", + "username": "user", + "email": "user@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905fb"]] + }, + { + "_id": "62f32504082fcc3049e91b14", + "username": "Deele", + "email": "Deele@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3769, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905fc"]] + }, + { + "_id": "62f32504082fcc3049e91b16", + "username": "user993683", + "email": "user993683@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b18", + "username": "bgusach", + "email": "bgusach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13650, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b1b", + "username": "Eugene Mala", + "email": "Eugene Mala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 841, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b1d", + "username": "Adam Jagosz", + "email": "Adam Jagosz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1344, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b1e", + "username": "Harish.bazee", + "email": "Harish.bazee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 618, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905fd"]] + }, + { + "_id": "62f32504082fcc3049e91b21", + "username": "petermeissner", + "email": "petermeissner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11668, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905fe"]] + }, + { + "_id": "62f32504082fcc3049e91b23", + "username": "racribeiro", + "email": "racribeiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b25", + "username": "serge", + "email": "serge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12587, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32504082fcc3049e91b26", + "username": "Gourav Singla", + "email": "Gourav Singla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1656, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e905ff"]] + }, + { + "_id": "62f32504082fcc3049e91b28", + "username": "bigpotato", + "email": "bigpotato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24964, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90600"]] + }, + { + "_id": "62f32504082fcc3049e91b29", + "username": "Arne H. Bitubekk", + "email": "Arne H. Bitubekk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2845, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90601"]] + }, + { + "_id": "62f32505082fcc3049e91b2a", + "username": "Jérôme Beau", + "email": "Jérôme Beau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9633, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90604"]] + }, + { + "_id": "62f32505082fcc3049e91b2c", + "username": "Marcos Lima", + "email": "Marcos Lima@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 761, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90608"]] + }, + { + "_id": "62f32505082fcc3049e91b2d", + "username": "stanleyxu2005", + "email": "stanleyxu2005@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7981, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90605"]] + }, + { + "_id": "62f32505082fcc3049e91b2f", + "username": "Ritesh Dhuri", + "email": "Ritesh Dhuri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90603"]] + }, + { + "_id": "62f32505082fcc3049e91b31", + "username": "Marco Marsala", + "email": "Marco Marsala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2217, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b32", + "username": "loretoparisi", + "email": "loretoparisi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14713, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90606"]] + }, + { + "_id": "62f32505082fcc3049e91b34", + "username": "Joshua Pinter", + "email": "Joshua Pinter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42381, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b35", + "username": "Nimesh", + "email": "Nimesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3064, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90607"]] + }, + { + "_id": "62f32505082fcc3049e91b37", + "username": "AxelH", + "email": "AxelH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13918, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b39", + "username": "Lonnie Best", + "email": "Lonnie Best@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8504, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90723"], + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e23"] + ] + }, + { + "_id": "62f32505082fcc3049e91b3b", + "username": "Brendan.H", + "email": "Brendan.H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b3c", + "username": "RBZ", + "email": "RBZ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2014, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060c"]] + }, + { + "_id": "62f32505082fcc3049e91b3e", + "username": "AliN11", + "email": "AliN11@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2041, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b41", + "username": "Random Elephant", + "email": "Random Elephant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b44", + "username": "Andre Figueiredo", + "email": "Andre Figueiredo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12224, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32505082fcc3049e91b46", + "username": "cronvel", + "email": "cronvel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3907, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060a"]] + }, + { + "_id": "62f32506082fcc3049e91b48", + "username": "Suganth G", + "email": "Suganth G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5036, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060e"]] + }, + { + "_id": "62f32506082fcc3049e91b49", + "username": "Madura Pradeep", + "email": "Madura Pradeep@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060d"]] + }, + { + "_id": "62f32506082fcc3049e91b4b", + "username": "Adam A.", + "email": "Adam A.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 320, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9060f"]] + }, + { + "_id": "62f32506082fcc3049e91b4d", + "username": "Zambonilli", + "email": "Zambonilli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3958, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32506082fcc3049e91b4f", + "username": "pery mimon", + "email": "pery mimon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6800, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90610"]] + }, + { + "_id": "62f32506082fcc3049e91b51", + "username": "Elid Garazlic", + "email": "Elid Garazlic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90612"]] + }, + { + "_id": "62f32506082fcc3049e91b53", + "username": "Eagle", + "email": "Eagle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32506082fcc3049e91b54", + "username": "Nicolo", + "email": "Nicolo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1540, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90611"]] + }, + { + "_id": "62f32506082fcc3049e91b55", + "username": "Marco Barbero", + "email": "Marco Barbero@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90614"]] + }, + { + "_id": "62f32506082fcc3049e91b57", + "username": "Giacomo", + "email": "Giacomo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32506082fcc3049e91b59", + "username": "adamduren", + "email": "adamduren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3567, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32506082fcc3049e91b5c", + "username": "Zach Saucier", + "email": "Zach Saucier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23901, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32506082fcc3049e91b5e", + "username": "Matas Vaitkevicius", + "email": "Matas Vaitkevicius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54752, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90613"], + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c3"] + ] + }, + { + "_id": "62f32506082fcc3049e91b5f", + "username": "Simon Prickett", + "email": "Simon Prickett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2827, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90615"]] + }, + { + "_id": "62f32506082fcc3049e91b60", + "username": "Arulraj", + "email": "Arulraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 423, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90616"]] + }, + { + "_id": "62f32507082fcc3049e91b61", + "username": "OZZIE", + "email": "OZZIE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5610, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90618"]] + }, + { + "_id": "62f32507082fcc3049e91b62", + "username": "Vikasdeep Singh", + "email": "Vikasdeep Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19775, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90617"]] + }, + { + "_id": "62f32507082fcc3049e91b63", + "username": "Ross", + "email": "Ross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90619"]] + }, + { + "_id": "62f32507082fcc3049e91b64", + "username": "Denis S Dujota", + "email": "Denis S Dujota@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 479, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061a"]] + }, + { + "_id": "62f32507082fcc3049e91b66", + "username": "Dmytro Medvid", + "email": "Dmytro Medvid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4648, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061c"]] + }, + { + "_id": "62f32507082fcc3049e91b68", + "username": "Pavithran Ravichandiran", + "email": "Pavithran Ravichandiran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1461, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061b"]] + }, + { + "_id": "62f32507082fcc3049e91b6a", + "username": "hanshenrik", + "email": "hanshenrik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17832, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32507082fcc3049e91b6b", + "username": "aleha", + "email": "aleha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7914, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061d"]] + }, + { + "_id": "62f32507082fcc3049e91b6c", + "username": "thecoolestname36", + "email": "thecoolestname36@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061e"]] + }, + { + "_id": "62f32507082fcc3049e91b6e", + "username": "Wiimm", + "email": "Wiimm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2572, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90620"]] + }, + { + "_id": "62f32507082fcc3049e91b6f", + "username": "Fellow Stranger", + "email": "Fellow Stranger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29501, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9061f"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd0"] + ] + }, + { + "_id": "62f32508082fcc3049e91b71", + "username": "Carlos Lombardi", + "email": "Carlos Lombardi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 349, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90621"]] + }, + { + "_id": "62f32508082fcc3049e91b72", + "username": "malamut", + "email": "malamut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90622"]] + }, + { + "_id": "62f32508082fcc3049e91b75", + "username": "General Grievance", + "email": "General Grievance@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4291, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32508082fcc3049e91b76", + "username": "caliche2000", + "email": "caliche2000@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 66, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90624"]] + }, + { + "_id": "62f32508082fcc3049e91b78", + "username": "Sergii", + "email": "Sergii@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 347, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32508082fcc3049e91b7a", + "username": "Amr Ali", + "email": "Amr Ali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2282, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32508082fcc3049e91b7e", + "username": "KFish", + "email": "KFish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 390, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90623"]] + }, + { + "_id": "62f32508082fcc3049e91b7f", + "username": "profimedica", + "email": "profimedica@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90626"]] + }, + { + "_id": "62f32508082fcc3049e91b81", + "username": "Rob Evans", + "email": "Rob Evans@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6524, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90625"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90647"] + ] + }, + { + "_id": "62f32508082fcc3049e91b82", + "username": "Salman", + "email": "Salman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 542, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90627"]] + }, + { + "_id": "62f32508082fcc3049e91b84", + "username": "chriscrossweb", + "email": "chriscrossweb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 328, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90628"]] + }, + { + "_id": "62f32508082fcc3049e91b85", + "username": "Ajsti.pl - Maciej Szewczyk", + "email": "Ajsti.pl - Maciej Szewczyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062a"]] + }, + { + "_id": "62f32509082fcc3049e91b86", + "username": "Kiran Maniya", + "email": "Kiran Maniya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7459, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062b"], + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b1"] + ] + }, + { + "_id": "62f32509082fcc3049e91b87", + "username": "ramya", + "email": "ramya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062e"]] + }, + { + "_id": "62f32509082fcc3049e91b89", + "username": "Neeraj", + "email": "Neeraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d54"]] + }, + { + "_id": "62f32509082fcc3049e91b8b", + "username": "Marcin Wanago", + "email": "Marcin Wanago@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90630"]] + }, + { + "_id": "62f32509082fcc3049e91b8d", + "username": "Kevin Jhangiani", + "email": "Kevin Jhangiani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1577, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32509082fcc3049e91b90", + "username": "Mosè Bottacini", + "email": "Mosè Bottacini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3568, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9062f"]] + }, + { + "_id": "62f32509082fcc3049e91b91", + "username": "Mohsen Alyafei", + "email": "Mohsen Alyafei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3905, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90632"], + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067b"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f9"] + ] + }, + { + "_id": "62f32509082fcc3049e91b92", + "username": "Alexandre ELIOT", + "email": "Alexandre ELIOT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 136, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90633"]] + }, + { + "_id": "62f32509082fcc3049e91b95", + "username": "jerry", + "email": "jerry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90631"]] + }, + { + "_id": "62f32509082fcc3049e91b98", + "username": "Nathan Wailes", + "email": "Nathan Wailes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8068, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32509082fcc3049e91b9b", + "username": "superkeci", + "email": "superkeci@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 141, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90634"]] + }, + { + "_id": "62f3250a082fcc3049e91b9c", + "username": "Butsaty", + "email": "Butsaty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1663, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90635"]] + }, + { + "_id": "62f3250a082fcc3049e91b9e", + "username": "Marek Marczak", + "email": "Marek Marczak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 507, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250a082fcc3049e91b9f", + "username": "Arifur Rahman Khan", + "email": "Arifur Rahman Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90639"]] + }, + { + "_id": "62f3250a082fcc3049e91ba0", + "username": "shahroz butt", + "email": "shahroz butt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 168, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90637"]] + }, + { + "_id": "62f3250a082fcc3049e91ba1", + "username": "Deejers", + "email": "Deejers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2827, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e90638"]] + }, + { + "_id": "62f3250a082fcc3049e91ba3", + "username": "Royi Namir", + "email": "Royi Namir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139751, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250a082fcc3049e91ba4", + "username": "Victor dlf", + "email": "Victor dlf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9063a"]] + }, + { + "_id": "62f3250a082fcc3049e91ba5", + "username": "Cybernetic", + "email": "Cybernetic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11478, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9063b"]] + }, + { + "_id": "62f3250a082fcc3049e91ba6", + "username": "S M Samnoon Abrar", + "email": "S M Samnoon Abrar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 535, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9063c"]] + }, + { + "_id": "62f3250a082fcc3049e91ba8", + "username": "iPadDeveloper2011", + "email": "iPadDeveloper2011@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4432, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250a082fcc3049e91bab", + "username": "Dan P.", + "email": "Dan P.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1649, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250a082fcc3049e91bac", + "username": "Calvin", + "email": "Calvin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9063e"]] + }, + { + "_id": "62f3250a082fcc3049e91bad", + "username": "Nahuel Ramos", + "email": "Nahuel Ramos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecb", "62f321c1082fcc3049e9063d"]] + }, + { + "_id": "62f3250a082fcc3049e91bae", + "username": "stinkycheeseman", + "email": "stinkycheeseman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39967, + "questionIds": ["62f321bb082fcc3049e8fecb"], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91baf", + "username": "Dan Lew", + "email": "Dan Lew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 84182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90640"]] + }, + { + "_id": "62f3250b082fcc3049e91bb1", + "username": "james_womack", + "email": "james_womack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9810, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bb3", + "username": "GetFree", + "email": "GetFree@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37384, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bb5", + "username": "user956584", + "email": "user956584@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4953, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ce"]] + }, + { + "_id": "62f3250b082fcc3049e91bb7", + "username": "Q10Viking", + "email": "Q10Viking@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 806, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bb8", + "username": "picardo", + "email": "picardo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9063f"]] + }, + { + "_id": "62f3250b082fcc3049e91bba", + "username": "the_drow", + "email": "the_drow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17965, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bbc", + "username": "abarnert", + "email": "abarnert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 338212, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bbd", + "username": "Kris Walker", + "email": "Kris Walker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 897, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90642"]] + }, + { + "_id": "62f3250b082fcc3049e91bbe", + "username": "Pascal", + "email": "Pascal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8729, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90643"]] + }, + { + "_id": "62f3250b082fcc3049e91bc0", + "username": "Jan Turoň", + "email": "Jan Turoň@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29386, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90644"]] + }, + { + "_id": "62f3250b082fcc3049e91bc1", + "username": "Andy Burke", + "email": "Andy Burke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90646"]] + }, + { + "_id": "62f3250b082fcc3049e91bc2", + "username": "itpastorn", + "email": "itpastorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2907, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90645"]] + }, + { + "_id": "62f3250b082fcc3049e91bc4", + "username": "René Nyffenegger", + "email": "René Nyffenegger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bc6", + "username": "Ian Lunn", + "email": "Ian Lunn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1313, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250b082fcc3049e91bc8", + "username": "d13", + "email": "d13@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9347, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90656"]] + }, + { + "_id": "62f3250c082fcc3049e91bca", + "username": "dewd", + "email": "dewd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4349, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250c082fcc3049e91bcc", + "username": "Diego Favero", + "email": "Diego Favero@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1813, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250c082fcc3049e91bce", + "username": "Turbo", + "email": "Turbo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 392, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250c082fcc3049e91bcf", + "username": "dule", + "email": "dule@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17340, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064a"]] + }, + { + "_id": "62f3250c082fcc3049e91bd0", + "username": "Bert Regelink", + "email": "Bert Regelink@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2656, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064c"]] + }, + { + "_id": "62f3250c082fcc3049e91bd1", + "username": "Alec", + "email": "Alec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064e"]] + }, + { + "_id": "62f3250c082fcc3049e91bd3", + "username": "Dan Ross", + "email": "Dan Ross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3506, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250c082fcc3049e91bd5", + "username": "garbanzio", + "email": "garbanzio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 826, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3250c082fcc3049e91bd6", + "username": "VaZaA", + "email": "VaZaA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9064d"]] + }, + { + "_id": "62f3250c082fcc3049e91bd8", + "username": "MikeM", + "email": "MikeM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12373, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91bdb", + "username": "Shuaib", + "email": "Shuaib@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 717, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90653"]] + }, + { + "_id": "62f32549082fcc3049e91bdd", + "username": "l00k", + "email": "l00k@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1485, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91bde", + "username": "Qohelet", + "email": "Qohelet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1310, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90654"]] + }, + { + "_id": "62f32549082fcc3049e91bdf", + "username": "basis", + "email": "basis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 148, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90655"]] + }, + { + "_id": "62f32549082fcc3049e91be0", + "username": "James Koss", + "email": "James Koss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 523, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90657"]] + }, + { + "_id": "62f32549082fcc3049e91be2", + "username": "John Sonderson", + "email": "John Sonderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3118, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90659"]] + }, + { + "_id": "62f32549082fcc3049e91be4", + "username": "zamnuts", + "email": "zamnuts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9333, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91be5", + "username": "Lukas Jelinek", + "email": "Lukas Jelinek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2217, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90658"]] + }, + { + "_id": "62f32549082fcc3049e91be7", + "username": "Sajuuk", + "email": "Sajuuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2237, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91be8", + "username": "yazjisuhail", + "email": "yazjisuhail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 357, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065a"]] + }, + { + "_id": "62f32549082fcc3049e91bea", + "username": "Matt H", + "email": "Matt H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6350, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91bec", + "username": "1-14x0r", + "email": "1-14x0r@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1677, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32549082fcc3049e91bed", + "username": "Mohammed Akdim", + "email": "Mohammed Akdim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1955, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065c"]] + }, + { + "_id": "62f3254a082fcc3049e91bef", + "username": "Marcus Junius Brutus", + "email": "Marcus Junius Brutus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24814, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bf1", + "username": "WinEunuuchs2Unix", + "email": "WinEunuuchs2Unix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1552, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bf4", + "username": "Bugs Bunny", + "email": "Bugs Bunny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2357, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bf6", + "username": "musemind", + "email": "musemind@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 967, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9065e"]] + }, + { + "_id": "62f3254a082fcc3049e91bf8", + "username": "Tosh", + "email": "Tosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1741, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bf9", + "username": "Tareq", + "email": "Tareq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5125, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90662"]] + }, + { + "_id": "62f3254a082fcc3049e91bfa", + "username": "Erich Horn", + "email": "Erich Horn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90664"]] + }, + { + "_id": "62f3254a082fcc3049e91bfc", + "username": "Petr Marek", + "email": "Petr Marek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1351, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bfe", + "username": "manikant gautam", + "email": "manikant gautam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3363, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91bff", + "username": "Charles Merriam", + "email": "Charles Merriam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18876, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90663"]] + }, + { + "_id": "62f3254a082fcc3049e91c01", + "username": "August", + "email": "August@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1610, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91c03", + "username": "HoldOffHunger", + "email": "HoldOffHunger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16104, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066e"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90777"], + ["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac8"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91045"] + ] + }, + { + "_id": "62f3254a082fcc3049e91c04", + "username": "João Oliveira", + "email": "João Oliveira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 477, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90665"]] + }, + { + "_id": "62f3254a082fcc3049e91c06", + "username": "ceztko", + "email": "ceztko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14243, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254a082fcc3049e91c08", + "username": "flori", + "email": "flori@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12585, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90666"]] + }, + { + "_id": "62f3254b082fcc3049e91c0a", + "username": "Naomi", + "email": "Naomi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 718, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254b082fcc3049e91c0b", + "username": "Marco", + "email": "Marco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2591, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90667"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ef2"] + ] + }, + { + "_id": "62f3254b082fcc3049e91c0d", + "username": "user1063287", + "email": "user1063287@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9623, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254b082fcc3049e91c0f", + "username": "Bogdan D", + "email": "Bogdan D@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4991, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254b082fcc3049e91c11", + "username": "Womble", + "email": "Womble@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 316, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254b082fcc3049e91c12", + "username": "knowingpark", + "email": "knowingpark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 571, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90669"]] + }, + { + "_id": "62f3254b082fcc3049e91c13", + "username": "Edward Brey", + "email": "Edward Brey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38538, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066c"]] + }, + { + "_id": "62f3254b082fcc3049e91c14", + "username": "j rdl", + "email": "j rdl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066b"]] + }, + { + "_id": "62f3254b082fcc3049e91c18", + "username": "Pavan Garre", + "email": "Pavan Garre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2531, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9066f"]] + }, + { + "_id": "62f3254b082fcc3049e91c19", + "username": "webpreneur", + "email": "webpreneur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 706, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90670"]] + }, + { + "_id": "62f3254c082fcc3049e91c1a", + "username": "aberaud", + "email": "aberaud@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 770, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90672"]] + }, + { + "_id": "62f3254c082fcc3049e91c1c", + "username": "ConductedClever", + "email": "ConductedClever@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3975, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90671"]] + }, + { + "_id": "62f3254c082fcc3049e91c1e", + "username": "Misha", + "email": "Misha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5100, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254c082fcc3049e91c21", + "username": "Aleks ", + "email": "Aleks @fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 679, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254c082fcc3049e91c22", + "username": "Nishant Dwivedi", + "email": "Nishant Dwivedi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 380, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90673"]] + }, + { + "_id": "62f3254c082fcc3049e91c24", + "username": "Exlord", + "email": "Exlord@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4703, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254c082fcc3049e91c25", + "username": "Pouria Moosavi", + "email": "Pouria Moosavi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 632, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90674"]] + }, + { + "_id": "62f3254c082fcc3049e91c26", + "username": "Ashok R", + "email": "Ashok R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18568, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90676"]] + }, + { + "_id": "62f3254c082fcc3049e91c28", + "username": "MrSegFaulty", + "email": "MrSegFaulty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254c082fcc3049e91c29", + "username": "GANESH CHOKHARE", + "email": "GANESH CHOKHARE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 186, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90677"]] + }, + { + "_id": "62f3254c082fcc3049e91c2a", + "username": "Fouad Boukredine", + "email": "Fouad Boukredine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1315, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90678"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd3"] + ] + }, + { + "_id": "62f3254c082fcc3049e91c2b", + "username": "Louis Christopher", + "email": "Louis Christopher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90675"]] + }, + { + "_id": "62f3254c082fcc3049e91c2c", + "username": "Raskolnikov", + "email": "Raskolnikov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90679"]] + }, + { + "_id": "62f3254c082fcc3049e91c2d", + "username": "asoni94", + "email": "asoni94@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067a"]] + }, + { + "_id": "62f3254d082fcc3049e91c31", + "username": "Tchakabam", + "email": "Tchakabam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 471, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254d082fcc3049e91c32", + "username": "rela589n", + "email": "rela589n@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 557, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067c"]] + }, + { + "_id": "62f3254d082fcc3049e91c35", + "username": "Eduardo Mior", + "email": "Eduardo Mior@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254d082fcc3049e91c36", + "username": "Dere Sagar", + "email": "Dere Sagar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067d"]] + }, + { + "_id": "62f3254d082fcc3049e91c37", + "username": "Vivek sharma", + "email": "Vivek sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067e"]] + }, + { + "_id": "62f3254d082fcc3049e91c38", + "username": "Radim Šafrán", + "email": "Radim Šafrán@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 195, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e9067f"]] + }, + { + "_id": "62f3254d082fcc3049e91c39", + "username": "FAHAD SIDDIQUI", + "email": "FAHAD SIDDIQUI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 621, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90680"]] + }, + { + "_id": "62f3254d082fcc3049e91c3a", + "username": "barhatsor", + "email": "barhatsor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1705, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90682"]] + }, + { + "_id": "62f3254d082fcc3049e91c3b", + "username": "uingtea", + "email": "uingtea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90684"]] + }, + { + "_id": "62f3254e082fcc3049e91c3d", + "username": "angry kiwi", + "email": "angry kiwi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9922, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c40", + "username": "iamcastelli", + "email": "iamcastelli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1293, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c41", + "username": "notnoop", + "email": "notnoop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57957, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90688"]] + }, + { + "_id": "62f3254e082fcc3049e91c42", + "username": "Pulkit Chaudhri", + "email": "Pulkit Chaudhri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90685"]] + }, + { + "_id": "62f3254e082fcc3049e91c44", + "username": "devios1", + "email": "devios1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35693, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c46", + "username": "Kevin Schroeder", + "email": "Kevin Schroeder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1296, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c49", + "username": "Álvaro González", + "email": "Álvaro González@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 136488, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c4a", + "username": "flatline", + "email": "flatline@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90689"]] + }, + { + "_id": "62f3254e082fcc3049e91c4b", + "username": "user669677", + "email": "user669677@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068c"], + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb8"] + ] + }, + { + "_id": "62f3254e082fcc3049e91c4d", + "username": "Eric Hodonsky", + "email": "Eric Hodonsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5046, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c4f", + "username": "Vincent McNabb", + "email": "Vincent McNabb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31329, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c51", + "username": "Gary", + "email": "Gary@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2147, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254e082fcc3049e91c52", + "username": "kmatheny", + "email": "kmatheny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3962, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068b"]] + }, + { + "_id": "62f3254e082fcc3049e91c53", + "username": "Praveen D", + "email": "Praveen D@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2318, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9068e"]] + }, + { + "_id": "62f3254e082fcc3049e91c54", + "username": "Mark Karwowski", + "email": "Mark Karwowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 619, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90690"]] + }, + { + "_id": "62f3254f082fcc3049e91c56", + "username": "jhliberty", + "email": "jhliberty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254f082fcc3049e91c57", + "username": "Dhana Krishnasamy", + "email": "Dhana Krishnasamy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2106, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90693"]] + }, + { + "_id": "62f3254f082fcc3049e91c58", + "username": "tjacks3", + "email": "tjacks3@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 597, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90691"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd2"] + ] + }, + { + "_id": "62f3254f082fcc3049e91c59", + "username": "Rakesh Kumar", + "email": "Rakesh Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2678, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90692"]] + }, + { + "_id": "62f3254f082fcc3049e91c5a", + "username": "Mahesh", + "email": "Mahesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90694"]] + }, + { + "_id": "62f3254f082fcc3049e91c5b", + "username": "Raghavendra", + "email": "Raghavendra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90696"]] + }, + { + "_id": "62f3254f082fcc3049e91c5d", + "username": "Kyll", + "email": "Kyll@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7042, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3254f082fcc3049e91c5e", + "username": "venkat7668", + "email": "venkat7668@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2507, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90695"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3b"] + ] + }, + { + "_id": "62f3254f082fcc3049e91c5f", + "username": "Sanjib Debnath", + "email": "Sanjib Debnath@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3228, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e90698"]] + }, + { + "_id": "62f32550082fcc3049e91c60", + "username": "Pravin Divraniya", + "email": "Pravin Divraniya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4025, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9069b"]] + }, + { + "_id": "62f32550082fcc3049e91c61", + "username": "Abdul Rehman Kaim Khani", + "email": "Abdul Rehman Kaim Khani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 936, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9069c"]] + }, + { + "_id": "62f32550082fcc3049e91c62", + "username": "unsuredev", + "email": "unsuredev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed0", "62f321c2082fcc3049e9069d"]] + }, + { + "_id": "62f32550082fcc3049e91c63", + "username": "John Duff", + "email": "John Duff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37090, + "questionIds": ["62f321bb082fcc3049e8fed0"], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c65", + "username": "Vatsal", + "email": "Vatsal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1928, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0c"]] + }, + { + "_id": "62f32550082fcc3049e91c66", + "username": "Bryan", + "email": "Bryan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3411, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906aa"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba5"] + ] + }, + { + "_id": "62f32550082fcc3049e91c69", + "username": "Sk8erPeter", + "email": "Sk8erPeter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6733, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c6a", + "username": "Richard Levasseur", + "email": "Richard Levasseur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906a9"]] + }, + { + "_id": "62f32550082fcc3049e91c6d", + "username": "ERROR 401", + "email": "ERROR 401@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 129, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c6f", + "username": "mercury", + "email": "mercury@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1828, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c71", + "username": "Ariful Islam", + "email": "Ariful Islam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 575, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c73", + "username": "Irvan Hilmi", + "email": "Irvan Hilmi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 334, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c75", + "username": "codrelphi", + "email": "codrelphi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1076, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32550082fcc3049e91c77", + "username": "Francis Lewis", + "email": "Francis Lewis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8672, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ae"]] + }, + { + "_id": "62f32550082fcc3049e91c78", + "username": "Axel Rauschmayer", + "email": "Axel Rauschmayer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24421, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ad"]] + }, + { + "_id": "62f32551082fcc3049e91c7a", + "username": "Ravi Ram", + "email": "Ravi Ram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23506, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32551082fcc3049e91c7c", + "username": "ppasler", + "email": "ppasler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3366, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32551082fcc3049e91c7e", + "username": "Sebastian", + "email": "Sebastian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2824, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32551082fcc3049e91c7f", + "username": "B.F.", + "email": "B.F.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 449, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b3"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101c"] + ] + }, + { + "_id": "62f32551082fcc3049e91c80", + "username": "ParaMeterz", + "email": "ParaMeterz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9047, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906af"]] + }, + { + "_id": "62f32551082fcc3049e91c81", + "username": "strider", + "email": "strider@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5224, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b0"]] + }, + { + "_id": "62f32551082fcc3049e91c83", + "username": "Pencroff", + "email": "Pencroff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 999, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b2"]] + }, + { + "_id": "62f32551082fcc3049e91c84", + "username": "mohamed-ibrahim", + "email": "mohamed-ibrahim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b4"]] + }, + { + "_id": "62f32551082fcc3049e91c85", + "username": "Lewis", + "email": "Lewis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13244, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b6"]] + }, + { + "_id": "62f32551082fcc3049e91c86", + "username": "Dheeraj Vepakomma", + "email": "Dheeraj Vepakomma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20191, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b7"]] + }, + { + "_id": "62f32551082fcc3049e91c88", + "username": "Marius Bakowski", + "email": "Marius Bakowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 115, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b8"]] + }, + { + "_id": "62f32552082fcc3049e91c8a", + "username": "abalter", + "email": "abalter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8765, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32552082fcc3049e91c8e", + "username": "Tjorriemorrie", + "email": "Tjorriemorrie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15984, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32552082fcc3049e91c8f", + "username": "Nicolas Bouvrette", + "email": "Nicolas Bouvrette@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3357, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906b9"]] + }, + { + "_id": "62f32552082fcc3049e91c90", + "username": "FieryCod", + "email": "FieryCod@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1556, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906bb"]] + }, + { + "_id": "62f32552082fcc3049e91c91", + "username": "Tadas V.", + "email": "Tadas V.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ba"]] + }, + { + "_id": "62f32552082fcc3049e91c93", + "username": "Pika", + "email": "Pika@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 497, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32552082fcc3049e91c94", + "username": "Artyom Pranovich", + "email": "Artyom Pranovich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6694, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906bc"]] + }, + { + "_id": "62f32552082fcc3049e91c95", + "username": "Jaime Rios", + "email": "Jaime Rios@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1728, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906bf"]] + }, + { + "_id": "62f32552082fcc3049e91c96", + "username": "Biber", + "email": "Biber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 688, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906be"]] + }, + { + "_id": "62f32552082fcc3049e91c98", + "username": "ooo", + "email": "ooo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32552082fcc3049e91c99", + "username": "Bamieh", + "email": "Bamieh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9577, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906bd"]] + }, + { + "_id": "62f32552082fcc3049e91c9a", + "username": "Muhammad Usman", + "email": "Muhammad Usman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9811, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c1"]] + }, + { + "_id": "62f32552082fcc3049e91c9b", + "username": "Dan Alboteanu", + "email": "Dan Alboteanu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8258, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c0"]] + }, + { + "_id": "62f32552082fcc3049e91c9d", + "username": "Rolf", + "email": "Rolf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5342, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32552082fcc3049e91c9e", + "username": "Harsh Patel", + "email": "Harsh Patel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5643, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c2"]] + }, + { + "_id": "62f32553082fcc3049e91c9f", + "username": "Zectbumo", + "email": "Zectbumo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3430, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c4"]] + }, + { + "_id": "62f32553082fcc3049e91ca1", + "username": "Sean Lindo", + "email": "Sean Lindo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1327, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32553082fcc3049e91ca2", + "username": "yehonatan yehezkel", + "email": "yehonatan yehezkel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 938, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c6"]] + }, + { + "_id": "62f32553082fcc3049e91ca4", + "username": "Harsh Phoujdar", + "email": "Harsh Phoujdar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 547, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32553082fcc3049e91ca5", + "username": "Ankit", + "email": "Ankit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 942, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c7"]] + }, + { + "_id": "62f32553082fcc3049e91ca6", + "username": "senthil", + "email": "senthil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c5"]] + }, + { + "_id": "62f32553082fcc3049e91ca8", + "username": "nrb", + "email": "nrb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c8"]] + }, + { + "_id": "62f32553082fcc3049e91caa", + "username": "PythonProgrammi", + "email": "PythonProgrammi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20743, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906c9"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4b"] + ] + }, + { + "_id": "62f32553082fcc3049e91cab", + "username": "Nicolas Cabanas", + "email": "Nicolas Cabanas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 183, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906cb"]] + }, + { + "_id": "62f32553082fcc3049e91cad", + "username": "Swap-IOS-Android", + "email": "Swap-IOS-Android@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4313, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32553082fcc3049e91caf", + "username": "Onera", + "email": "Onera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ca"]] + }, + { + "_id": "62f32554082fcc3049e91cb1", + "username": "Nivethan", + "email": "Nivethan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 756, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32554082fcc3049e91cb2", + "username": "Akhil Ramani", + "email": "Akhil Ramani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906ce"]] + }, + { + "_id": "62f32554082fcc3049e91cb3", + "username": "lodey", + "email": "lodey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906cf"]] + }, + { + "_id": "62f32554082fcc3049e91cb4", + "username": "Isaac Jandalala", + "email": "Isaac Jandalala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 88, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906d2"]] + }, + { + "_id": "62f32554082fcc3049e91cb5", + "username": "kerelape", + "email": "kerelape@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 98, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecf", "62f321c2082fcc3049e906d3"]] + }, + { + "_id": "62f32554082fcc3049e91cb6", + "username": "Tanmoy", + "email": "Tanmoy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42438, + "questionIds": ["62f321bb082fcc3049e8fecf"], + "answerIds": [] + }, + { + "_id": "62f32554082fcc3049e91cb8", + "username": "Tim Down", + "email": "Tim Down@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 308441, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d6"], + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90975"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3b"] + ] + }, + { + "_id": "62f32591082fcc3049e91cb9", + "username": "John Wundes", + "email": "John Wundes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d7"]] + }, + { + "_id": "62f32591082fcc3049e91cba", + "username": "janr", + "email": "janr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3634, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d8"]] + }, + { + "_id": "62f32591082fcc3049e91cbb", + "username": "Billy Moon", + "email": "Billy Moon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55143, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906da"]] + }, + { + "_id": "62f32591082fcc3049e91cbd", + "username": "Michał Perłakowski", + "email": "Michał Perłakowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 82239, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32591082fcc3049e91cc0", + "username": "John Henckel", + "email": "John Henckel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9023, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32591082fcc3049e91cc1", + "username": "MidnightTortoise", + "email": "MidnightTortoise@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 472, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906db"]] + }, + { + "_id": "62f32591082fcc3049e91cc2", + "username": "ajax333221", + "email": "ajax333221@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11156, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906dc"]] + }, + { + "_id": "62f32591082fcc3049e91cc3", + "username": "CruorVult", + "email": "CruorVult@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 803, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906dd"]] + }, + { + "_id": "62f32591082fcc3049e91cc5", + "username": "test30", + "email": "test30@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3210, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32591082fcc3049e91cc8", + "username": "namuol", + "email": "namuol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9516, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906de"]] + }, + { + "_id": "62f32591082fcc3049e91ccb", + "username": "Roy Tinker", + "email": "Roy Tinker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10046, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32591082fcc3049e91ccd", + "username": "Ahmet DAL", + "email": "Ahmet DAL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4265, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906df"]] + }, + { + "_id": "62f32591082fcc3049e91ccf", + "username": "the system", + "email": "the system@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9004, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32591082fcc3049e91cd1", + "username": "Marcus", + "email": "Marcus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1212, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e0"]] + }, + { + "_id": "62f32592082fcc3049e91cd3", + "username": "Safareli", + "email": "Safareli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 702, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e1"]] + }, + { + "_id": "62f32592082fcc3049e91cd5", + "username": "dmi3y", + "email": "dmi3y@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3342, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32592082fcc3049e91cd6", + "username": "exebook", + "email": "exebook@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30080, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e2"]] + }, + { + "_id": "62f32592082fcc3049e91cd7", + "username": "RoboTamer", + "email": "RoboTamer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3387, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e3"]] + }, + { + "_id": "62f32592082fcc3049e91cd8", + "username": "Brad Parks", + "email": "Brad Parks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61115, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e6"]] + }, + { + "_id": "62f32592082fcc3049e91cd9", + "username": "Fela", + "email": "Fela@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25760, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e5"]] + }, + { + "_id": "62f32592082fcc3049e91cdc", + "username": "rsbkk", + "email": "rsbkk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 235, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e8"]] + }, + { + "_id": "62f32592082fcc3049e91cde", + "username": "TechTurtle", + "email": "TechTurtle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1975, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32592082fcc3049e91cdf", + "username": "Dexygen", + "email": "Dexygen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12073, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e7"], + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0f"] + ] + }, + { + "_id": "62f32592082fcc3049e91ce1", + "username": "László Kardinál", + "email": "László Kardinál@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 306, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32592082fcc3049e91ce2", + "username": "Sensei_Shoh", + "email": "Sensei_Shoh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 619, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906e9"]] + }, + { + "_id": "62f32592082fcc3049e91ce3", + "username": "user3367643", + "email": "user3367643@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ea"]] + }, + { + "_id": "62f32593082fcc3049e91ce7", + "username": "le_top", + "email": "le_top@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 335, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ec"]] + }, + { + "_id": "62f32593082fcc3049e91ce8", + "username": "VIJAY P", + "email": "VIJAY P@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ee"]] + }, + { + "_id": "62f32593082fcc3049e91ce9", + "username": "shinobi", + "email": "shinobi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2411, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ed"]] + }, + { + "_id": "62f32593082fcc3049e91ceb", + "username": "yesil", + "email": "yesil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 697, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f0"]] + }, + { + "_id": "62f32593082fcc3049e91ced", + "username": "Kermit_ice_tea", + "email": "Kermit_ice_tea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 468, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32593082fcc3049e91cee", + "username": "Sheelpriy", + "email": "Sheelpriy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1645, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f3"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a50"] + ] + }, + { + "_id": "62f32594082fcc3049e91cef", + "username": "kolyaseg", + "email": "kolyaseg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f6"]] + }, + { + "_id": "62f32594082fcc3049e91cf0", + "username": "Vikash Kumar", + "email": "Vikash Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1702, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f5"]] + }, + { + "_id": "62f32594082fcc3049e91cf1", + "username": "Ric Flair", + "email": "Ric Flair@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f8"]] + }, + { + "_id": "62f32594082fcc3049e91cf4", + "username": "STEEL", + "email": "STEEL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7378, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f7"]] + }, + { + "_id": "62f32594082fcc3049e91cf7", + "username": "Partha Sarathi Nanda", + "email": "Partha Sarathi Nanda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906f9"]] + }, + { + "_id": "62f32594082fcc3049e91cfd", + "username": "underthecode", + "email": "underthecode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906fc"]] + }, + { + "_id": "62f32594082fcc3049e91d02", + "username": "Luis felipe De jesus Munoz", + "email": "Luis felipe De jesus Munoz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6972, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906fe"]] + }, + { + "_id": "62f32594082fcc3049e91d03", + "username": "Atishay Jain", + "email": "Atishay Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906fd"]] + }, + { + "_id": "62f32595082fcc3049e91d05", + "username": "giraffesyo", + "email": "giraffesyo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4384, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32595082fcc3049e91d06", + "username": "Alauddin Afif Cassandra", + "email": "Alauddin Afif Cassandra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 387, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90702"]] + }, + { + "_id": "62f32595082fcc3049e91d07", + "username": "Robby_rob", + "email": "Robby_rob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 190, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90701"]] + }, + { + "_id": "62f32595082fcc3049e91d09", + "username": "3xCh1_23", + "email": "3xCh1_23@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1433, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d0b", + "username": "Paul Sweatte", + "email": "Paul Sweatte@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23647, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b64"], + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075d"] + ] + }, + { + "_id": "62f32596082fcc3049e91d0d", + "username": "staticsan", + "email": "staticsan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90828"]] + }, + { + "_id": "62f32596082fcc3049e91d11", + "username": "Zane", + "email": "Zane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 916, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d13", + "username": "IcedDante", + "email": "IcedDante@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5835, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d15", + "username": "user1493948", + "email": "user1493948@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d17", + "username": "Tom Andraszek", + "email": "Tom Andraszek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1606, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d19", + "username": "Rishabh Agrahari", + "email": "Rishabh Agrahari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3211, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d1b", + "username": "César León", + "email": "César León@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2860, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d1d", + "username": "Iharob Al Asimi", + "email": "Iharob Al Asimi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 52146, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d1f", + "username": "RayLoveless", + "email": "RayLoveless@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18204, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d21", + "username": "Anonymous", + "email": "Anonymous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47093, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90707"]] + }, + { + "_id": "62f32596082fcc3049e91d23", + "username": "BMiner", + "email": "BMiner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15950, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d25", + "username": "KooiInc", + "email": "KooiInc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 113461, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90708"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb7"] + ] + }, + { + "_id": "62f32596082fcc3049e91d27", + "username": "RobKohr", + "email": "RobKohr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6236, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090d"]] + }, + { + "_id": "62f32596082fcc3049e91d29", + "username": "Lodewijk", + "email": "Lodewijk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3431, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d2d", + "username": "Pavel", + "email": "Pavel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5042, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d30", + "username": "Michael Mior", + "email": "Michael Mior@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27301, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d32", + "username": "user2418182", + "email": "user2418182@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d34", + "username": "nevelis", + "email": "nevelis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 736, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d36", + "username": "Florian Wendelborn", + "email": "Florian Wendelborn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1523, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d38", + "username": "Wolfie Inu", + "email": "Wolfie Inu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d3a", + "username": "AturSams", + "email": "AturSams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7179, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d3f", + "username": "Seblor", + "email": "Seblor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6568, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d41", + "username": "bijayshrestha", + "email": "bijayshrestha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906ff"]] + }, + { + "_id": "62f32596082fcc3049e91d43", + "username": "Souvik Dey", + "email": "Souvik Dey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e90700"]] + }, + { + "_id": "62f32596082fcc3049e91d47", + "username": "dotancohen", + "email": "dotancohen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28178, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d4a", + "username": "borracciaBlu", + "email": "borracciaBlu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3881, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d4b", + "username": "stillatmycomputer", + "email": "stillatmycomputer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 175, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90709"]] + }, + { + "_id": "62f32596082fcc3049e91d4d", + "username": "fforw", + "email": "fforw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5213, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d4f", + "username": "Jordão", + "email": "Jordão@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53757, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070a"]] + }, + { + "_id": "62f32596082fcc3049e91d51", + "username": "Jason", + "email": "Jason@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8208, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db3"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91012"] + ] + }, + { + "_id": "62f32596082fcc3049e91d54", + "username": "Korjavin Ivan", + "email": "Korjavin Ivan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 439, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d57", + "username": "Prakash GPz", + "email": "Prakash GPz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1605, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90725"]] + }, + { + "_id": "62f32596082fcc3049e91d5b", + "username": "James Wilkins", + "email": "James Wilkins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6251, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101d"]] + }, + { + "_id": "62f32596082fcc3049e91d5d", + "username": "foobored", + "email": "foobored@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 231, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d60", + "username": "Tyler", + "email": "Tyler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 351, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070b"]] + }, + { + "_id": "62f32596082fcc3049e91d62", + "username": "Inuart", + "email": "Inuart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1382, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32596082fcc3049e91d63", + "username": "Devin Rhode", + "email": "Devin Rhode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20932, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070c"]] + }, + { + "_id": "62f32597082fcc3049e91d65", + "username": "moliad", + "email": "moliad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1493, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d67", + "username": "semente", + "email": "semente@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6919, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070d"]] + }, + { + "_id": "62f32597082fcc3049e91d6a", + "username": "Peter V. Mørch", + "email": "Peter V. Mørch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11855, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070e"]] + }, + { + "_id": "62f32597082fcc3049e91d6c", + "username": "CpILL", + "email": "CpILL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5485, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d6e", + "username": "Davi Fiamenghi", + "email": "Davi Fiamenghi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1209, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d71", + "username": "hostingutilities.com", + "email": "hostingutilities.com@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8034, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d74", + "username": "Ryan Marshall", + "email": "Ryan Marshall@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 473, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d76", + "username": "beginner_", + "email": "beginner_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6880, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d78", + "username": "Venkat", + "email": "Venkat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1219, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d7c", + "username": "Gavin", + "email": "Gavin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6819, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d7e", + "username": "Tom Beech", + "email": "Tom Beech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9070f"]] + }, + { + "_id": "62f32597082fcc3049e91d80", + "username": "xdhmoore", + "email": "xdhmoore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7980, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d82", + "username": "David Nouls", + "email": "David Nouls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1885, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d83", + "username": "jpfreire", + "email": "jpfreire@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1268, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90710"]] + }, + { + "_id": "62f32597082fcc3049e91d84", + "username": "Aditya Hajare", + "email": "Aditya Hajare@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1303, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90712"]] + }, + { + "_id": "62f32597082fcc3049e91d86", + "username": "Sejanus", + "email": "Sejanus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3051, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d87", + "username": "Anmol Saraf", + "email": "Anmol Saraf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14287, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90711"]] + }, + { + "_id": "62f32597082fcc3049e91d89", + "username": "Colin", + "email": "Colin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 650, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d8c", + "username": "e-info128", + "email": "e-info128@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3245, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90717"]] + }, + { + "_id": "62f32597082fcc3049e91d8d", + "username": "BearCode", + "email": "BearCode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90714"]] + }, + { + "_id": "62f32597082fcc3049e91d91", + "username": "Steve Bennett", + "email": "Steve Bennett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101416, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d95", + "username": "schmijos", + "email": "schmijos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7536, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d98", + "username": "Alex Bitek", + "email": "Alex Bitek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6463, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d9b", + "username": "Danilo M. Oliveira", + "email": "Danilo M. Oliveira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 689, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91d9f", + "username": "Jan", + "email": "Jan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91da0", + "username": "Iain MacKay", + "email": "Iain MacKay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90715"]] + }, + { + "_id": "62f32597082fcc3049e91da2", + "username": "Barry Chapman", + "email": "Barry Chapman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6568, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32597082fcc3049e91da3", + "username": "AdrianCooney", + "email": "AdrianCooney@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 753, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90718"]] + }, + { + "_id": "62f32597082fcc3049e91da4", + "username": "KTys", + "email": "KTys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 170, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90719"]] + }, + { + "_id": "62f32597082fcc3049e91da5", + "username": "AgelessEssence", + "email": "AgelessEssence@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5995, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071a"]] + }, + { + "_id": "62f32597082fcc3049e91da6", + "username": "pocheptsov", + "email": "pocheptsov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1914, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071b"]] + }, + { + "_id": "62f32597082fcc3049e91da7", + "username": "Mr. Alien", + "email": "Mr. Alien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 148670, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071c"]] + }, + { + "_id": "62f32598082fcc3049e91da9", + "username": "Huei Tan", + "email": "Huei Tan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2197, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32598082fcc3049e91dac", + "username": "mightyiam", + "email": "mightyiam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071e"]] + }, + { + "_id": "62f32598082fcc3049e91dae", + "username": "RandomInsano", + "email": "RandomInsano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1114, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32598082fcc3049e91db0", + "username": "f.khantsis", + "email": "f.khantsis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2986, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32598082fcc3049e91db1", + "username": "Vignesh Subramanian", + "email": "Vignesh Subramanian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6941, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9071f"]] + }, + { + "_id": "62f32598082fcc3049e91db2", + "username": "Charles Brandt", + "email": "Charles Brandt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 853, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90720"]] + }, + { + "_id": "62f32598082fcc3049e91db4", + "username": "tomByrer", + "email": "tomByrer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1067, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32598082fcc3049e91db6", + "username": "seo", + "email": "seo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1911, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90721"]] + }, + { + "_id": "62f32598082fcc3049e91db7", + "username": "earl3s", + "email": "earl3s@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2313, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90724"]] + }, + { + "_id": "62f32598082fcc3049e91db8", + "username": "Sonevol", + "email": "Sonevol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90726"]] + }, + { + "_id": "62f32599082fcc3049e91db9", + "username": "jenil christo", + "email": "jenil christo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 666, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90728"], + ["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c9"] + ] + }, + { + "_id": "62f32599082fcc3049e91dbb", + "username": "user151496", + "email": "user151496@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1699, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32599082fcc3049e91dbd", + "username": "Pragmatiq", + "email": "Pragmatiq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90727"]] + }, + { + "_id": "62f32599082fcc3049e91dbf", + "username": "cmpreshn", + "email": "cmpreshn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 162, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32599082fcc3049e91dc2", + "username": "FlatLander", + "email": "FlatLander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1657, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32599082fcc3049e91dc3", + "username": "Pedro Andrade", + "email": "Pedro Andrade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4407, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072c"]] + }, + { + "_id": "62f32599082fcc3049e91dc5", + "username": "Kaz", + "email": "Kaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 52484, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32599082fcc3049e91dc6", + "username": "Niv", + "email": "Niv@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072e"]] + }, + { + "_id": "62f32599082fcc3049e91dc7", + "username": "NimaDoustdar", + "email": "NimaDoustdar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 172, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072d"]] + }, + { + "_id": "62f32599082fcc3049e91dc8", + "username": "Abhishek Goel", + "email": "Abhishek Goel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17173, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e9072f"]] + }, + { + "_id": "62f32599082fcc3049e91dc9", + "username": "ankit bhusal", + "email": "ankit bhusal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 151, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed3", "62f321c3082fcc3049e90730"]] + }, + { + "_id": "62f32599082fcc3049e91dca", + "username": "Newy", + "email": "Newy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37207, + "questionIds": ["62f321bb082fcc3049e8fed3"], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91dcb", + "username": "Andrew Dunkman", + "email": "Andrew Dunkman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90732"]] + }, + { + "_id": "62f3259a082fcc3049e91dce", + "username": "Deniz Dogan", + "email": "Deniz Dogan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25033, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91dd0", + "username": "Aaron Dufour", + "email": "Aaron Dufour@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16870, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91dd2", + "username": "chaos", + "email": "chaos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119586, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90731"]] + }, + { + "_id": "62f3259a082fcc3049e91dd5", + "username": "Aayush Sinha", + "email": "Aayush Sinha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 377, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91dd6", + "username": "Pablo Fernandez", + "email": "Pablo Fernandez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99652, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90734"]] + }, + { + "_id": "62f3259a082fcc3049e91dd9", + "username": "Eugenio Miró", + "email": "Eugenio Miró@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2350, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91ddb", + "username": "sorki", + "email": "sorki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 441, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91ddd", + "username": "Anonymous Pi", + "email": "Anonymous Pi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 125, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91ddf", + "username": "rlemon", + "email": "rlemon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17185, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91de1", + "username": "ryanwebjackson", + "email": "ryanwebjackson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 852, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91de5", + "username": "Prof", + "email": "Prof@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 637, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91de6", + "username": "DevinB", + "email": "DevinB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90735"]] + }, + { + "_id": "62f3259a082fcc3049e91de9", + "username": "WGroleau", + "email": "WGroleau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 432, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259a082fcc3049e91dea", + "username": "Alan Plum", + "email": "Alan Plum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10694, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90736"]] + }, + { + "_id": "62f3259a082fcc3049e91deb", + "username": "beauburrier", + "email": "beauburrier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1349, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90737"]] + }, + { + "_id": "62f3259a082fcc3049e91dec", + "username": "acuth", + "email": "acuth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 663, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90738"]] + }, + { + "_id": "62f3259a082fcc3049e91ded", + "username": "a_w", + "email": "a_w@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073a"]] + }, + { + "_id": "62f3259a082fcc3049e91dee", + "username": "Ben Flynn", + "email": "Ben Flynn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18024, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90739"], + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907af"] + ] + }, + { + "_id": "62f3259b082fcc3049e91def", + "username": "naazgull", + "email": "naazgull@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073b"]] + }, + { + "_id": "62f3259b082fcc3049e91df1", + "username": "Mike", + "email": "Mike@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259b082fcc3049e91df2", + "username": "Mainguy", + "email": "Mainguy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1603, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073c"]] + }, + { + "_id": "62f3259b082fcc3049e91df3", + "username": "naugtur", + "email": "naugtur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16697, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073d"]] + }, + { + "_id": "62f3259b082fcc3049e91df4", + "username": "Aaron", + "email": "Aaron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2001, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073e"]] + }, + { + "_id": "62f3259b082fcc3049e91df5", + "username": "CoR", + "email": "CoR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3698, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90740"]] + }, + { + "_id": "62f3259b082fcc3049e91df7", + "username": "mjaggard", + "email": "mjaggard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9073f"]] + }, + { + "_id": "62f3259b082fcc3049e91df8", + "username": "Michael Erickson", + "email": "Michael Erickson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3839, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90741"]] + }, + { + "_id": "62f3259b082fcc3049e91df9", + "username": "Matthew Strawbridge", + "email": "Matthew Strawbridge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19049, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90742"]] + }, + { + "_id": "62f3259b082fcc3049e91dfb", + "username": "Andrew Barber", + "email": "Andrew Barber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38644, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259b082fcc3049e91dfd", + "username": "Ian Maddox", + "email": "Ian Maddox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90743"]] + }, + { + "_id": "62f3259b082fcc3049e91dff", + "username": "Viktor Sehr", + "email": "Viktor Sehr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12610, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259b082fcc3049e91e00", + "username": "caiocpricci2", + "email": "caiocpricci2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7644, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90744"]] + }, + { + "_id": "62f3259c082fcc3049e91e02", + "username": "Seeker", + "email": "Seeker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2235, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e04", + "username": "Mouad Debbar", + "email": "Mouad Debbar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2898, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e05", + "username": "FaTaL_ErRoR", + "email": "FaTaL_ErRoR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90746"]] + }, + { + "_id": "62f3259c082fcc3049e91e07", + "username": "xaralis", + "email": "xaralis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4406, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e09", + "username": "Rachael", + "email": "Rachael@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90745"]] + }, + { + "_id": "62f3259c082fcc3049e91e0a", + "username": "BishopZ", + "email": "BishopZ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6139, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90748"], + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083b"] + ] + }, + { + "_id": "62f3259c082fcc3049e91e0b", + "username": "Homer6", + "email": "Homer6@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14673, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90747"]] + }, + { + "_id": "62f3259c082fcc3049e91e0c", + "username": "user2882556", + "email": "user2882556@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074a"]] + }, + { + "_id": "62f3259c082fcc3049e91e0e", + "username": "jab", + "email": "jab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4033, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e10", + "username": "mafu", + "email": "mafu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30482, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e12", + "username": "xDaizu", + "email": "xDaizu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1021, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e17", + "username": "Wheezil", + "email": "Wheezil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2937, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e1a", + "username": "Robert McLean", + "email": "Robert McLean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 151, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e1b", + "username": "StephaneAG", + "email": "StephaneAG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 997, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90749"]] + }, + { + "_id": "62f3259c082fcc3049e91e1d", + "username": "chim", + "email": "chim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8143, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c5"]] + }, + { + "_id": "62f3259c082fcc3049e91e20", + "username": "tponthieux", + "email": "tponthieux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1446, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074d"]] + }, + { + "_id": "62f3259c082fcc3049e91e22", + "username": "EML", + "email": "EML@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9023, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e24", + "username": "emery", + "email": "emery@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7217, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259c082fcc3049e91e26", + "username": "Shemeer M Ali", + "email": "Shemeer M Ali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1026, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074c"]] + }, + { + "_id": "62f3259d082fcc3049e91e28", + "username": "Beejor", + "email": "Beejor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7600, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259d082fcc3049e91e29", + "username": "Gabriel Ratener", + "email": "Gabriel Ratener@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 565, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9074f"]] + }, + { + "_id": "62f3259d082fcc3049e91e2b", + "username": "pguardiario", + "email": "pguardiario@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51889, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90750"]] + }, + { + "_id": "62f3259d082fcc3049e91e2d", + "username": "Matt Gibson", + "email": "Matt Gibson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37438, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259d082fcc3049e91e2e", + "username": "Rodrigo", + "email": "Rodrigo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4381, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90752"]] + }, + { + "_id": "62f3259d082fcc3049e91e30", + "username": "Aadit M Shah", + "email": "Aadit M Shah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71106, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3259d082fcc3049e91e31", + "username": "Xeridea", + "email": "Xeridea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90753"]] + }, + { + "_id": "62f3259d082fcc3049e91e32", + "username": "tomekwi", + "email": "tomekwi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1938, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90754"]] + }, + { + "_id": "62f3259d082fcc3049e91e34", + "username": "JSideris", + "email": "JSideris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4883, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90758"]] + }, + { + "_id": "62f3259d082fcc3049e91e35", + "username": "Elo", + "email": "Elo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2035, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90756"]] + }, + { + "_id": "62f3259d082fcc3049e91e36", + "username": "Ronald Davis", + "email": "Ronald Davis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90755"]] + }, + { + "_id": "62f3259d082fcc3049e91e37", + "username": "Miscreant", + "email": "Miscreant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5956, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90757"]] + }, + { + "_id": "62f3259e082fcc3049e91e38", + "username": "Caty Lawren", + "email": "Caty Lawren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075a"]] + }, + { + "_id": "62f3259e082fcc3049e91e3a", + "username": "Aistis", + "email": "Aistis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3411, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90759"]] + }, + { + "_id": "62f3259e082fcc3049e91e3c", + "username": "fmsf", + "email": "fmsf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35302, + "questionIds": ["62f321bb082fcc3049e8fed1"], + "answerIds": [] + }, + { + "_id": "62f3259e082fcc3049e91e3e", + "username": "ceremcem", + "email": "ceremcem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075b"]] + }, + { + "_id": "62f325db082fcc3049e91e3f", + "username": "God Ѻ", + "email": "God Ѻ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90763"]] + }, + { + "_id": "62f325db082fcc3049e91e41", + "username": "Ivan", + "email": "Ivan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3987, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90764"]] + }, + { + "_id": "62f325db082fcc3049e91e42", + "username": "Paul Stettler", + "email": "Paul Stettler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90766"]] + }, + { + "_id": "62f325db082fcc3049e91e44", + "username": "Shadow", + "email": "Shadow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8177, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325db082fcc3049e91e45", + "username": "CarstenP", + "email": "CarstenP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90765"]] + }, + { + "_id": "62f325db082fcc3049e91e47", + "username": "chharvey", + "email": "chharvey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7636, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca5"]] + }, + { + "_id": "62f325db082fcc3049e91e48", + "username": "Stitch", + "email": "Stitch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90768"]] + }, + { + "_id": "62f325db082fcc3049e91e4a", + "username": "pomber", + "email": "pomber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90767"]] + }, + { + "_id": "62f325db082fcc3049e91e4b", + "username": "Franz Kiermaier", + "email": "Franz Kiermaier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 387, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076a"]] + }, + { + "_id": "62f325db082fcc3049e91e4c", + "username": "MUY Belgium", + "email": "MUY Belgium@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076b"]] + }, + { + "_id": "62f325dc082fcc3049e91e4e", + "username": "Zeel Shah", + "email": "Zeel Shah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 462, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dc082fcc3049e91e4f", + "username": "Riccardo Persiani", + "email": "Riccardo Persiani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 622, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076c"]] + }, + { + "_id": "62f325dc082fcc3049e91e51", + "username": "Shankar", + "email": "Shankar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 117, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076e"]] + }, + { + "_id": "62f325dc082fcc3049e91e52", + "username": "ByteMe", + "email": "ByteMe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 999, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076d"]] + }, + { + "_id": "62f325dc082fcc3049e91e54", + "username": "GroovyDotCom", + "email": "GroovyDotCom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1264, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dc082fcc3049e91e56", + "username": "Kapytanhook", + "email": "Kapytanhook@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 825, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9076f"]] + }, + { + "_id": "62f325dc082fcc3049e91e58", + "username": "vitiral", + "email": "vitiral@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7537, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dc082fcc3049e91e5a", + "username": "Philipp Ludwig", + "email": "Philipp Ludwig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3098, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dc082fcc3049e91e5b", + "username": "Ahmed Mohammedali", + "email": "Ahmed Mohammedali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 410, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90770"]] + }, + { + "_id": "62f325dc082fcc3049e91e5d", + "username": "DDphp", + "email": "DDphp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dc082fcc3049e91e5f", + "username": "k06a", + "email": "k06a@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16759, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90771"]] + }, + { + "_id": "62f325dd082fcc3049e91e60", + "username": "sfscs", + "email": "sfscs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 496, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90772"]] + }, + { + "_id": "62f325dd082fcc3049e91e61", + "username": "Jared Dykstra", + "email": "Jared Dykstra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3556, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90773"]] + }, + { + "_id": "62f325dd082fcc3049e91e63", + "username": "pasha", + "email": "pasha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 394, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e64", + "username": "Ludolfyn", + "email": "Ludolfyn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1415, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90774"]] + }, + { + "_id": "62f325dd082fcc3049e91e66", + "username": "T.Woody", + "email": "T.Woody@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1043, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e67", + "username": "nkitku", + "email": "nkitku@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3588, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90776"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de8"] + ] + }, + { + "_id": "62f325dd082fcc3049e91e68", + "username": "Tawfik Nasser", + "email": "Tawfik Nasser@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 857, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90775"]] + }, + { + "_id": "62f325dd082fcc3049e91e69", + "username": "Mir-Ismaili", + "email": "Mir-Ismaili@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11242, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90778"]] + }, + { + "_id": "62f325dd082fcc3049e91e6b", + "username": "mousetail", + "email": "mousetail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5098, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e6d", + "username": "Ray Foss", + "email": "Ray Foss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3243, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90783"]] + }, + { + "_id": "62f325dd082fcc3049e91e6e", + "username": "quicVO", + "email": "quicVO@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 750, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90779"]] + }, + { + "_id": "62f325dd082fcc3049e91e6f", + "username": "Nalin Ranjan", + "email": "Nalin Ranjan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1565, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077c"]] + }, + { + "_id": "62f325dd082fcc3049e91e71", + "username": "Hovercraft Full Of Eels", + "email": "Hovercraft Full Of Eels@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 280625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e72", + "username": "Aleksandr Chernyi", + "email": "Aleksandr Chernyi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077d"]] + }, + { + "_id": "62f325dd082fcc3049e91e74", + "username": "sureshvv", + "email": "sureshvv@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3999, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e76", + "username": "Ravinder Payal", + "email": "Ravinder Payal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2738, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077b"]] + }, + { + "_id": "62f325dd082fcc3049e91e78", + "username": "Matias Haeussler", + "email": "Matias Haeussler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 971, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325dd082fcc3049e91e79", + "username": "S. Hesam", + "email": "S. Hesam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3731, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90780"]] + }, + { + "_id": "62f325dd082fcc3049e91e7a", + "username": "ForeverLearner", + "email": "ForeverLearner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1631, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9077f"]] + }, + { + "_id": "62f325de082fcc3049e91e7b", + "username": "Arjun G Perambra", + "email": "Arjun G Perambra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1979, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90781"]] + }, + { + "_id": "62f325de082fcc3049e91e7c", + "username": "Eldar Djafarov", + "email": "Eldar Djafarov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21893, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90790"]] + }, + { + "_id": "62f325de082fcc3049e91e80", + "username": "rahul", + "email": "rahul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 180829, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90791"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d51"] + ] + }, + { + "_id": "62f325de082fcc3049e91e82", + "username": "Govinda Sakhare", + "email": "Govinda Sakhare@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4233, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325de082fcc3049e91e84", + "username": "Alexander Derck", + "email": "Alexander Derck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13048, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325de082fcc3049e91e87", + "username": "JAAulde", + "email": "JAAulde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18897, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90795"]] + }, + { + "_id": "62f325de082fcc3049e91e89", + "username": "Fabio Martins", + "email": "Fabio Martins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 115, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325de082fcc3049e91e8a", + "username": "Jeff Poulton", + "email": "Jeff Poulton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6002, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90794"]] + }, + { + "_id": "62f325de082fcc3049e91e8c", + "username": "James Drinkard", + "email": "James Drinkard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14770, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90796"], + ["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff6"] + ] + }, + { + "_id": "62f325df082fcc3049e91e8d", + "username": "Naga Srinu Kapusetti", + "email": "Naga Srinu Kapusetti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1417, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90797"]] + }, + { + "_id": "62f325df082fcc3049e91e90", + "username": "Taiger", + "email": "Taiger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91e91", + "username": "Dilip0165", + "email": "Dilip0165@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90798"]] + }, + { + "_id": "62f325df082fcc3049e91e92", + "username": "Abdullah Shoaib", + "email": "Abdullah Shoaib@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 396, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e90799"]] + }, + { + "_id": "62f325df082fcc3049e91e93", + "username": "Parth Raval", + "email": "Parth Raval@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3701, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e9079a"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d27"] + ] + }, + { + "_id": "62f325df082fcc3049e91e94", + "username": "Sathish Shajahan", + "email": "Sathish Shajahan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed6", "62f321c3082fcc3049e9079c"]] + }, + { + "_id": "62f325df082fcc3049e91e95", + "username": "RaYell", + "email": "RaYell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 68374, + "questionIds": ["62f321bb082fcc3049e8fed6"], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91e97", + "username": "Jon Galloway", + "email": "Jon Galloway@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51538, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907a7"]] + }, + { + "_id": "62f325df082fcc3049e91e99", + "username": "Eric Sebasta", + "email": "Eric Sebasta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 225, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91e9a", + "username": "Eric Wendelin", + "email": "Eric Wendelin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41573, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907a9"]] + }, + { + "_id": "62f325df082fcc3049e91e9b", + "username": "Eric Bailey", + "email": "Eric Bailey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907aa"]] + }, + { + "_id": "62f325df082fcc3049e91e9d", + "username": "Peter Boughton", + "email": "Peter Boughton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea0", + "username": "Erik Olson", + "email": "Erik Olson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1134, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea2", + "username": "Alex Jolig", + "email": "Alex Jolig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13142, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea4", + "username": "Krease", + "email": "Krease@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15358, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea6", + "username": "Mr Anderson", + "email": "Mr Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2125, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea8", + "username": "Lajos Mészáros", + "email": "Lajos Mészáros@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3590, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325df082fcc3049e91ea9", + "username": "roenving", + "email": "roenving@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2486, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907a8"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f18"] + ] + }, + { + "_id": "62f325e0082fcc3049e91eaa", + "username": "Hiren Kansara", + "email": "Hiren Kansara@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 211, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907ab"]] + }, + { + "_id": "62f325e0082fcc3049e91ead", + "username": "kfrncs", + "email": "kfrncs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 413, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91eae", + "username": "Andrew Orsich", + "email": "Andrew Orsich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907ac"]] + }, + { + "_id": "62f325e0082fcc3049e91eb1", + "username": "ELLIOTTCABLE", + "email": "ELLIOTTCABLE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15965, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91eb4", + "username": "Wilf", + "email": "Wilf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 693, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91eb5", + "username": "Tyilo", + "email": "Tyilo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27808, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907ad"]] + }, + { + "_id": "62f325e0082fcc3049e91eb6", + "username": "PseudoNinja", + "email": "PseudoNinja@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2787, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907ae"]] + }, + { + "_id": "62f325e0082fcc3049e91eb8", + "username": "Roman Polen.", + "email": "Roman Polen.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 501, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91eba", + "username": "Lukasz 'Severiaan' Grela", + "email": "Lukasz 'Severiaan' Grela@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5858, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91ebb", + "username": "Gopal Krishna Ranjan", + "email": "Gopal Krishna Ranjan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 828, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b0"]] + }, + { + "_id": "62f325e0082fcc3049e91ebc", + "username": "Alex Robinson", + "email": "Alex Robinson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 579, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b2"]] + }, + { + "_id": "62f325e0082fcc3049e91ebe", + "username": "Gabe", + "email": "Gabe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2250, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91ec1", + "username": "Anthony Rutledge", + "email": "Anthony Rutledge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6054, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91ec3", + "username": "gcampbell", + "email": "gcampbell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1242, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e0082fcc3049e91ec4", + "username": "Travis J", + "email": "Travis J@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79493, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b1"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90807"] + ] + }, + { + "_id": "62f325e0082fcc3049e91ec5", + "username": "shingokko", + "email": "shingokko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 394, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b3"]] + }, + { + "_id": "62f325e0082fcc3049e91ec7", + "username": "alfred", + "email": "alfred@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 970, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b4"]] + }, + { + "_id": "62f325e1082fcc3049e91ec9", + "username": "moka", + "email": "moka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22442, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b5"]] + }, + { + "_id": "62f325e1082fcc3049e91eca", + "username": "uttamcafedeweb", + "email": "uttamcafedeweb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b7"]] + }, + { + "_id": "62f325e1082fcc3049e91ecb", + "username": "StackSlave", + "email": "StackSlave@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10469, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b8"]] + }, + { + "_id": "62f325e1082fcc3049e91ecc", + "username": "kofifus", + "email": "kofifus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14883, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907b9"]] + }, + { + "_id": "62f325e1082fcc3049e91ecd", + "username": "Danish Khan", + "email": "Danish Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 309, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907be"]] + }, + { + "_id": "62f325e2082fcc3049e91ece", + "username": "donatso", + "email": "donatso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c2"]] + }, + { + "_id": "62f325e2082fcc3049e91ecf", + "username": "Brian Nezhad", + "email": "Brian Nezhad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5970, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c1"]] + }, + { + "_id": "62f325e2082fcc3049e91ed0", + "username": "Jai Prakash", + "email": "Jai Prakash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c3"]] + }, + { + "_id": "62f325e2082fcc3049e91ed1", + "username": "timbo", + "email": "timbo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11855, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c4"]] + }, + { + "_id": "62f325e2082fcc3049e91ed2", + "username": "gomn", + "email": "gomn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed4", "62f321c3082fcc3049e907c5"]] + }, + { + "_id": "62f325e2082fcc3049e91ed3", + "username": "Nathan Smith", + "email": "Nathan Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35367, + "questionIds": ["62f321bb082fcc3049e8fed4"], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ed5", + "username": "Vajk Hermecz", + "email": "Vajk Hermecz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4933, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ed7", + "username": "Alex Shaffer", + "email": "Alex Shaffer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 616, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ed9", + "username": "rofrol", + "email": "rofrol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13367, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91edb", + "username": "Gerrie van Wyk", + "email": "Gerrie van Wyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 649, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91edd", + "username": "nsandersen", + "email": "nsandersen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 796, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91edf", + "username": "simo", + "email": "simo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14438, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907c8"]] + }, + { + "_id": "62f325e2082fcc3049e91ee1", + "username": "fitzgeraldsteele", + "email": "fitzgeraldsteele@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4537, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ee3", + "username": "Filipe Madureira", + "email": "Filipe Madureira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 350, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ee5", + "username": "barshopen", + "email": "barshopen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 966, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ee7", + "username": "Lle.4", + "email": "Lle.4@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e2082fcc3049e91ee9", + "username": "Sébastien", + "email": "Sébastien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1963, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907c7"]] + }, + { + "_id": "62f325e3082fcc3049e91eed", + "username": "Samuel Lindblom", + "email": "Samuel Lindblom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 818, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e3082fcc3049e91eee", + "username": "Thulasiram", + "email": "Thulasiram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8268, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907c9"]] + }, + { + "_id": "62f325e3082fcc3049e91eef", + "username": "Tony Brix", + "email": "Tony Brix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3895, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907cc"], + ["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d5"] + ] + }, + { + "_id": "62f325e3082fcc3049e91ef0", + "username": "Dmitry Pavlov", + "email": "Dmitry Pavlov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28903, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907cb"]] + }, + { + "_id": "62f325e3082fcc3049e91ef1", + "username": "Prasad DLV", + "email": "Prasad DLV@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907cd"]] + }, + { + "_id": "62f325e3082fcc3049e91ef3", + "username": "webzy", + "email": "webzy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907cf"]] + }, + { + "_id": "62f325e3082fcc3049e91ef4", + "username": "Mite Mitreski", + "email": "Mite Mitreski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3548, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d0"]] + }, + { + "_id": "62f325e3082fcc3049e91ef6", + "username": "pat capozzi", + "email": "pat capozzi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1364, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d1"]] + }, + { + "_id": "62f325e3082fcc3049e91ef8", + "username": "Benny Jobigan", + "email": "Benny Jobigan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4855, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e3082fcc3049e91efa", + "username": "JHH", + "email": "JHH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7687, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e3082fcc3049e91efb", + "username": "sebastian.i", + "email": "sebastian.i@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d2"]] + }, + { + "_id": "62f325e4082fcc3049e91efc", + "username": "JD Smith", + "email": "JD Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1754, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d3"]] + }, + { + "_id": "62f325e4082fcc3049e91efe", + "username": "Rahul Tagore", + "email": "Rahul Tagore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e4082fcc3049e91eff", + "username": "SaidB", + "email": "SaidB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d5"]] + }, + { + "_id": "62f325e4082fcc3049e91f00", + "username": "Gene R", + "email": "Gene R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3644, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d6"]] + }, + { + "_id": "62f325e4082fcc3049e91f01", + "username": "JP de la Torre", + "email": "JP de la Torre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1460, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d7"]] + }, + { + "_id": "62f325e4082fcc3049e91f02", + "username": "Arjun Nayak", + "email": "Arjun Nayak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1155, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907d8"]] + }, + { + "_id": "62f325e5082fcc3049e91f03", + "username": "Henadzi Rabkin", + "email": "Henadzi Rabkin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6634, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907da"]] + }, + { + "_id": "62f325e5082fcc3049e91f05", + "username": "lbrahim", + "email": "lbrahim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3529, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e5082fcc3049e91f06", + "username": "Amit Kumar Gupta", + "email": "Amit Kumar Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6844, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907dc"]] + }, + { + "_id": "62f325e5082fcc3049e91f07", + "username": "Dave", + "email": "Dave@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2765, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907dd"], + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907de"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90ddf"] + ] + }, + { + "_id": "62f325e5082fcc3049e91f09", + "username": "Vijay Maheriya", + "email": "Vijay Maheriya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907df"]] + }, + { + "_id": "62f325e5082fcc3049e91f0b", + "username": "Pants", + "email": "Pants@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2179, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e5082fcc3049e91f0c", + "username": "Combine", + "email": "Combine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3496, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e3"]] + }, + { + "_id": "62f325e5082fcc3049e91f0d", + "username": "Kirk Strobeck", + "email": "Kirk Strobeck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17195, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e5"]] + }, + { + "_id": "62f325e5082fcc3049e91f0e", + "username": "Mr Nsubuga", + "email": "Mr Nsubuga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 280, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e4"]] + }, + { + "_id": "62f325e6082fcc3049e91f0f", + "username": "Hinrich", + "email": "Hinrich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12877, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e7"]] + }, + { + "_id": "62f325e6082fcc3049e91f10", + "username": "Matias Osmerini", + "email": "Matias Osmerini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e8"]] + }, + { + "_id": "62f325e6082fcc3049e91f11", + "username": "lewdev", + "email": "lewdev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6382, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907e9"]] + }, + { + "_id": "62f325e6082fcc3049e91f12", + "username": "site", + "email": "site@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1390, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ec"]] + }, + { + "_id": "62f325e6082fcc3049e91f13", + "username": "blairzotron", + "email": "blairzotron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 259, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907eb"]] + }, + { + "_id": "62f325e6082fcc3049e91f14", + "username": "Melchia", + "email": "Melchia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20270, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ee"]] + }, + { + "_id": "62f325e7082fcc3049e91f15", + "username": "Guilherme Ferreira", + "email": "Guilherme Ferreira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ed"]] + }, + { + "_id": "62f325e7082fcc3049e91f16", + "username": "K Vij", + "email": "K Vij@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1475, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907ef"]] + }, + { + "_id": "62f325e7082fcc3049e91f17", + "username": "Chris Kobrzak", + "email": "Chris Kobrzak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 856, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f0"]] + }, + { + "_id": "62f325e7082fcc3049e91f18", + "username": "Prabha", + "email": "Prabha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 236, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f3"]] + }, + { + "_id": "62f325e7082fcc3049e91f19", + "username": "perepm", + "email": "perepm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 840, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f6"]] + }, + { + "_id": "62f325e7082fcc3049e91f1a", + "username": "saran3h", + "email": "saran3h@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10618, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f5"]] + }, + { + "_id": "62f325e7082fcc3049e91f1b", + "username": "moshfiqrony", + "email": "moshfiqrony@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3035, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f7"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dea"] + ] + }, + { + "_id": "62f325e7082fcc3049e91f1c", + "username": "Weilory", + "email": "Weilory@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1968, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907f8"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90582"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92faa"] + ] + }, + { + "_id": "62f325e7082fcc3049e91f1d", + "username": "saddam", + "email": "saddam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 146, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed7", "62f321c4082fcc3049e907fa"]] + }, + { + "_id": "62f325e8082fcc3049e91f1e", + "username": "leora", + "email": "leora@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179369, + "questionIds": ["62f321bb082fcc3049e8fed7"], + "answerIds": [] + }, + { + "_id": "62f325e8082fcc3049e91f20", + "username": "aswzen", + "email": "aswzen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1254, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e8082fcc3049e91f22", + "username": "Jet Blue", + "email": "Jet Blue@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e8082fcc3049e91f24", + "username": "Abhishek Singh", + "email": "Abhishek Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1550, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e8082fcc3049e91f25", + "username": "Bjorn", + "email": "Bjorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65735, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e907fc"]] + }, + { + "_id": "62f325e8082fcc3049e91f26", + "username": "jottos", + "email": "jottos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19798, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e907fd"]] + }, + { + "_id": "62f325e8082fcc3049e91f28", + "username": "Boann", + "email": "Boann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47454, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90800"]] + }, + { + "_id": "62f325e8082fcc3049e91f29", + "username": "user1724763", + "email": "user1724763@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90801"]] + }, + { + "_id": "62f325e8082fcc3049e91f2a", + "username": "eglasius", + "email": "eglasius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35465, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e907fe"]] + }, + { + "_id": "62f325e8082fcc3049e91f2c", + "username": "pixel 67", + "email": "pixel 67@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1360, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080e"]] + }, + { + "_id": "62f325e8082fcc3049e91f2d", + "username": "Ben McCormick", + "email": "Ben McCormick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24524, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90802"]] + }, + { + "_id": "62f325e8082fcc3049e91f2f", + "username": "Kemal Dağ", + "email": "Kemal Dağ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2715, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90803"]] + }, + { + "_id": "62f325e8082fcc3049e91f30", + "username": "yilmazburk", + "email": "yilmazburk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90804"]] + }, + { + "_id": "62f325e9082fcc3049e91f31", + "username": "Daryl", + "email": "Daryl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3154, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90808"]] + }, + { + "_id": "62f325e9082fcc3049e91f34", + "username": "user2290820", + "email": "user2290820@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2491, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f36", + "username": "niry", + "email": "niry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3079, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f37", + "username": "Aust", + "email": "Aust@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11292, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90805"]] + }, + { + "_id": "62f325e9082fcc3049e91f39", + "username": "Kyle Falconer", + "email": "Kyle Falconer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8024, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90846"]] + }, + { + "_id": "62f325e9082fcc3049e91f3b", + "username": "Nico", + "email": "Nico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1060, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f3d", + "username": "JLRishe", + "email": "JLRishe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 96244, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f3f", + "username": "Timar Ivo Batis", + "email": "Timar Ivo Batis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1729, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f40", + "username": "neurosnap", + "email": "neurosnap@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5460, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90806"]] + }, + { + "_id": "62f325e9082fcc3049e91f42", + "username": "jherax", + "email": "jherax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5188, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325e9082fcc3049e91f44", + "username": "Christian Landgren", + "email": "Christian Landgren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12459, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080a"]] + }, + { + "_id": "62f325e9082fcc3049e91f45", + "username": "wpding", + "email": "wpding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 186, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90809"]] + }, + { + "_id": "62f325e9082fcc3049e91f46", + "username": "axelduch", + "email": "axelduch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10599, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080b"]] + }, + { + "_id": "62f325e9082fcc3049e91f48", + "username": "Rune FS", + "email": "Rune FS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080c"]] + }, + { + "_id": "62f325e9082fcc3049e91f4a", + "username": "Rax Wunter", + "email": "Rax Wunter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2561, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080d"]] + }, + { + "_id": "62f325ea082fcc3049e91f4d", + "username": "Ali Kahoot", + "email": "Ali Kahoot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2751, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9080f"]] + }, + { + "_id": "62f325ea082fcc3049e91f4e", + "username": "Buksy", + "email": "Buksy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10866, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90810"]] + }, + { + "_id": "62f325ea082fcc3049e91f4f", + "username": "Prithvi Uppalapati", + "email": "Prithvi Uppalapati@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 750, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90811"]] + }, + { + "_id": "62f325ea082fcc3049e91f51", + "username": "lovasoa", + "email": "lovasoa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f325ea082fcc3049e91f52", + "username": "Alexander Levakov", + "email": "Alexander Levakov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90812"]] + }, + { + "_id": "62f325ea082fcc3049e91f53", + "username": "Vikash_Singh", + "email": "Vikash_Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1815, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90814"]] + }, + { + "_id": "62f325ea082fcc3049e91f54", + "username": "neatsu", + "email": "neatsu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 154, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90817"]] + }, + { + "_id": "62f325ea082fcc3049e91f55", + "username": "sidhuko", + "email": "sidhuko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3018, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90818"]] + }, + { + "_id": "62f325eb082fcc3049e91f56", + "username": "Eyal Segal", + "email": "Eyal Segal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081a"]] + }, + { + "_id": "62f325eb082fcc3049e91f58", + "username": "Brooks DuBois", + "email": "Brooks DuBois@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 645, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90819"]] + }, + { + "_id": "62f325eb082fcc3049e91f5a", + "username": "stemon", + "email": "stemon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 515, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081c"]] + }, + { + "_id": "62f325eb082fcc3049e91f5b", + "username": "Mark Manning", + "email": "Mark Manning@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081b"]] + }, + { + "_id": "62f325eb082fcc3049e91f5c", + "username": "user1559625", + "email": "user1559625@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2504, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081e"]] + }, + { + "_id": "62f32628082fcc3049e91f5e", + "username": "Noman_1", + "email": "Noman_1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 441, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f5f", + "username": "Shivang Gupta", + "email": "Shivang Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2941, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90823"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2f"] + ] + }, + { + "_id": "62f32629082fcc3049e91f61", + "username": "Woozar", + "email": "Woozar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 822, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f62", + "username": "Jared Farrish", + "email": "Jared Farrish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47450, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90826"]] + }, + { + "_id": "62f32629082fcc3049e91f64", + "username": "NoxFly", + "email": "NoxFly@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 165, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f66", + "username": "Jose Quijada", + "email": "Jose Quijada@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 470, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f67", + "username": "Jonny Buchanan", + "email": "Jonny Buchanan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60651, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90827"]] + }, + { + "_id": "62f32629082fcc3049e91f68", + "username": "Shadow2531", + "email": "Shadow2531@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11780, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082a"], + ["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041a"] + ] + }, + { + "_id": "62f32629082fcc3049e91f69", + "username": "JW.", + "email": "JW.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49367, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90829"]] + }, + { + "_id": "62f32629082fcc3049e91f6a", + "username": "Icaro Dourado", + "email": "Icaro Dourado@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082c"]] + }, + { + "_id": "62f32629082fcc3049e91f6c", + "username": "pauloya", + "email": "pauloya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2525, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f6d", + "username": "ander", + "email": "ander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082b"]] + }, + { + "_id": "62f32629082fcc3049e91f6f", + "username": "Thomas Eding", + "email": "Thomas Eding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33787, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082f"], + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90976"] + ] + }, + { + "_id": "62f32629082fcc3049e91f71", + "username": "moo", + "email": "moo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7420, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f72", + "username": "PixelSlave", + "email": "PixelSlave@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082e"]] + }, + { + "_id": "62f32629082fcc3049e91f74", + "username": "dav_i", + "email": "dav_i@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26623, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32629082fcc3049e91f75", + "username": "Steven", + "email": "Steven@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9082d"]] + }, + { + "_id": "62f3262a082fcc3049e91f77", + "username": "tillda", + "email": "tillda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17700, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262a082fcc3049e91f79", + "username": "Steel Brain", + "email": "Steel Brain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4112, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262a082fcc3049e91f7b", + "username": "Szymon Wygnański", + "email": "Szymon Wygnański@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10154, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262a082fcc3049e91f7c", + "username": "cypher", + "email": "cypher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 419, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90830"]] + }, + { + "_id": "62f3262a082fcc3049e91f7e", + "username": "YMMD", + "email": "YMMD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3720, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262a082fcc3049e91f7f", + "username": "risingfish", + "email": "risingfish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90832"]] + }, + { + "_id": "62f3262a082fcc3049e91f81", + "username": "thdoan", + "email": "thdoan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17703, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90831"]] + }, + { + "_id": "62f3262a082fcc3049e91f83", + "username": "Yuck", + "email": "Yuck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47595, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262a082fcc3049e91f84", + "username": "hajikelist", + "email": "hajikelist@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1100, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90835"]] + }, + { + "_id": "62f3262a082fcc3049e91f85", + "username": "imjustmatthew", + "email": "imjustmatthew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 176, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90836"]] + }, + { + "_id": "62f3262b082fcc3049e91f86", + "username": "Scrimothy", + "email": "Scrimothy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2506, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90837"]] + }, + { + "_id": "62f3262b082fcc3049e91f88", + "username": "thisismydesign", + "email": "thisismydesign@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15895, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262b082fcc3049e91f89", + "username": "mbeasley", + "email": "mbeasley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4754, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90838"]] + }, + { + "_id": "62f3262b082fcc3049e91f8a", + "username": "jerone", + "email": "jerone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15510, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90839"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aed"] + ] + }, + { + "_id": "62f3262b082fcc3049e91f8b", + "username": "AndreasPizsa", + "email": "AndreasPizsa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083a"]] + }, + { + "_id": "62f3262b082fcc3049e91f8d", + "username": "Kevin Boucher", + "email": "Kevin Boucher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15823, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262b082fcc3049e91f8e", + "username": "jackvsworld", + "email": "jackvsworld@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083c"]] + }, + { + "_id": "62f3262b082fcc3049e91f8f", + "username": "dalimian", + "email": "dalimian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1746, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083e"]] + }, + { + "_id": "62f3262b082fcc3049e91f90", + "username": "purab", + "email": "purab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 211, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083d"]] + }, + { + "_id": "62f3262b082fcc3049e91f91", + "username": "user3638793", + "email": "user3638793@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90840"]] + }, + { + "_id": "62f3262b082fcc3049e91f92", + "username": "Andreas Dyballa", + "email": "Andreas Dyballa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9083f"]] + }, + { + "_id": "62f3262c082fcc3049e91f93", + "username": "zobier", + "email": "zobier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1559, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90841"]] + }, + { + "_id": "62f3262c082fcc3049e91f94", + "username": "Dead.Rabit", + "email": "Dead.Rabit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1955, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90842"]] + }, + { + "_id": "62f3262c082fcc3049e91f95", + "username": "Cliff Mayson", + "email": "Cliff Mayson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 476, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90843"]] + }, + { + "_id": "62f3262c082fcc3049e91f96", + "username": "CMCDragonkai", + "email": "CMCDragonkai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5910, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90844"]] + }, + { + "_id": "62f3262c082fcc3049e91f97", + "username": "Jan Remunda", + "email": "Jan Remunda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90845"]] + }, + { + "_id": "62f3262c082fcc3049e91f98", + "username": "BrDaHa", + "email": "BrDaHa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4589, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90847"]] + }, + { + "_id": "62f3262c082fcc3049e91f99", + "username": "Mahes", + "email": "Mahes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084a"]] + }, + { + "_id": "62f3262c082fcc3049e91f9b", + "username": "jakubiszon", + "email": "jakubiszon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262c082fcc3049e91f9c", + "username": "user3310384", + "email": "user3310384@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90849"]] + }, + { + "_id": "62f3262c082fcc3049e91f9d", + "username": "Timo Ernst", + "email": "Timo Ernst@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14451, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084d"]] + }, + { + "_id": "62f3262c082fcc3049e91f9e", + "username": "konsumer", + "email": "konsumer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3303, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084c"]] + }, + { + "_id": "62f3262d082fcc3049e91f9f", + "username": "Adam Pietrasiak", + "email": "Adam Pietrasiak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12037, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084e"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9d"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d28"] + ] + }, + { + "_id": "62f3262d082fcc3049e91fa1", + "username": "spottedmahn", + "email": "spottedmahn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13615, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262d082fcc3049e91fa2", + "username": "sospedra", + "email": "sospedra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13408, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9084f"]] + }, + { + "_id": "62f3262d082fcc3049e91fa3", + "username": "Martin Malinda", + "email": "Martin Malinda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90850"]] + }, + { + "_id": "62f3262d082fcc3049e91fa4", + "username": "Fizer Khan", + "email": "Fizer Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81583, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90851"], + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90985"] + ] + }, + { + "_id": "62f3262d082fcc3049e91fa6", + "username": "null", + "email": "null@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2555, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90852"]] + }, + { + "_id": "62f3262d082fcc3049e91fa8", + "username": "Hugo Zapata", + "email": "Hugo Zapata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4264, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262d082fcc3049e91fab", + "username": "foxdonut", + "email": "foxdonut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7419, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262d082fcc3049e91fac", + "username": "Siten", + "email": "Siten@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4473, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90854"]] + }, + { + "_id": "62f3262d082fcc3049e91fad", + "username": "vbranden", + "email": "vbranden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5434, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90853"]] + }, + { + "_id": "62f3262d082fcc3049e91fae", + "username": "ecabuk", + "email": "ecabuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1551, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90855"]] + }, + { + "_id": "62f3262d082fcc3049e91faf", + "username": "jdnichollsc", + "email": "jdnichollsc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1408, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90856"]] + }, + { + "_id": "62f3262e082fcc3049e91fb0", + "username": "Sandip Nirmal", + "email": "Sandip Nirmal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2080, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90858"]] + }, + { + "_id": "62f3262e082fcc3049e91fb1", + "username": "benipsen", + "email": "benipsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 473, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90857"]] + }, + { + "_id": "62f3262e082fcc3049e91fb2", + "username": "jose.serapicos", + "email": "jose.serapicos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 521, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085a"]] + }, + { + "_id": "62f3262e082fcc3049e91fb4", + "username": "Angelo Oparah", + "email": "Angelo Oparah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 663, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262e082fcc3049e91fb5", + "username": "Hakan Fıstık", + "email": "Hakan Fıstık@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14845, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90859"]] + }, + { + "_id": "62f3262e082fcc3049e91fb6", + "username": "Rohman HM", + "email": "Rohman HM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2264, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085b"]] + }, + { + "_id": "62f3262e082fcc3049e91fb7", + "username": "Steve Mc", + "email": "Steve Mc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3373, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085c"]] + }, + { + "_id": "62f3262e082fcc3049e91fb8", + "username": "Surya R Praveen", + "email": "Surya R Praveen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085d"]] + }, + { + "_id": "62f3262e082fcc3049e91fba", + "username": "serdar.sanri", + "email": "serdar.sanri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2147, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262e082fcc3049e91fbb", + "username": "Dayem Siddiqui", + "email": "Dayem Siddiqui@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 155, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9085e"]] + }, + { + "_id": "62f3262e082fcc3049e91fbc", + "username": "Chanakya Vadla", + "email": "Chanakya Vadla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2989, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90860"]] + }, + { + "_id": "62f3262e082fcc3049e91fbe", + "username": "Prashanth Hegde", + "email": "Prashanth Hegde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 174, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262e082fcc3049e91fbf", + "username": "guest271314", + "email": "guest271314@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90861"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b0"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cfd"] + ] + }, + { + "_id": "62f3262e082fcc3049e91fc1", + "username": "D. Pardal", + "email": "D. Pardal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5694, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262e082fcc3049e91fc2", + "username": "Mohammad Farahani", + "email": "Mohammad Farahani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 543, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90862"]] + }, + { + "_id": "62f3262f082fcc3049e91fc4", + "username": "Oskar Berggren", + "email": "Oskar Berggren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262f082fcc3049e91fc5", + "username": "Kostanos", + "email": "Kostanos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8750, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90864"]] + }, + { + "_id": "62f3262f082fcc3049e91fc6", + "username": "OhkaBaka", + "email": "OhkaBaka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90865"]] + }, + { + "_id": "62f3262f082fcc3049e91fc7", + "username": "Yohan", + "email": "Yohan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90866"]] + }, + { + "_id": "62f3262f082fcc3049e91fc8", + "username": "pankaj sharma", + "email": "pankaj sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 276, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90867"]] + }, + { + "_id": "62f3262f082fcc3049e91fca", + "username": "Denis Nutiu", + "email": "Denis Nutiu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 759, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3262f082fcc3049e91fcc", + "username": "Ratan Uday Kumar", + "email": "Ratan Uday Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4838, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90868"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d14"] + ] + }, + { + "_id": "62f32630082fcc3049e91fce", + "username": "Al Albers", + "email": "Al Albers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086a"]] + }, + { + "_id": "62f32630082fcc3049e91fcf", + "username": "Yuriy Litvin", + "email": "Yuriy Litvin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90869"]] + }, + { + "_id": "62f32630082fcc3049e91fd0", + "username": "olegtaranenko", + "email": "olegtaranenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3607, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086c"]] + }, + { + "_id": "62f32630082fcc3049e91fd2", + "username": "zeross", + "email": "zeross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32630082fcc3049e91fd4", + "username": "panatoni", + "email": "panatoni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 645, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086b"]] + }, + { + "_id": "62f32630082fcc3049e91fd5", + "username": "sçuçu", + "email": "sçuçu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086e"]] + }, + { + "_id": "62f32630082fcc3049e91fd6", + "username": "James Anderson Jr.", + "email": "James Anderson Jr.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 721, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086d"]] + }, + { + "_id": "62f32630082fcc3049e91fd7", + "username": "Mansi", + "email": "Mansi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90870"]] + }, + { + "_id": "62f32630082fcc3049e91fd8", + "username": "Faizan Khan", + "email": "Faizan Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9086f"]] + }, + { + "_id": "62f32630082fcc3049e91fd9", + "username": "Justin Liu", + "email": "Justin Liu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 563, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90872"]] + }, + { + "_id": "62f32630082fcc3049e91fda", + "username": "jacobq", + "email": "jacobq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10662, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90871"]] + }, + { + "_id": "62f32630082fcc3049e91fdb", + "username": "Neil Higgins", + "email": "Neil Higgins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90874"]] + }, + { + "_id": "62f32630082fcc3049e91fdd", + "username": "ritesh", + "email": "ritesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90873"]] + }, + { + "_id": "62f32631082fcc3049e91fde", + "username": "Bhupender Keswani", + "email": "Bhupender Keswani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1178, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90875"]] + }, + { + "_id": "62f32631082fcc3049e91fe0", + "username": "Vaibhav S", + "email": "Vaibhav S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 163, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91fe1", + "username": "Abderrahmen", + "email": "Abderrahmen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 373, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90876"]] + }, + { + "_id": "62f32631082fcc3049e91fe3", + "username": "Wyck", + "email": "Wyck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91fe5", + "username": "Kieran Ryan", + "email": "Kieran Ryan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 581, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91fe7", + "username": "Kal", + "email": "Kal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1282, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90877"]] + }, + { + "_id": "62f32631082fcc3049e91fe9", + "username": "Jonathan Lyon", + "email": "Jonathan Lyon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3662, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91fea", + "username": "cskwg", + "email": "cskwg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 641, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90879"]] + }, + { + "_id": "62f32631082fcc3049e91fec", + "username": "Khanakia", + "email": "Khanakia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 653, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91fed", + "username": "Alexandre Annic", + "email": "Alexandre Annic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8885, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087a"]] + }, + { + "_id": "62f32631082fcc3049e91fef", + "username": "Alexander Santos", + "email": "Alexander Santos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1239, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91ff0", + "username": "OMANSAK", + "email": "OMANSAK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 885, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087c"]] + }, + { + "_id": "62f32631082fcc3049e91ff2", + "username": "Akin Zeman", + "email": "Akin Zeman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 343, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91ff3", + "username": "Debasish Jena", + "email": "Debasish Jena@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087b"]] + }, + { + "_id": "62f32631082fcc3049e91ff5", + "username": "jbalintac", + "email": "jbalintac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32631082fcc3049e91ff7", + "username": "ksugiarto", + "email": "ksugiarto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 930, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e91ff9", + "username": "opticyclic", + "email": "opticyclic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6638, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e91ffb", + "username": "ru4ert", + "email": "ru4ert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 794, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e9087f"]] + }, + { + "_id": "62f32632082fcc3049e91ffd", + "username": "Nat", + "email": "Nat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 550, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e91ffe", + "username": "Sigit", + "email": "Sigit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 658, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90880"]] + }, + { + "_id": "62f32632082fcc3049e92000", + "username": "Asinus Rex", + "email": "Asinus Rex@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 515, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92002", + "username": "Muhammed Moussa", + "email": "Muhammed Moussa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3636, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92004", + "username": "Just a coder", + "email": "Just a coder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13129, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92006", + "username": "marko-36", + "email": "marko-36@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1098, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92008", + "username": "Ivan Yulin", + "email": "Ivan Yulin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 620, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92009", + "username": "cabbage dude", + "email": "cabbage dude@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90882"]] + }, + { + "_id": "62f32632082fcc3049e9200b", + "username": "Richard Torcato", + "email": "Richard Torcato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90883"]] + }, + { + "_id": "62f32632082fcc3049e9200d", + "username": "prabhatojha", + "email": "prabhatojha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1726, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e9200f", + "username": "Denis", + "email": "Denis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2371, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92010", + "username": "tslocum", + "email": "tslocum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3422, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088a"]] + }, + { + "_id": "62f32632082fcc3049e92011", + "username": "M. Sherbeeny", + "email": "M. Sherbeeny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed5", "62f321c4082fcc3049e90885"]] + }, + { + "_id": "62f32632082fcc3049e92012", + "username": "Kevin", + "email": "Kevin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7816, + "questionIds": ["62f321bb082fcc3049e8fed5"], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088f"]] + }, + { + "_id": "62f32632082fcc3049e92014", + "username": "Sebastian Rittau", + "email": "Sebastian Rittau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19263, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92016", + "username": "Morgan Cheng", + "email": "Morgan Cheng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71172, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e92019", + "username": "juhan_h", + "email": "juhan_h@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3865, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32632082fcc3049e9201a", + "username": "Pandincus", + "email": "Pandincus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9416, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088b"]] + }, + { + "_id": "62f32632082fcc3049e9201d", + "username": "Ricky", + "email": "Ricky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088c"]] + }, + { + "_id": "62f32633082fcc3049e9201e", + "username": "Rixius", + "email": "Rixius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2103, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088e"]] + }, + { + "_id": "62f32633082fcc3049e92020", + "username": "user1032531", + "email": "user1032531@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23233, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92023", + "username": "Anjana Silva", + "email": "Anjana Silva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6947, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92026", + "username": "AlejandroVD", + "email": "AlejandroVD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1359, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92027", + "username": "Erwin", + "email": "Erwin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9088d"]] + }, + { + "_id": "62f32633082fcc3049e92029", + "username": "aehlke", + "email": "aehlke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14532, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e9202b", + "username": "Abhi Beckert", + "email": "Abhi Beckert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31949, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e9202e", + "username": "svidgen", + "email": "svidgen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13555, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e9202f", + "username": "Eric", + "email": "Eric@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 473, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90891"]] + }, + { + "_id": "62f32633082fcc3049e92031", + "username": "Kevin Meredith", + "email": "Kevin Meredith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39896, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92033", + "username": "rojobuffalo", + "email": "rojobuffalo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2903, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92034", + "username": "MarkPflug", + "email": "MarkPflug@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90890"]] + }, + { + "_id": "62f32633082fcc3049e92035", + "username": "Michael Anderson", + "email": "Michael Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 66890, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90892"]] + }, + { + "_id": "62f32633082fcc3049e92037", + "username": "icktoofay", + "email": "icktoofay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 122948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e92038", + "username": "Corey Richardson", + "email": "Corey Richardson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2987, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90895"]] + }, + { + "_id": "62f32633082fcc3049e9203a", + "username": "NikoKyriakid", + "email": "NikoKyriakid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 565, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32633082fcc3049e9203b", + "username": "Joe Johnson", + "email": "Joe Johnson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1803, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90896"]] + }, + { + "_id": "62f32634082fcc3049e9203d", + "username": "Headbank", + "email": "Headbank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 352, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32634082fcc3049e9203e", + "username": "wayneseymour", + "email": "wayneseymour@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 313, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90897"]] + }, + { + "_id": "62f32634082fcc3049e92040", + "username": "Marthijn", + "email": "Marthijn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3272, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90898"], + ["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e9097a"] + ] + }, + { + "_id": "62f32634082fcc3049e92042", + "username": "DenisS", + "email": "DenisS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1597, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089a"], + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6e"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313f"] + ] + }, + { + "_id": "62f32634082fcc3049e92045", + "username": "user8897421", + "email": "user8897421@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32634082fcc3049e92046", + "username": "bevacqua", + "email": "bevacqua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45846, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e90899"]] + }, + { + "_id": "62f32634082fcc3049e92047", + "username": "sam", + "email": "sam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39328, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089b"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93205"] + ] + }, + { + "_id": "62f32634082fcc3049e92049", + "username": "Stranded Kid", + "email": "Stranded Kid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1387, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32634082fcc3049e9204a", + "username": "raskalbass", + "email": "raskalbass@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 741, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089c"]] + }, + { + "_id": "62f32634082fcc3049e9204c", + "username": "Angelin Nadar", + "email": "Angelin Nadar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8608, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089e"], + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c70"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93141"] + ] + }, + { + "_id": "62f32635082fcc3049e9204d", + "username": "Seti", + "email": "Seti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1983, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e9089f"]] + }, + { + "_id": "62f32635082fcc3049e9204e", + "username": "Val", + "email": "Val@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a2"]] + }, + { + "_id": "62f32635082fcc3049e92050", + "username": "Travis", + "email": "Travis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1164, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a1"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e75"] + ] + }, + { + "_id": "62f32635082fcc3049e92051", + "username": "lzl124631x", + "email": "lzl124631x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a4"]] + }, + { + "_id": "62f32635082fcc3049e92052", + "username": "Mike Clark", + "email": "Mike Clark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1800, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a3"], + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5f"] + ] + }, + { + "_id": "62f32635082fcc3049e92053", + "username": "Marian Klühspies", + "email": "Marian Klühspies@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14195, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a5"]] + }, + { + "_id": "62f32635082fcc3049e92055", + "username": "IliasT", + "email": "IliasT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3565, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a8"]] + }, + { + "_id": "62f32636082fcc3049e92057", + "username": "Aditya Vashishtha", + "email": "Aditya Vashishtha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 169, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908aa"]] + }, + { + "_id": "62f32636082fcc3049e92058", + "username": "blackmiaool", + "email": "blackmiaool@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5164, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908a9"]] + }, + { + "_id": "62f32636082fcc3049e92059", + "username": "Sarkis Arutiunian", + "email": "Sarkis Arutiunian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908ab"]] + }, + { + "_id": "62f32636082fcc3049e9205a", + "username": "Aliaksandr Sushkevich", + "email": "Aliaksandr Sushkevich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10178, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908ac"]] + }, + { + "_id": "62f32636082fcc3049e9205b", + "username": "Krishnadas PC", + "email": "Krishnadas PC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5063, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908ad"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90ddb"] + ] + }, + { + "_id": "62f32636082fcc3049e9205d", + "username": "Ben Racicot", + "email": "Ben Racicot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4587, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32636082fcc3049e9205e", + "username": "Przemek Struciński", + "email": "Przemek Struciński@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4574, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908af"]] + }, + { + "_id": "62f32636082fcc3049e92060", + "username": "CodeDraken", + "email": "CodeDraken@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1005, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908ae"]] + }, + { + "_id": "62f32636082fcc3049e92062", + "username": "aecend", + "email": "aecend@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2327, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32636082fcc3049e92063", + "username": "Sajad Saderi", + "email": "Sajad Saderi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3675, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b2"]] + }, + { + "_id": "62f32636082fcc3049e92065", + "username": "Peter Seliger", + "email": "Peter Seliger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32637082fcc3049e92067", + "username": "Hanzla Habib", + "email": "Hanzla Habib@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2932, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b4"]] + }, + { + "_id": "62f32637082fcc3049e92068", + "username": "ßãlãjî", + "email": "ßãlãjî@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7324, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed9", "62f321c5082fcc3049e908b6"]] + }, + { + "_id": "62f32637082fcc3049e92069", + "username": "Matt Sheppard", + "email": "Matt Sheppard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114329, + "questionIds": ["62f321bb082fcc3049e8fed9"], + "answerIds": [] + }, + { + "_id": "62f32637082fcc3049e9206a", + "username": "mythz", + "email": "mythz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139444, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908b8"]] + }, + { + "_id": "62f32637082fcc3049e9206b", + "username": "zcopley", + "email": "zcopley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 562, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908b9"]] + }, + { + "_id": "62f32637082fcc3049e9206c", + "username": "gavenkoa", + "email": "gavenkoa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42077, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908bb"]] + }, + { + "_id": "62f32637082fcc3049e9206e", + "username": "Undistraction", + "email": "Undistraction@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41093, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32637082fcc3049e92070", + "username": "Rick Hanlon II", + "email": "Rick Hanlon II@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18893, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908bc"]] + }, + { + "_id": "62f32637082fcc3049e92072", + "username": "NoBugs", + "email": "NoBugs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8978, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32637082fcc3049e92074", + "username": "minigeek", + "email": "minigeek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2288, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32638082fcc3049e92075", + "username": "Milen Boev", + "email": "Milen Boev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 571, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908bd"]] + }, + { + "_id": "62f32638082fcc3049e92076", + "username": "wires", + "email": "wires@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4658, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c0"]] + }, + { + "_id": "62f32638082fcc3049e92077", + "username": "Just Jake", + "email": "Just Jake@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4618, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908be"]] + }, + { + "_id": "62f32638082fcc3049e92078", + "username": "adius", + "email": "adius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12138, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908bf"], + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce1"] + ] + }, + { + "_id": "62f32638082fcc3049e92079", + "username": "Kellen Stuart", + "email": "Kellen Stuart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6595, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c3"]] + }, + { + "_id": "62f32638082fcc3049e9207a", + "username": "user5044606", + "email": "user5044606@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c2"]] + }, + { + "_id": "62f32638082fcc3049e9207b", + "username": "Adel MANI", + "email": "Adel MANI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 789, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c1"]] + }, + { + "_id": "62f32638082fcc3049e9207c", + "username": "Charmie", + "email": "Charmie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2137, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c4"]] + }, + { + "_id": "62f32638082fcc3049e9207d", + "username": "everlasto", + "email": "everlasto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4730, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c6"]] + }, + { + "_id": "62f32639082fcc3049e9207e", + "username": "benshope", + "email": "benshope@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2866, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908c7"]] + }, + { + "_id": "62f32639082fcc3049e9207f", + "username": "James Heazlewood", + "email": "James Heazlewood@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 732, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908ca"]] + }, + { + "_id": "62f32639082fcc3049e92081", + "username": "Doomd", + "email": "Doomd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 972, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32639082fcc3049e92082", + "username": "snovelli", + "email": "snovelli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908cb"]] + }, + { + "_id": "62f32639082fcc3049e92083", + "username": "Louie", + "email": "Louie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908cc"]] + }, + { + "_id": "62f32639082fcc3049e92084", + "username": "Teiem", + "email": "Teiem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1001, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908ce"]] + }, + { + "_id": "62f32639082fcc3049e92085", + "username": "Mahdyfo", + "email": "Mahdyfo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908cd"]] + }, + { + "_id": "62f32639082fcc3049e92086", + "username": "fauzimh", + "email": "fauzimh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 416, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908d0"]] + }, + { + "_id": "62f32639082fcc3049e92087", + "username": "Mustak_Talukder", + "email": "Mustak_Talukder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 117, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908cf"]] + }, + { + "_id": "62f3263a082fcc3049e92089", + "username": "avalanche1", + "email": "avalanche1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2353, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3263a082fcc3049e9208a", + "username": "xmoonlight", + "email": "xmoonlight@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feda", "62f321c5082fcc3049e908d1"]] + }, + { + "_id": "62f3263a082fcc3049e9208b", + "username": "Mattias", + "email": "Mattias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5079, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d3"]] + }, + { + "_id": "62f3263a082fcc3049e9208c", + "username": "Oli", + "email": "Oli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 229910, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d4"]] + }, + { + "_id": "62f3263a082fcc3049e9208d", + "username": "pixxaar", + "email": "pixxaar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d6"]] + }, + { + "_id": "62f3263a082fcc3049e9208e", + "username": "Jordan Feldstein", + "email": "Jordan Feldstein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d8"]] + }, + { + "_id": "62f3263a082fcc3049e9208f", + "username": "wbharding", + "email": "wbharding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d7"]] + }, + { + "_id": "62f3263a082fcc3049e92090", + "username": "Techie", + "email": "Techie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43737, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908da"]] + }, + { + "_id": "62f3263a082fcc3049e92092", + "username": "Alessandro Cosentino", + "email": "Alessandro Cosentino@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2090, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3263a082fcc3049e92094", + "username": "olanod", + "email": "olanod@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908d9"]] + }, + { + "_id": "62f3263b082fcc3049e92095", + "username": "farnoush resa", + "email": "farnoush resa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 383, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908dc"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b77"] + ] + }, + { + "_id": "62f3263b082fcc3049e92096", + "username": "user1091949", + "email": "user1091949@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1863, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908dd"]] + }, + { + "_id": "62f3263b082fcc3049e92097", + "username": "tnt-rox", + "email": "tnt-rox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5132, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908df"]] + }, + { + "_id": "62f3263b082fcc3049e92099", + "username": "Zayn Ali", + "email": "Zayn Ali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4455, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e1"]] + }, + { + "_id": "62f3263b082fcc3049e9209a", + "username": "ArtisticPhoenix", + "email": "ArtisticPhoenix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20904, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e0"]] + }, + { + "_id": "62f3263b082fcc3049e9209b", + "username": "Allende", + "email": "Allende@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1460, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e2"]] + }, + { + "_id": "62f3263b082fcc3049e9209d", + "username": "nonchip", + "email": "nonchip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1024, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3263b082fcc3049e9209e", + "username": "Erick Lanford Xenes", + "email": "Erick Lanford Xenes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1376, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e4"]] + }, + { + "_id": "62f3263b082fcc3049e9209f", + "username": "Vivek Aasaithambi", + "email": "Vivek Aasaithambi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 901, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e3"]] + }, + { + "_id": "62f3263c082fcc3049e920a0", + "username": "Siddhartha Chowdhury", + "email": "Siddhartha Chowdhury@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e5"]] + }, + { + "_id": "62f3263c082fcc3049e920a1", + "username": "Daniel Nyamasyo", + "email": "Daniel Nyamasyo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e6"]] + }, + { + "_id": "62f3263c082fcc3049e920a2", + "username": "lat94", + "email": "lat94@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 501, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e8"]] + }, + { + "_id": "62f3263c082fcc3049e920a3", + "username": "MEAbid", + "email": "MEAbid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 540, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908e7"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c08"] + ] + }, + { + "_id": "62f32679082fcc3049e920a6", + "username": "Michael Wang", + "email": "Michael Wang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908ef"]] + }, + { + "_id": "62f32679082fcc3049e920a8", + "username": "Diego Vinícius", + "email": "Diego Vinícius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2004, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908f1"]] + }, + { + "_id": "62f32679082fcc3049e920a9", + "username": "wvandaal", + "email": "wvandaal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3885, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f4"]] + }, + { + "_id": "62f32679082fcc3049e920aa", + "username": "Adjit", + "email": "Adjit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9758, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f5"]] + }, + { + "_id": "62f32679082fcc3049e920ac", + "username": "Mathew MacLean", + "email": "Mathew MacLean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24514, + "questionIds": ["62f321bb082fcc3049e8fedc"], + "answerIds": [] + }, + { + "_id": "62f32679082fcc3049e920af", + "username": "Razvan B.", + "email": "Razvan B.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6581, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f3"]] + }, + { + "_id": "62f32679082fcc3049e920b0", + "username": "DA.", + "email": "DA.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38629, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f6"]] + }, + { + "_id": "62f32679082fcc3049e920b1", + "username": "Sandro Paganotti", + "email": "Sandro Paganotti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2209, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f7"]] + }, + { + "_id": "62f32679082fcc3049e920b3", + "username": "Toothbrush", + "email": "Toothbrush@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2080, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32679082fcc3049e920b4", + "username": "Fiambre", + "email": "Fiambre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1899, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f8"]] + }, + { + "_id": "62f32679082fcc3049e920b7", + "username": "Hrishabh Gupta", + "email": "Hrishabh Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1876, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32679082fcc3049e920b9", + "username": "MStrutt", + "email": "MStrutt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 809, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f9"]] + }, + { + "_id": "62f32679082fcc3049e920bb", + "username": "hooman", + "email": "hooman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 506, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32679082fcc3049e920bd", + "username": "Prisoner", + "email": "Prisoner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26855, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908f2"]] + }, + { + "_id": "62f3267a082fcc3049e920be", + "username": "Shipow", + "email": "Shipow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2421, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908fa"]] + }, + { + "_id": "62f3267a082fcc3049e920bf", + "username": "Sam Tremaine", + "email": "Sam Tremaine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908fb"]] + }, + { + "_id": "62f3267a082fcc3049e920c0", + "username": "Ricardo Zea", + "email": "Ricardo Zea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9665, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908fd"]] + }, + { + "_id": "62f3267a082fcc3049e920c1", + "username": "Ruskin", + "email": "Ruskin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5282, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908fe"]] + }, + { + "_id": "62f3267a082fcc3049e920c2", + "username": "Gauri Bhosle", + "email": "Gauri Bhosle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4325, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e90900"]] + }, + { + "_id": "62f3267a082fcc3049e920c3", + "username": "Sleek Geek", + "email": "Sleek Geek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4470, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908ff"]] + }, + { + "_id": "62f3267a082fcc3049e920c4", + "username": "the Hutt", + "email": "the Hutt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16245, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e90902"]] + }, + { + "_id": "62f3267a082fcc3049e920c5", + "username": "Nic Bell", + "email": "Nic Bell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 503, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedc", "62f321c5082fcc3049e908fc"]] + }, + { + "_id": "62f3267b082fcc3049e920c7", + "username": "Xiè Jìléi", + "email": "Xiè Jìléi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13037, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267b082fcc3049e920c9", + "username": "Jay Taylor", + "email": "Jay Taylor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267b082fcc3049e920cb", + "username": "thejonwithnoh", + "email": "thejonwithnoh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 612, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267b082fcc3049e920cc", + "username": "ephemient", + "email": "ephemient@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 191438, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90904"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90da9"] + ] + }, + { + "_id": "62f3267b082fcc3049e920cd", + "username": "Avdi", + "email": "Avdi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18230, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90905"]] + }, + { + "_id": "62f3267b082fcc3049e920cf", + "username": "skerit", + "email": "skerit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267b082fcc3049e920d0", + "username": "Markus", + "email": "Markus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90906"]] + }, + { + "_id": "62f3267b082fcc3049e920d1", + "username": "David Coallier", + "email": "David Coallier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 106, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090b"]] + }, + { + "_id": "62f3267b082fcc3049e920d2", + "username": "philfreo", + "email": "philfreo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39635, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90909"]] + }, + { + "_id": "62f3267b082fcc3049e920d3", + "username": "Algy", + "email": "Algy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090a"]] + }, + { + "_id": "62f3267b082fcc3049e920d4", + "username": "antony", + "email": "antony@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2603, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090c"]] + }, + { + "_id": "62f3267c082fcc3049e920d5", + "username": "user909278", + "email": "user909278@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9090f"]] + }, + { + "_id": "62f3267c082fcc3049e920d7", + "username": "Prabhakar Kasi", + "email": "Prabhakar Kasi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 511, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90912"]] + }, + { + "_id": "62f3267c082fcc3049e920d8", + "username": "Emre Erkan", + "email": "Emre Erkan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90911"]] + }, + { + "_id": "62f3267c082fcc3049e920d9", + "username": "Industrial", + "email": "Industrial@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39450, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90913"]] + }, + { + "_id": "62f3267c082fcc3049e920da", + "username": "Michael Benin", + "email": "Michael Benin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4169, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90915"]] + }, + { + "_id": "62f3267c082fcc3049e920db", + "username": "Paweł Szczur", + "email": "Paweł Szczur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5325, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90914"]] + }, + { + "_id": "62f3267c082fcc3049e920dc", + "username": "Andreas Linden", + "email": "Andreas Linden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12263, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90916"]] + }, + { + "_id": "62f3267d082fcc3049e920dd", + "username": "pagid", + "email": "pagid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13023, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90918"]] + }, + { + "_id": "62f3267d082fcc3049e920df", + "username": "jleviaguirre", + "email": "jleviaguirre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 646, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90919"]] + }, + { + "_id": "62f3267d082fcc3049e920e0", + "username": "Paul Spaulding", + "email": "Paul Spaulding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1061, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091a"]] + }, + { + "_id": "62f3267d082fcc3049e920e1", + "username": "korywka", + "email": "korywka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7070, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091c"]] + }, + { + "_id": "62f3267d082fcc3049e920e2", + "username": "Paul", + "email": "Paul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 161, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091b"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4d"], + ["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b65"], + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c49"] + ] + }, + { + "_id": "62f3267d082fcc3049e920e3", + "username": "AndreasE", + "email": "AndreasE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 434, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091e"]] + }, + { + "_id": "62f3267d082fcc3049e920e4", + "username": "Egor Kloos", + "email": "Egor Kloos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 65, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091d"]] + }, + { + "_id": "62f3267d082fcc3049e920e5", + "username": "devmao", + "email": "devmao@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 650, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9091f"]] + }, + { + "_id": "62f3267d082fcc3049e920e7", + "username": "ItalyPaleAle", + "email": "ItalyPaleAle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7055, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267d082fcc3049e920e8", + "username": "Ryan Walls", + "email": "Ryan Walls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90920"]] + }, + { + "_id": "62f3267e082fcc3049e920ea", + "username": "Joachim Lous", + "email": "Joachim Lous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1086, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267e082fcc3049e920eb", + "username": "NanoWizard", + "email": "NanoWizard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1934, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90922"]] + }, + { + "_id": "62f3267e082fcc3049e920ec", + "username": "antitoxic", + "email": "antitoxic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3716, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90921"]] + }, + { + "_id": "62f3267e082fcc3049e920ed", + "username": "appsmatics", + "email": "appsmatics@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 639, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90923"]] + }, + { + "_id": "62f3267e082fcc3049e920ee", + "username": "Dinusha", + "email": "Dinusha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 688, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90926"]] + }, + { + "_id": "62f3267e082fcc3049e920ef", + "username": "Etherealone", + "email": "Etherealone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3399, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90925"]] + }, + { + "_id": "62f3267e082fcc3049e920f0", + "username": "Vikash Pandey", + "email": "Vikash Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90927"]] + }, + { + "_id": "62f3267e082fcc3049e920f1", + "username": "Reza Roshan", + "email": "Reza Roshan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90929"]] + }, + { + "_id": "62f3267e082fcc3049e920f2", + "username": "Sherali Turdiyev", + "email": "Sherali Turdiyev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1655, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90928"]] + }, + { + "_id": "62f3267f082fcc3049e920f3", + "username": "Raphaël", + "email": "Raphaël@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1727, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092b"]] + }, + { + "_id": "62f3267f082fcc3049e920f4", + "username": "Yaki Klein", + "email": "Yaki Klein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3510, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092c"]] + }, + { + "_id": "62f3267f082fcc3049e920f5", + "username": "trincot", + "email": "trincot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 274347, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092e"]] + }, + { + "_id": "62f3267f082fcc3049e920f8", + "username": "connexo", + "email": "connexo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49956, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267f082fcc3049e920fa", + "username": "klewis", + "email": "klewis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6803, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267f082fcc3049e920fc", + "username": "Merrin K", + "email": "Merrin K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1334, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3267f082fcc3049e920fd", + "username": "Jaime Asm", + "email": "Jaime Asm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9092f"]] + }, + { + "_id": "62f3267f082fcc3049e920fe", + "username": "mitch3ls", + "email": "mitch3ls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90930"]] + }, + { + "_id": "62f3267f082fcc3049e920ff", + "username": "toster-cx", + "email": "toster-cx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2165, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90931"]] + }, + { + "_id": "62f3267f082fcc3049e92100", + "username": "Legends", + "email": "Legends@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19457, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90932"]] + }, + { + "_id": "62f3267f082fcc3049e92101", + "username": "aircraft", + "email": "aircraft@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90934"]] + }, + { + "_id": "62f3267f082fcc3049e92102", + "username": "Logan", + "email": "Logan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90933"]] + }, + { + "_id": "62f32680082fcc3049e92104", + "username": "Shilpe Saxena", + "email": "Shilpe Saxena@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32680082fcc3049e92105", + "username": "gildniy", + "email": "gildniy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2708, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90936"]] + }, + { + "_id": "62f32680082fcc3049e92106", + "username": "Tejas Savaliya", + "email": "Tejas Savaliya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 406, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90937"]] + }, + { + "_id": "62f32680082fcc3049e92107", + "username": "Paweł Otto", + "email": "Paweł Otto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90938"]] + }, + { + "_id": "62f32680082fcc3049e92108", + "username": "space97", + "email": "space97@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093a"]] + }, + { + "_id": "62f32680082fcc3049e9210a", + "username": "Kavale arun", + "email": "Kavale arun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 511, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093c"]] + }, + { + "_id": "62f32680082fcc3049e9210b", + "username": "Piyush Rana", + "email": "Piyush Rana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 526, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093e"]] + }, + { + "_id": "62f32680082fcc3049e9210c", + "username": "Soura Ghosh", + "email": "Soura Ghosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 825, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093d"], + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4f"] + ] + }, + { + "_id": "62f32681082fcc3049e9210d", + "username": "kokhta shukvani", + "email": "kokhta shukvani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 92, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e9093f"]] + }, + { + "_id": "62f32681082fcc3049e9210e", + "username": "Mehadi Hassan", + "email": "Mehadi Hassan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1090, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90940"]] + }, + { + "_id": "62f32681082fcc3049e9210f", + "username": "dunubh", + "email": "dunubh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedd", "62f321c5082fcc3049e90943"]] + }, + { + "_id": "62f32681082fcc3049e92110", + "username": "JC Grubbs", + "email": "JC Grubbs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37261, + "questionIds": ["62f321bb082fcc3049e8fedd"], + "answerIds": [] + }, + { + "_id": "62f32681082fcc3049e92112", + "username": "trinalbadger587", + "email": "trinalbadger587@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1603, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32681082fcc3049e92114", + "username": "Peter J", + "email": "Peter J@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90944"]] + }, + { + "_id": "62f32681082fcc3049e92115", + "username": "Joberror", + "email": "Joberror@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5730, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90946"]] + }, + { + "_id": "62f32681082fcc3049e92116", + "username": "Francisco Alvarado", + "email": "Francisco Alvarado@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2807, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90947"]] + }, + { + "_id": "62f32681082fcc3049e92117", + "username": "Phillip Senn", + "email": "Phillip Senn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45263, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90948"]] + }, + { + "_id": "62f32682082fcc3049e92118", + "username": "Matt Browne", + "email": "Matt Browne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11743, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90949"]] + }, + { + "_id": "62f32682082fcc3049e9211a", + "username": "ajzbrun", + "email": "ajzbrun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 87, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32682082fcc3049e9211b", + "username": "Cam Tullos", + "email": "Cam Tullos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2467, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094a"]] + }, + { + "_id": "62f32682082fcc3049e9211c", + "username": "Alex V", + "email": "Alex V@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17858, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094b"]] + }, + { + "_id": "62f32682082fcc3049e9211e", + "username": "RedDragon", + "email": "RedDragon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1951, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094c"]] + }, + { + "_id": "62f32682082fcc3049e9211f", + "username": "Ramesh", + "email": "Ramesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094e"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93200"] + ] + }, + { + "_id": "62f32682082fcc3049e92120", + "username": "Swadesh Bhattacharya", + "email": "Swadesh Bhattacharya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9094f"]] + }, + { + "_id": "62f32682082fcc3049e92121", + "username": "Darin Peterson", + "email": "Darin Peterson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1222, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90951"]] + }, + { + "_id": "62f32682082fcc3049e92122", + "username": "Rodrigo Dias", + "email": "Rodrigo Dias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90952"]] + }, + { + "_id": "62f32682082fcc3049e92123", + "username": "rmbianchi", + "email": "rmbianchi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90950"]] + }, + { + "_id": "62f32683082fcc3049e92124", + "username": "Mathias Lykkegaard Lorenzen", + "email": "Mathias Lykkegaard Lorenzen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90953"]] + }, + { + "_id": "62f32683082fcc3049e92125", + "username": "jeswin", + "email": "jeswin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 374, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90954"]] + }, + { + "_id": "62f32683082fcc3049e92126", + "username": "Randy Greencorn", + "email": "Randy Greencorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3764, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90955"]] + }, + { + "_id": "62f32683082fcc3049e92127", + "username": "Juan", + "email": "Juan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 993, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90956"]] + }, + { + "_id": "62f326c0082fcc3049e92128", + "username": "user1585204", + "email": "user1585204@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 719, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095e"]] + }, + { + "_id": "62f326c0082fcc3049e92129", + "username": "Mehdi Bouzidi", + "email": "Mehdi Bouzidi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1907, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095f"]] + }, + { + "_id": "62f326c0082fcc3049e9212a", + "username": "fitorec", + "email": "fitorec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90961"]] + }, + { + "_id": "62f326c0082fcc3049e9212b", + "username": "Iyyappan Amirthalingam", + "email": "Iyyappan Amirthalingam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 194, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90962"]] + }, + { + "_id": "62f326c0082fcc3049e9212c", + "username": "Samir Lakhani", + "email": "Samir Lakhani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 665, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90965"]] + }, + { + "_id": "62f326c1082fcc3049e9212d", + "username": "salman ifrahim", + "email": "salman ifrahim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 193, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90967"]] + }, + { + "_id": "62f326c1082fcc3049e92130", + "username": "Guffa", + "email": "Guffa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 670079, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90971"]] + }, + { + "_id": "62f326c1082fcc3049e92134", + "username": "Jacob Relkin", + "email": "Jacob Relkin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 157575, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90970"]] + }, + { + "_id": "62f326c1082fcc3049e9213a", + "username": "Anurag", + "email": "Anurag@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137738, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90973"]] + }, + { + "_id": "62f326c1082fcc3049e9213d", + "username": "makerofthings7", + "email": "makerofthings7@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58199, + "questionIds": ["62f321bb082fcc3049e8fee1"], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e9213f", + "username": "MooGoo", + "email": "MooGoo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44760, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a8f"]] + }, + { + "_id": "62f326c1082fcc3049e92144", + "username": "Jonathan DS", + "email": "Jonathan DS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1944, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e92146", + "username": "Paul S.", + "email": "Paul S.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 62154, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e92148", + "username": "andig", + "email": "andig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12820, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e9214b", + "username": "asifaftab87", + "email": "asifaftab87@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1225, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e9214d", + "username": "Diego Faria", + "email": "Diego Faria@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8504, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e92150", + "username": "Jamie Pate", + "email": "Jamie Pate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1592, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e92153", + "username": "Cristian Sanchez", + "email": "Cristian Sanchez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29475, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90974"]] + }, + { + "_id": "62f326c1082fcc3049e92156", + "username": "TheZ", + "email": "TheZ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3583, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e92158", + "username": "Spongman", + "email": "Spongman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9127, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e9215e", + "username": "Mahdi Khalili", + "email": "Mahdi Khalili@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 963, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c1082fcc3049e9215f", + "username": "anmarti", + "email": "anmarti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4955, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90977"]] + }, + { + "_id": "62f326c2082fcc3049e92161", + "username": "Zenexer", + "email": "Zenexer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17949, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e90979"]] + }, + { + "_id": "62f326c2082fcc3049e92163", + "username": "FruitBreak", + "email": "FruitBreak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 570, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c2082fcc3049e92164", + "username": "Hrishi", + "email": "Hrishi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7080, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e9097b"]] + }, + { + "_id": "62f326c2082fcc3049e92166", + "username": "Brian M. Hunt", + "email": "Brian M. Hunt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77166, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c2082fcc3049e92169", + "username": "Kamafeather", + "email": "Kamafeather@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7358, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c2082fcc3049e9216a", + "username": "Joseph Gabriel", + "email": "Joseph Gabriel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8085, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e9097c"]] + }, + { + "_id": "62f326c2082fcc3049e9216b", + "username": "sourcecode", + "email": "sourcecode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3678, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee1", "62f321c5082fcc3049e9097e"]] + }, + { + "_id": "62f326c2082fcc3049e9216c", + "username": "rjmunro", + "email": "rjmunro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90981"]] + }, + { + "_id": "62f326c2082fcc3049e9216f", + "username": "Paul Draper", + "email": "Paul Draper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 72752, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c2082fcc3049e92170", + "username": "Jens Roland", + "email": "Jens Roland@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27112, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90980"]] + }, + { + "_id": "62f326c2082fcc3049e92171", + "username": "MK_Dev", + "email": "MK_Dev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9097f"]] + }, + { + "_id": "62f326c3082fcc3049e92172", + "username": "Mαzen", + "email": "Mαzen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 535, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90982"]] + }, + { + "_id": "62f326c3082fcc3049e92174", + "username": "Gabriel Littman", + "email": "Gabriel Littman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 532, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c3082fcc3049e92177", + "username": "Omnimike", + "email": "Omnimike@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90984"]] + }, + { + "_id": "62f326c3082fcc3049e92178", + "username": "yoel halb", + "email": "yoel halb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11562, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90983"]] + }, + { + "_id": "62f326c3082fcc3049e92179", + "username": "Pawan Singh", + "email": "Pawan Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1237, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90986"]] + }, + { + "_id": "62f326c3082fcc3049e9217b", + "username": "amit bakle", + "email": "amit bakle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3233, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c3082fcc3049e9217d", + "username": "miike3459", + "email": "miike3459@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1401, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c3082fcc3049e9217e", + "username": "9ete", + "email": "9ete@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3622, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90987"]] + }, + { + "_id": "62f326c3082fcc3049e9217f", + "username": "Maarten Peels", + "email": "Maarten Peels@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 942, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90988"]] + }, + { + "_id": "62f326c3082fcc3049e92180", + "username": "Danil Gaponov", + "email": "Danil Gaponov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1393, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098a"]] + }, + { + "_id": "62f326c3082fcc3049e92181", + "username": "CodingIntrigue", + "email": "CodingIntrigue@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 72057, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90989"]] + }, + { + "_id": "62f326c3082fcc3049e92182", + "username": "Karl", + "email": "Karl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1712, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098b"]] + }, + { + "_id": "62f326c4082fcc3049e92183", + "username": "Downhillski", + "email": "Downhillski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2429, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098c"]] + }, + { + "_id": "62f326c4082fcc3049e92184", + "username": "Satyapriya Mishra", + "email": "Satyapriya Mishra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 130, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098d"]] + }, + { + "_id": "62f326c4082fcc3049e92185", + "username": "Emil Reña Enriquez", + "email": "Emil Reña Enriquez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2762, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098e"]] + }, + { + "_id": "62f326c4082fcc3049e92186", + "username": "JmLavoier", + "email": "JmLavoier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 473, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9098f"]] + }, + { + "_id": "62f326c4082fcc3049e92187", + "username": "Jayme", + "email": "Jayme@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1638, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90993"]] + }, + { + "_id": "62f326c4082fcc3049e92188", + "username": "Sanu Uthaiah Bollera", + "email": "Sanu Uthaiah Bollera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 915, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90995"], + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b4"] + ] + }, + { + "_id": "62f326c4082fcc3049e9218a", + "username": "Hoque MD Zahidul", + "email": "Hoque MD Zahidul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8646, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90994"]] + }, + { + "_id": "62f326c5082fcc3049e9218c", + "username": "monikapatelIT", + "email": "monikapatelIT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 930, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c5082fcc3049e9218d", + "username": "Flavio Copes", + "email": "Flavio Copes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e90998"]] + }, + { + "_id": "62f326c5082fcc3049e9218f", + "username": "Mihail Malostanidis", + "email": "Mihail Malostanidis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2422, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c5082fcc3049e92191", + "username": "Aditya", + "email": "Aditya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 728, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee2", "62f321c5082fcc3049e9099b"]] + }, + { + "_id": "62f326c5082fcc3049e92192", + "username": "interstar", + "email": "interstar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24708, + "questionIds": ["62f321bb082fcc3049e8fee2"], + "answerIds": [] + }, + { + "_id": "62f326c5082fcc3049e92193", + "username": "Jon Erickson", + "email": "Jon Erickson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 108452, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e9099e"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e31"] + ] + }, + { + "_id": "62f326c5082fcc3049e92195", + "username": "Devon", + "email": "Devon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5746, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e9099d"]] + }, + { + "_id": "62f326c5082fcc3049e92198", + "username": "Jake McGraw", + "email": "Jake McGraw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54608, + "questionIds": ["62f321bb082fcc3049e8fede"], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e9099c"]] + }, + { + "_id": "62f326c5082fcc3049e92199", + "username": "Mad_Hat", + "email": "Mad_Hat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e9099f"]] + }, + { + "_id": "62f326c6082fcc3049e9219b", + "username": "Th4t Guy", + "email": "Th4t Guy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1444, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e9219d", + "username": "Jeremy W", + "email": "Jeremy W@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1791, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e9219f", + "username": "Robert", + "email": "Robert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2555, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921a2", + "username": "Ismael Miguel", + "email": "Ismael Miguel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4031, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921a4", + "username": "user1280483", + "email": "user1280483@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 449, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921a6", + "username": "Parzh from Ukraine", + "email": "Parzh from Ukraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6610, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921a7", + "username": "Tim Büthe", + "email": "Tim Büthe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61526, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a0"]] + }, + { + "_id": "62f326c6082fcc3049e921a8", + "username": "amypellegrini", + "email": "amypellegrini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1026, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a2"]] + }, + { + "_id": "62f326c6082fcc3049e921aa", + "username": "Blue Skies", + "email": "Blue Skies@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2885, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921ab", + "username": "jcreamer898", + "email": "jcreamer898@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8009, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a5"]] + }, + { + "_id": "62f326c6082fcc3049e921ad", + "username": "nnnnnn", + "email": "nnnnnn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 144076, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921af", + "username": "Uyghur Lives Matter", + "email": "Uyghur Lives Matter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17587, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921b1", + "username": "Oleg", + "email": "Oleg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9181, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a4"]] + }, + { + "_id": "62f326c6082fcc3049e921b2", + "username": "andy_314", + "email": "andy_314@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 424, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a7"]] + }, + { + "_id": "62f326c6082fcc3049e921b4", + "username": "user1106925", + "email": "user1106925@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921b5", + "username": "hiway", + "email": "hiway@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a9"]] + }, + { + "_id": "62f326c6082fcc3049e921b6", + "username": "王奕然", + "email": "王奕然@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3601, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a8"]] + }, + { + "_id": "62f326c6082fcc3049e921b8", + "username": "Emile Bergeron", + "email": "Emile Bergeron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16268, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921ba", + "username": "user7892745", + "email": "user7892745@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c6082fcc3049e921bc", + "username": "Tilak Madichetti", + "email": "Tilak Madichetti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3565, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b9"]] + }, + { + "_id": "62f326c6082fcc3049e921bd", + "username": "SJG", + "email": "SJG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1723, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909a6"]] + }, + { + "_id": "62f326c7082fcc3049e921be", + "username": "MAX POWER", + "email": "MAX POWER@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5047, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909ab"]] + }, + { + "_id": "62f326c7082fcc3049e921bf", + "username": "technosaurus", + "email": "technosaurus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7411, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909ad"]] + }, + { + "_id": "62f326c7082fcc3049e921c1", + "username": "Eternal1", + "email": "Eternal1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5257, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909ac"]] + }, + { + "_id": "62f326c7082fcc3049e921c3", + "username": "Jarvl", + "email": "Jarvl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c7082fcc3049e921c5", + "username": "Santiago Hernández", + "email": "Santiago Hernández@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5043, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909ae"]] + }, + { + "_id": "62f326c7082fcc3049e921c6", + "username": "Oliver", + "email": "Oliver@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8913, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b1"], + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e22"] + ] + }, + { + "_id": "62f326c7082fcc3049e921c8", + "username": "Pranav Labhe", + "email": "Pranav Labhe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1873, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c7082fcc3049e921ca", + "username": "Anurag Deokar", + "email": "Anurag Deokar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 830, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909af"]] + }, + { + "_id": "62f326c7082fcc3049e921cb", + "username": "ducdhm", + "email": "ducdhm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1919, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b2"]] + }, + { + "_id": "62f326c8082fcc3049e921cd", + "username": "Abdur Rehman", + "email": "Abdur Rehman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3122, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c8082fcc3049e921ce", + "username": "abhirathore2006", + "email": "abhirathore2006@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3071, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b6"]] + }, + { + "_id": "62f326c8082fcc3049e921cf", + "username": "Jonathan Cardoz", + "email": "Jonathan Cardoz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 784, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909b7"]] + }, + { + "_id": "62f326c8082fcc3049e921d1", + "username": "Roland", + "email": "Roland@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 167, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326c8082fcc3049e921d2", + "username": "sina_Islam", + "email": "sina_Islam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 862, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909bc"]] + }, + { + "_id": "62f326c8082fcc3049e921d3", + "username": "Jonas Lundman", + "email": "Jonas Lundman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909bd"]] + }, + { + "_id": "62f326c9082fcc3049e921d4", + "username": "Hassan Sadeghi", + "email": "Hassan Sadeghi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1316, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909bf"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0d"] + ] + }, + { + "_id": "62f326c9082fcc3049e921d5", + "username": "Manish Vadher", + "email": "Manish Vadher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1436, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909be"]] + }, + { + "_id": "62f326c9082fcc3049e921d6", + "username": "Rafal Enden", + "email": "Rafal Enden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2738, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c0"]] + }, + { + "_id": "62f326c9082fcc3049e921d7", + "username": "Greedo", + "email": "Greedo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3279, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c5"]] + }, + { + "_id": "62f326c9082fcc3049e921d9", + "username": "Amit Sharma", + "email": "Amit Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 461, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c4"]] + }, + { + "_id": "62f326c9082fcc3049e921da", + "username": "Haroon Fayyaz", + "email": "Haroon Fayyaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c7"]] + }, + { + "_id": "62f326c9082fcc3049e921db", + "username": "Gareth Compton", + "email": "Gareth Compton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c6"]] + }, + { + "_id": "62f326ca082fcc3049e921dc", + "username": "Vishwesh Chotaliya", + "email": "Vishwesh Chotaliya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fede", "62f321c5082fcc3049e909c8"]] + }, + { + "_id": "62f326ca082fcc3049e921de", + "username": "oligofren", + "email": "oligofren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18447, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921e0", + "username": "Ashish Negi", + "email": "Ashish Negi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4973, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921e3", + "username": "Fredrick", + "email": "Fredrick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 378, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921e5", + "username": "Sethen", + "email": "Sethen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10790, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921e6", + "username": "Justin Voskuhl", + "email": "Justin Voskuhl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2812, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909ca"]] + }, + { + "_id": "62f326ca082fcc3049e921e7", + "username": "aster_x", + "email": "aster_x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909cd"]] + }, + { + "_id": "62f326ca082fcc3049e921e9", + "username": "Jimmy T.", + "email": "Jimmy T.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3873, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921eb", + "username": "Alex Grande", + "email": "Alex Grande@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7801, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909cc"]] + }, + { + "_id": "62f326ca082fcc3049e921ed", + "username": "Ezeke", + "email": "Ezeke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 942, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921ef", + "username": "Jordan Arseno", + "email": "Jordan Arseno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6832, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ca082fcc3049e921f0", + "username": "Guria", + "email": "Guria@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6878, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909cb"]] + }, + { + "_id": "62f326ca082fcc3049e921f1", + "username": "Andy Lorenz", + "email": "Andy Lorenz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2619, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909cf"]] + }, + { + "_id": "62f326ca082fcc3049e921f3", + "username": "JProgrammer", + "email": "JProgrammer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2720, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909ce"]] + }, + { + "_id": "62f326ca082fcc3049e921f4", + "username": "Adrian May", + "email": "Adrian May@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2021, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d0"]] + }, + { + "_id": "62f326ca082fcc3049e921f7", + "username": "Nadu", + "email": "Nadu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2284, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d1"]] + }, + { + "_id": "62f326cb082fcc3049e921f8", + "username": "doublejosh", + "email": "doublejosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d2"]] + }, + { + "_id": "62f326cb082fcc3049e921fa", + "username": "Rudie", + "email": "Rudie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d3"]] + }, + { + "_id": "62f326cb082fcc3049e921fb", + "username": "Mac", + "email": "Mac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1259, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d7"]] + }, + { + "_id": "62f326cb082fcc3049e921fd", + "username": "Moshiur Rahman", + "email": "Moshiur Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1432, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d9"]] + }, + { + "_id": "62f326cb082fcc3049e921fe", + "username": "Flavien Volken", + "email": "Flavien Volken@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16938, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d8"]] + }, + { + "_id": "62f326cb082fcc3049e921ff", + "username": "Gabriel H", + "email": "Gabriel H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1462, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909db"]] + }, + { + "_id": "62f326cb082fcc3049e92201", + "username": "Kristopher Johnson", + "email": "Kristopher Johnson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79616, + "questionIds": ["62f321bb082fcc3049e8fee0"], + "answerIds": [] + }, + { + "_id": "62f326cb082fcc3049e92202", + "username": "manasa woddeyar manu", + "email": "manasa woddeyar manu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909da"]] + }, + { + "_id": "62f326cb082fcc3049e92203", + "username": "zevero", + "email": "zevero@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2124, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909d6"]] + }, + { + "_id": "62f326cc082fcc3049e92205", + "username": "Akash Aher", + "email": "Akash Aher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 257, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee0", "62f321c6082fcc3049e909dd"]] + }, + { + "_id": "62f326cc082fcc3049e92207", + "username": "Robin Rodricks", + "email": "Robin Rodricks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 106173, + "questionIds": ["62f321bb082fcc3049e8fee5"], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e9220b", + "username": "Spectric", + "email": "Spectric@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27859, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e9220d", + "username": "Vivart", + "email": "Vivart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14284, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f0"]] + }, + { + "_id": "62f326cc082fcc3049e92210", + "username": "noisy", + "email": "noisy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6236, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e92212", + "username": "MLK.DEV", + "email": "MLK.DEV@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 453, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e92214", + "username": "catbadger", + "email": "catbadger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1561, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e92216", + "username": "Math Coder 101", + "email": "Math Coder 101@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 145, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e92218", + "username": "Edison Pebojot", + "email": "Edison Pebojot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 288, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e9221a", + "username": "DeeStarks", + "email": "DeeStarks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 192, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e9221c", + "username": "sshanzel", + "email": "sshanzel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 349, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cc082fcc3049e9221d", + "username": "David Murdoch", + "email": "David Murdoch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85813, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909ef"]] + }, + { + "_id": "62f326cc082fcc3049e9221e", + "username": "Jeremy Warne", + "email": "Jeremy Warne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3367, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f1"]] + }, + { + "_id": "62f326cc082fcc3049e9221f", + "username": "George Filippakos", + "email": "George Filippakos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15779, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f2"]] + }, + { + "_id": "62f326cd082fcc3049e92220", + "username": "Erenor Paz", + "email": "Erenor Paz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2881, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f4"]] + }, + { + "_id": "62f326cd082fcc3049e92221", + "username": "Thomas Stjernegaard Jeppesen", + "email": "Thomas Stjernegaard Jeppesen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 384, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f3"]] + }, + { + "_id": "62f326cd082fcc3049e92223", + "username": "Craig Jacobs", + "email": "Craig Jacobs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 910, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e92225", + "username": "kkatusic", + "email": "kkatusic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e92226", + "username": "Haimei", + "email": "Haimei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12085, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f6"]] + }, + { + "_id": "62f326cd082fcc3049e92227", + "username": "Prathamesh Rasam", + "email": "Prathamesh Rasam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 416, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f7"]] + }, + { + "_id": "62f326cd082fcc3049e92228", + "username": "Shine", + "email": "Shine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 862, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f5"]] + }, + { + "_id": "62f326cd082fcc3049e92229", + "username": "Suraj", + "email": "Suraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2083, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909f8"]] + }, + { + "_id": "62f326cd082fcc3049e9222b", + "username": "BIOHAZARD", + "email": "BIOHAZARD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1839, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e9222d", + "username": "xeruf", + "email": "xeruf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2075, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e9222e", + "username": "Dheeraj Thedijje", + "email": "Dheeraj Thedijje@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 977, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909fa"]] + }, + { + "_id": "62f326cd082fcc3049e92230", + "username": "J0ANMM", + "email": "J0ANMM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7013, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e92232", + "username": "MD. Atiqur Rahman", + "email": "MD. Atiqur Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1915, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e92234", + "username": "Johann", + "email": "Johann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24657, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326cd082fcc3049e92236", + "username": "Vinay Kaithwas", + "email": "Vinay Kaithwas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909fc"]] + }, + { + "_id": "62f326ce082fcc3049e92238", + "username": "Webdeveloper011", + "email": "Webdeveloper011@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 83, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909fd"]] + }, + { + "_id": "62f326ce082fcc3049e9223a", + "username": "Stefan Reich", + "email": "Stefan Reich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 920, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9223b", + "username": "James Bond", + "email": "James Bond@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2565, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee5", "62f321c6082fcc3049e909fe"]] + }, + { + "_id": "62f326ce082fcc3049e9223d", + "username": "Ian Boyd", + "email": "Ian Boyd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 236159, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9223e", + "username": "Mike Samuel", + "email": "Mike Samuel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114579, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a00"]] + }, + { + "_id": "62f326ce082fcc3049e92241", + "username": "Aaron Gray", + "email": "Aaron Gray@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10653, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92243", + "username": "Brad", + "email": "Brad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15084, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92245", + "username": "BentOnCoding", + "email": "BentOnCoding@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26126, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92247", + "username": "x0n", + "email": "x0n@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49908, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9224a", + "username": "iaforek", + "email": "iaforek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2504, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9224c", + "username": "Sam Alexander", + "email": "Sam Alexander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 595, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9224e", + "username": "user378380", + "email": "user378380@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 751, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92250", + "username": "Ed of the Mountain", + "email": "Ed of the Mountain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4784, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92252", + "username": "Nicholas Hamilton", + "email": "Nicholas Hamilton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9565, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92254", + "username": "Dulguun Otgon", + "email": "Dulguun Otgon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1875, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92256", + "username": "Leon", + "email": "Leon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 386, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9225a", + "username": "fcaserio", + "email": "fcaserio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 648, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9225d", + "username": "Switch386", + "email": "Switch386@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 287, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e9225f", + "username": "AndreasS", + "email": "AndreasS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 305, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f326ce082fcc3049e92261", + "username": "Phate", + "email": "Phate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5524, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e92263", + "username": "Allen Linatoc", + "email": "Allen Linatoc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 604, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e92264", + "username": "Arif", + "email": "Arif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5434, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a07"], + ["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320f"] + ] + }, + { + "_id": "62f3270b082fcc3049e92266", + "username": "GollyJer", + "email": "GollyJer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19620, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e92268", + "username": "Vix", + "email": "Vix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 968, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a08"]] + }, + { + "_id": "62f3270b082fcc3049e9226a", + "username": "Gorky", + "email": "Gorky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1283, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e9226b", + "username": "JerryP", + "email": "JerryP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 451, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a09"]] + }, + { + "_id": "62f3270b082fcc3049e9226d", + "username": "kalyanbk", + "email": "kalyanbk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e9226f", + "username": "GFoley83", + "email": "GFoley83@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3399, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270b082fcc3049e92271", + "username": "rickdog", + "email": "rickdog@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 718, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0c"]] + }, + { + "_id": "62f3270b082fcc3049e92272", + "username": "Pedro Pereira", + "email": "Pedro Pereira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 470, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0b"]] + }, + { + "_id": "62f3270b082fcc3049e92273", + "username": "Mark Giblin", + "email": "Mark Giblin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1028, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0e"]] + }, + { + "_id": "62f3270b082fcc3049e92274", + "username": "Hriju", + "email": "Hriju@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 680, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0d"]] + }, + { + "_id": "62f3270b082fcc3049e92275", + "username": "Keith Blanchard", + "email": "Keith Blanchard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 553, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a10"]] + }, + { + "_id": "62f3270b082fcc3049e92276", + "username": "Luca C.", + "email": "Luca C.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10256, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a0f"]] + }, + { + "_id": "62f3270c082fcc3049e92277", + "username": "centurian", + "email": "centurian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1170, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a11"]] + }, + { + "_id": "62f3270c082fcc3049e92279", + "username": "JoshKisb", + "email": "JoshKisb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 743, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e9227b", + "username": "ddagsan", + "email": "ddagsan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1678, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a12"]] + }, + { + "_id": "62f3270c082fcc3049e9227e", + "username": "Nick stands with Ukraine", + "email": "Nick stands with Ukraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6455, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e9227f", + "username": "GorvGoyl", + "email": "GorvGoyl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34709, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a13"]] + }, + { + "_id": "62f3270c082fcc3049e92280", + "username": "jonathan klevin", + "email": "jonathan klevin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 165, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a14"]] + }, + { + "_id": "62f3270c082fcc3049e92281", + "username": "Ravikant", + "email": "Ravikant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a15"]] + }, + { + "_id": "62f3270c082fcc3049e92283", + "username": "Fabian von Ellerts", + "email": "Fabian von Ellerts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4197, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e92284", + "username": "jales cardoso", + "email": "jales cardoso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a16"]] + }, + { + "_id": "62f3270c082fcc3049e92286", + "username": "Gufran Hasan", + "email": "Gufran Hasan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8302, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e92288", + "username": "nobjta_9x_tq", + "email": "nobjta_9x_tq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a18"]] + }, + { + "_id": "62f3270c082fcc3049e9228a", + "username": "BlackBeard", + "email": "BlackBeard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9640, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a17"], + ["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c27"], + ["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908ea"] + ] + }, + { + "_id": "62f3270c082fcc3049e9228c", + "username": "ticktock", + "email": "ticktock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1543, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e9228d", + "username": "Alexandre Magro", + "email": "Alexandre Magro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 993, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1a"]] + }, + { + "_id": "62f3270c082fcc3049e9228f", + "username": "Molomby", + "email": "Molomby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4933, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270c082fcc3049e92291", + "username": "cubefox", + "email": "cubefox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1129, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a19"]] + }, + { + "_id": "62f3270d082fcc3049e92292", + "username": "yaserso", + "email": "yaserso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1b"]] + }, + { + "_id": "62f3270d082fcc3049e92293", + "username": "Narasimha Reddy - Geeker", + "email": "Narasimha Reddy - Geeker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3164, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1c"]] + }, + { + "_id": "62f3270d082fcc3049e92294", + "username": "Daniel Delgado", + "email": "Daniel Delgado@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1d"]] + }, + { + "_id": "62f3270d082fcc3049e92295", + "username": "Pascal Polleunus", + "email": "Pascal Polleunus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2304, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a1e"]] + }, + { + "_id": "62f3270d082fcc3049e92296", + "username": "Sean Bannister", + "email": "Sean Bannister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a20"]] + }, + { + "_id": "62f3270d082fcc3049e92297", + "username": "Sunny Sultan", + "email": "Sunny Sultan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 920, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a21"]] + }, + { + "_id": "62f3270d082fcc3049e92299", + "username": "Aurovrata", + "email": "Aurovrata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1718, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270d082fcc3049e9229a", + "username": "piyush nath", + "email": "piyush nath@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a22"]] + }, + { + "_id": "62f3270d082fcc3049e9229b", + "username": "Hassan Ali Shahzad", + "email": "Hassan Ali Shahzad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a23"]] + }, + { + "_id": "62f3270d082fcc3049e9229c", + "username": "Shuvojit Saha", + "email": "Shuvojit Saha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 382, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a24"]] + }, + { + "_id": "62f3270e082fcc3049e9229e", + "username": "Pi Delport", + "email": "Pi Delport@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10031, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922a0", + "username": "kli", + "email": "kli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 400, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922a2", + "username": "bnieland", + "email": "bnieland@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a2a"]] + }, + { + "_id": "62f3270e082fcc3049e922a3", + "username": "Uladzislau Ulasenka", + "email": "Uladzislau Ulasenka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 530, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a26"]] + }, + { + "_id": "62f3270e082fcc3049e922a4", + "username": "user7396942", + "email": "user7396942@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a25"]] + }, + { + "_id": "62f3270e082fcc3049e922a5", + "username": "thenewjames", + "email": "thenewjames@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 775, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a27"]] + }, + { + "_id": "62f3270e082fcc3049e922a6", + "username": "Yilmaz", + "email": "Yilmaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16541, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a28"], + ["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cba"] + ] + }, + { + "_id": "62f3270e082fcc3049e922a7", + "username": "Pakpoom Tiwakornkit", + "email": "Pakpoom Tiwakornkit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a29"]] + }, + { + "_id": "62f3270e082fcc3049e922a8", + "username": "doekman", + "email": "doekman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18262, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a2c"]] + }, + { + "_id": "62f3270e082fcc3049e922aa", + "username": "jj33", + "email": "jj33@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7333, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a2b"]] + }, + { + "_id": "62f3270e082fcc3049e922ac", + "username": "Tres", + "email": "Tres@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5544, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922ae", + "username": "alexserver", + "email": "alexserver@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1318, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922b0", + "username": "Clain Dsilva", + "email": "Clain Dsilva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1623, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922b3", + "username": "stonyau", + "email": "stonyau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2022, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922b5", + "username": "Matthew Layton", + "email": "Matthew Layton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36019, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922b7", + "username": "MadeInDreams", + "email": "MadeInDreams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270e082fcc3049e922b8", + "username": "James Coglan", + "email": "James Coglan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a2e"]] + }, + { + "_id": "62f3270e082fcc3049e922b9", + "username": "Polsonby", + "email": "Polsonby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22685, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a2d"]] + }, + { + "_id": "62f3270f082fcc3049e922bb", + "username": "KOVIKO", + "email": "KOVIKO@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1310, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922be", + "username": "mdup", + "email": "mdup@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7041, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922c0", + "username": "John Dvorak", + "email": "John Dvorak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26048, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922c2", + "username": "GabLeRoux", + "email": "GabLeRoux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15225, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922c5", + "username": "Francisco Hodge", + "email": "Francisco Hodge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1305, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922c7", + "username": "aeosynth", + "email": "aeosynth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a2f"]] + }, + { + "_id": "62f3270f082fcc3049e922c8", + "username": "Jānis Elmeris", + "email": "Jānis Elmeris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1906, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a31"]] + }, + { + "_id": "62f3270f082fcc3049e922ca", + "username": "Emeka Mbah", + "email": "Emeka Mbah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15587, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922cb", + "username": "Jerry", + "email": "Jerry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a32"]] + }, + { + "_id": "62f3270f082fcc3049e922cc", + "username": "wade harrell", + "email": "wade harrell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a33"]] + }, + { + "_id": "62f3270f082fcc3049e922ce", + "username": "Rayjax", + "email": "Rayjax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7164, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922d0", + "username": "Minhaj", + "email": "Minhaj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 388, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922d2", + "username": "NoobishPro", + "email": "NoobishPro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2443, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922d4", + "username": "Dziad Borowy", + "email": "Dziad Borowy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12105, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3270f082fcc3049e922d6", + "username": "Mohamad", + "email": "Mohamad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34039, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d96"]] + }, + { + "_id": "62f3270f082fcc3049e922d7", + "username": "Sherzod", + "email": "Sherzod@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4811, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a36"]] + }, + { + "_id": "62f3270f082fcc3049e922d9", + "username": "Eric Anderson", + "email": "Eric Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3614, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a35"]] + }, + { + "_id": "62f3270f082fcc3049e922da", + "username": "Joon", + "email": "Joon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8686, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a38"]] + }, + { + "_id": "62f3270f082fcc3049e922db", + "username": "Ally", + "email": "Ally@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4656, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a37"]] + }, + { + "_id": "62f32710082fcc3049e922dd", + "username": "zero_cool", + "email": "zero_cool@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3594, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32710082fcc3049e922df", + "username": "Ron Sims II", + "email": "Ron Sims II@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 546, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3a"]] + }, + { + "_id": "62f32710082fcc3049e922e1", + "username": "abo-elleef", + "email": "abo-elleef@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1900, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a39"]] + }, + { + "_id": "62f32710082fcc3049e922e2", + "username": "stylesenberg", + "email": "stylesenberg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 519, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3d"]] + }, + { + "_id": "62f32710082fcc3049e922e4", + "username": "pcnate", + "email": "pcnate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1646, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3e"]] + }, + { + "_id": "62f32710082fcc3049e922e5", + "username": "Pian0_M4n", + "email": "Pian0_M4n@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2435, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a3f"]] + }, + { + "_id": "62f32711082fcc3049e922e8", + "username": "MacroMan", + "email": "MacroMan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2021, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a46"]] + }, + { + "_id": "62f32711082fcc3049e922ea", + "username": "JSG", + "email": "JSG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 350, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922eb", + "username": "solanki...", + "email": "solanki...@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4592, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a45"], + ["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec8"] + ] + }, + { + "_id": "62f32711082fcc3049e922ed", + "username": "Patata", + "email": "Patata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 263, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922ef", + "username": "Barry Michael Doyle", + "email": "Barry Michael Doyle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8014, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922f1", + "username": "Yunnosch", + "email": "Yunnosch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25140, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922f3", + "username": "Alberto Perez", + "email": "Alberto Perez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2357, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922f4", + "username": "saurabhgoyal795", + "email": "saurabhgoyal795@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1197, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a47"]] + }, + { + "_id": "62f32711082fcc3049e922f6", + "username": "siluveru kiran kumar", + "email": "siluveru kiran kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 589, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922f8", + "username": "tdjprog", + "email": "tdjprog@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 678, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a48"]] + }, + { + "_id": "62f32711082fcc3049e922fd", + "username": "Pang", + "email": "Pang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9111, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32711082fcc3049e922ff", + "username": "Mithu A Quayium", + "email": "Mithu A Quayium@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 709, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4a"]] + }, + { + "_id": "62f32711082fcc3049e92302", + "username": "njmwas", + "email": "njmwas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1001, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a49"]] + }, + { + "_id": "62f32712082fcc3049e92304", + "username": "shaheb", + "email": "shaheb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 448, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a4e"]] + }, + { + "_id": "62f32712082fcc3049e92309", + "username": "Rashed Rahat", + "email": "Rashed Rahat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2023, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a52"]] + }, + { + "_id": "62f32712082fcc3049e9230a", + "username": "Coni", + "email": "Coni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee4", "62f321c7082fcc3049e90a53"]] + }, + { + "_id": "62f32712082fcc3049e9230b", + "username": "Gareth Simpson", + "email": "Gareth Simpson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35323, + "questionIds": ["62f321bb082fcc3049e8fee4"], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e9230e", + "username": "Mark Micallef", + "email": "Mark Micallef@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2453, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92310", + "username": "Niko Bellic", + "email": "Niko Bellic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2189, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92312", + "username": "cee", + "email": "cee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 252, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92314", + "username": "Sakshi Nagpal", + "email": "Sakshi Nagpal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 963, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92316", + "username": "Aprillion", + "email": "Aprillion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92318", + "username": "DSLuminary", + "email": "DSLuminary@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32712082fcc3049e92319", + "username": "Samuel Meddows", + "email": "Samuel Meddows@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34862, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a56"]] + }, + { + "_id": "62f32713082fcc3049e9231a", + "username": "Jimmy M", + "email": "Jimmy M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1587, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a57"]] + }, + { + "_id": "62f32713082fcc3049e9231c", + "username": "Peter Munnings", + "email": "Peter Munnings@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3269, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9231e", + "username": "Risadinha", + "email": "Risadinha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14868, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92320", + "username": "Freewalker", + "email": "Freewalker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5217, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92321", + "username": "benjamin.keen", + "email": "benjamin.keen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1882, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a59"]] + }, + { + "_id": "62f32713082fcc3049e92322", + "username": "roshan", + "email": "roshan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2302, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5d"]] + }, + { + "_id": "62f32713082fcc3049e92324", + "username": "panhandel", + "email": "panhandel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 664, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92326", + "username": "hyde", + "email": "hyde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2475, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92327", + "username": "Rishabh Marya", + "email": "Rishabh Marya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 707, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5b"]] + }, + { + "_id": "62f32713082fcc3049e92329", + "username": "drusepth", + "email": "drusepth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 373, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9232b", + "username": "Andrea", + "email": "Andrea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 874, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5e"]] + }, + { + "_id": "62f32713082fcc3049e9232d", + "username": "Varun Natraaj", + "email": "Varun Natraaj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5c"]] + }, + { + "_id": "62f32713082fcc3049e9232f", + "username": "Andy N", + "email": "Andy N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 804, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92332", + "username": "user3374348", + "email": "user3374348@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92334", + "username": "Spacemancraig", + "email": "Spacemancraig@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1248, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92338", + "username": "ptomato", + "email": "ptomato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54305, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9233a", + "username": "elon", + "email": "elon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9233c", + "username": "chris", + "email": "chris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 615, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9233e", + "username": "user3772108", + "email": "user3772108@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 804, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e9233f", + "username": "Dunaevsky Maxim", + "email": "Dunaevsky Maxim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2804, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a5f"]] + }, + { + "_id": "62f32713082fcc3049e92342", + "username": "Dunc", + "email": "Dunc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17574, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92344", + "username": "Kind Contributor", + "email": "Kind Contributor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16318, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92346", + "username": "Adil H. Raza", + "email": "Adil H. Raza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1417, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32713082fcc3049e92347", + "username": "Morad", + "email": "Morad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2681, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a60"]] + }, + { + "_id": "62f32714082fcc3049e92349", + "username": "Brock Davis", + "email": "Brock Davis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a62"]] + }, + { + "_id": "62f32714082fcc3049e9234b", + "username": "Inrego", + "email": "Inrego@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1434, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32714082fcc3049e9234d", + "username": "McKay", + "email": "McKay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12139, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32714082fcc3049e9234f", + "username": "JoBaxter", + "email": "JoBaxter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 698, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32714082fcc3049e92350", + "username": "Marshal", + "email": "Marshal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3926, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a61"]] + }, + { + "_id": "62f32714082fcc3049e92351", + "username": "Fannon", + "email": "Fannon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a63"]] + }, + { + "_id": "62f32714082fcc3049e92353", + "username": "Artjom B.", + "email": "Artjom B.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 60051, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32714082fcc3049e92355", + "username": "Jas", + "email": "Jas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 476, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a64"]] + }, + { + "_id": "62f32714082fcc3049e92357", + "username": "Perposterer", + "email": "Perposterer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 190, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32714082fcc3049e92358", + "username": "Jose Rojas", + "email": "Jose Rojas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3410, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a65"]] + }, + { + "_id": "62f32714082fcc3049e92359", + "username": "Phil Ricketts", + "email": "Phil Ricketts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7346, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a67"]] + }, + { + "_id": "62f32714082fcc3049e9235a", + "username": "user1338062", + "email": "user1338062@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10831, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a68"]] + }, + { + "_id": "62f32714082fcc3049e9235b", + "username": "Akhil", + "email": "Akhil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a69"]] + }, + { + "_id": "62f32714082fcc3049e9235d", + "username": "marverix", + "email": "marverix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6755, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6a"]] + }, + { + "_id": "62f32715082fcc3049e9235e", + "username": "Toucouleur", + "email": "Toucouleur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1095, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6b"]] + }, + { + "_id": "62f32715082fcc3049e92360", + "username": "DevonDahon", + "email": "DevonDahon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6072, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e92362", + "username": "Paul Carlton", + "email": "Paul Carlton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2685, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e92363", + "username": "ISONecroMAn", + "email": "ISONecroMAn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1362, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6c"]] + }, + { + "_id": "62f32715082fcc3049e92364", + "username": "Isayevskiy_Sergey", + "email": "Isayevskiy_Sergey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a6e"]] + }, + { + "_id": "62f32715082fcc3049e92366", + "username": "LStarky", + "email": "LStarky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2685, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e92368", + "username": "GC_", + "email": "GC_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 438, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e9236a", + "username": "xji", + "email": "xji@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6055, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e9236b", + "username": "Oded Breiner", + "email": "Oded Breiner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27274, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a71"]] + }, + { + "_id": "62f32715082fcc3049e9236c", + "username": "Jayant Patil", + "email": "Jayant Patil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1434, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a72"]] + }, + { + "_id": "62f32715082fcc3049e9236d", + "username": "Souvik", + "email": "Souvik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 803, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a73"]] + }, + { + "_id": "62f32715082fcc3049e9236f", + "username": "nirazul", + "email": "nirazul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3798, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e92371", + "username": "L2_Paver", + "email": "L2_Paver@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 546, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32715082fcc3049e92372", + "username": "Blair Anderson", + "email": "Blair Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a74"]] + }, + { + "_id": "62f32716082fcc3049e92374", + "username": "Mika Karjunen", + "email": "Mika Karjunen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 332, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32716082fcc3049e92375", + "username": "Aung Htet", + "email": "Aung Htet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 701, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a75"]] + }, + { + "_id": "62f32716082fcc3049e92376", + "username": "jafarbtech", + "email": "jafarbtech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6437, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a76"]] + }, + { + "_id": "62f32716082fcc3049e92377", + "username": "jyotibisht", + "email": "jyotibisht@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a77"]] + }, + { + "_id": "62f32716082fcc3049e92378", + "username": "Alex Ivasyuv", + "email": "Alex Ivasyuv@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8265, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7a"]] + }, + { + "_id": "62f32716082fcc3049e92379", + "username": "alain.janinm", + "email": "alain.janinm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19665, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a79"]] + }, + { + "_id": "62f32716082fcc3049e9237b", + "username": "Jeff Lowery", + "email": "Jeff Lowery@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2323, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32716082fcc3049e9237c", + "username": "Harry Bosh", + "email": "Harry Bosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3240, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a78"]] + }, + { + "_id": "62f32716082fcc3049e9237d", + "username": "Yugandhar Chaudhari", + "email": "Yugandhar Chaudhari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3539, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7b"]] + }, + { + "_id": "62f32716082fcc3049e9237e", + "username": "Jerome Hurley", + "email": "Jerome Hurley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7d"]] + }, + { + "_id": "62f32716082fcc3049e92381", + "username": "Rihard Novozhilov", + "email": "Rihard Novozhilov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 530, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32716082fcc3049e92382", + "username": "anil shrestha", + "email": "anil shrestha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1341, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7c"]] + }, + { + "_id": "62f32716082fcc3049e92383", + "username": "agDev", + "email": "agDev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 847, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7e"]] + }, + { + "_id": "62f32717082fcc3049e92385", + "username": "Adrian Bartholomew", + "email": "Adrian Bartholomew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2269, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a7f"]] + }, + { + "_id": "62f32717082fcc3049e92387", + "username": "Ashish Pathak", + "email": "Ashish Pathak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 769, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a80"]] + }, + { + "_id": "62f32717082fcc3049e92388", + "username": "Consta Gorgan", + "email": "Consta Gorgan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 529, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a81"]] + }, + { + "_id": "62f32717082fcc3049e92389", + "username": "Caiuby Freitas", + "email": "Caiuby Freitas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 262, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a82"]] + }, + { + "_id": "62f32717082fcc3049e9238b", + "username": "Jaydeep Shil", + "email": "Jaydeep Shil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1748, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32717082fcc3049e9238e", + "username": "zardilior", + "email": "zardilior@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2586, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32717082fcc3049e9238f", + "username": "SpaceX", + "email": "SpaceX@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a84"]] + }, + { + "_id": "62f32717082fcc3049e92390", + "username": "Yanir Calisar", + "email": "Yanir Calisar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 467, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a85"]] + }, + { + "_id": "62f32717082fcc3049e92391", + "username": "IonicMan", + "email": "IonicMan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 616, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a87"]] + }, + { + "_id": "62f32717082fcc3049e92393", + "username": "Shubham Chadokar", + "email": "Shubham Chadokar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2138, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a88"]] + }, + { + "_id": "62f32718082fcc3049e92394", + "username": "Mansour Alnasser", + "email": "Mansour Alnasser@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a8a"]] + }, + { + "_id": "62f32718082fcc3049e92395", + "username": "Parking Master", + "email": "Parking Master@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 402, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a8b"]] + }, + { + "_id": "62f32718082fcc3049e92396", + "username": "jchnxu", + "email": "jchnxu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 841, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a8c"]] + }, + { + "_id": "62f32718082fcc3049e92397", + "username": "Jorge", + "email": "Jorge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a8e"]] + }, + { + "_id": "62f32718082fcc3049e92399", + "username": "Craws", + "email": "Craws@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 482, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32718082fcc3049e9239b", + "username": "imbr", + "email": "imbr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4672, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32718082fcc3049e9239c", + "username": "SarjanWebDev", + "email": "SarjanWebDev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 513, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee6", "62f321c7082fcc3049e90a8d"]] + }, + { + "_id": "62f32718082fcc3049e9239d", + "username": "Suresh", + "email": "Suresh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37067, + "questionIds": ["62f321bb082fcc3049e8fee6"], + "answerIds": [] + }, + { + "_id": "62f32718082fcc3049e9239e", + "username": "gor", + "email": "gor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a91"]] + }, + { + "_id": "62f32718082fcc3049e9239f", + "username": "balupton", + "email": "balupton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44899, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a92"]] + }, + { + "_id": "62f32718082fcc3049e923a1", + "username": "Nuno", + "email": "Nuno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3019, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32718082fcc3049e923a3", + "username": "Mauvis Ledford", + "email": "Mauvis Ledford@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38474, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a90"]] + }, + { + "_id": "62f32719082fcc3049e923a5", + "username": "Sean H. Worthington", + "email": "Sean H. Worthington@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1531, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a93"]] + }, + { + "_id": "62f32719082fcc3049e923a6", + "username": "sgmonda", + "email": "sgmonda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2464, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a94"]] + }, + { + "_id": "62f32719082fcc3049e923a7", + "username": "Paul van Jaarsveld", + "email": "Paul van Jaarsveld@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1444, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a96"]] + }, + { + "_id": "62f32719082fcc3049e923a8", + "username": "Amadu Bah", + "email": "Amadu Bah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2759, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a95"]] + }, + { + "_id": "62f32719082fcc3049e923a9", + "username": "Zlatko", + "email": "Zlatko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17964, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a98"]] + }, + { + "_id": "62f32719082fcc3049e923ab", + "username": "JK ABC", + "email": "JK ABC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 576, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32719082fcc3049e923ac", + "username": "real_ate", + "email": "real_ate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10299, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a97"]] + }, + { + "_id": "62f32719082fcc3049e923ad", + "username": "Piyush Sagar", + "email": "Piyush Sagar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2731, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a99"]] + }, + { + "_id": "62f32719082fcc3049e923af", + "username": "Subramanya Rao", + "email": "Subramanya Rao@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32719082fcc3049e923b0", + "username": "dthree", + "email": "dthree@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18648, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9a"]] + }, + { + "_id": "62f32719082fcc3049e923b1", + "username": "Evren Kutar", + "email": "Evren Kutar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9c"]] + }, + { + "_id": "62f3271a082fcc3049e923b3", + "username": "barwnikk", + "email": "barwnikk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 920, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271a082fcc3049e923b4", + "username": "MatCas", + "email": "MatCas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9d"]] + }, + { + "_id": "62f3271a082fcc3049e923b5", + "username": "Cassidy", + "email": "Cassidy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 317, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9e"]] + }, + { + "_id": "62f3271a082fcc3049e923b6", + "username": "Joseph Merdrignac", + "email": "Joseph Merdrignac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3050, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90a9f"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eae"] + ] + }, + { + "_id": "62f3271a082fcc3049e923b7", + "username": "Adeojo Emmanuel IMM", + "email": "Adeojo Emmanuel IMM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1950, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa0"]] + }, + { + "_id": "62f3271a082fcc3049e923b8", + "username": "Rubin bhandari", + "email": "Rubin bhandari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1677, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa2"]] + }, + { + "_id": "62f3271a082fcc3049e923b9", + "username": "S.Mishra", + "email": "S.Mishra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3078, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa1"]] + }, + { + "_id": "62f3271a082fcc3049e923ba", + "username": "grebenyuksv", + "email": "grebenyuksv@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1669, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa5"]] + }, + { + "_id": "62f3271a082fcc3049e923bb", + "username": "bhwp", + "email": "bhwp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa3"]] + }, + { + "_id": "62f3271a082fcc3049e923bd", + "username": "user3285954", + "email": "user3285954@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4321, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271a082fcc3049e923be", + "username": "Akshay Rajput", + "email": "Akshay Rajput@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1820, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa6"]] + }, + { + "_id": "62f3271b082fcc3049e923bf", + "username": "isacvale", + "email": "isacvale@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 587, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa8"]] + }, + { + "_id": "62f3271b082fcc3049e923c1", + "username": "hastrb", + "email": "hastrb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 326, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271b082fcc3049e923c2", + "username": "Michael Warner", + "email": "Michael Warner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3286, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa7"]] + }, + { + "_id": "62f3271b082fcc3049e923c4", + "username": "Endless", + "email": "Endless@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30028, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271b082fcc3049e923c5", + "username": "tibalt", + "email": "tibalt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14313, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aa9"]] + }, + { + "_id": "62f3271b082fcc3049e923c6", + "username": "Manvel", + "email": "Manvel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aaa"]] + }, + { + "_id": "62f3271b082fcc3049e923c7", + "username": "rmolinamir", + "email": "rmolinamir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 711, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aab"]] + }, + { + "_id": "62f3271b082fcc3049e923c8", + "username": "Giorgio Robino", + "email": "Giorgio Robino@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2008, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aac"]] + }, + { + "_id": "62f3271b082fcc3049e923c9", + "username": "Carson", + "email": "Carson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4160, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aad"]] + }, + { + "_id": "62f3271b082fcc3049e923cb", + "username": "temporary_user_name", + "email": "temporary_user_name@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34219, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6d"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313e"] + ] + }, + { + "_id": "62f3271b082fcc3049e923cc", + "username": "Andrew Odri", + "email": "Andrew Odri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7968, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aae"]] + }, + { + "_id": "62f3271b082fcc3049e923cd", + "username": "Idan_Krupnik", + "email": "Idan_Krupnik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 313, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90ab0"]] + }, + { + "_id": "62f3271b082fcc3049e923d0", + "username": "Preston L. Bannister", + "email": "Preston L. Bannister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 320, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90aaf"]] + }, + { + "_id": "62f3271c082fcc3049e923d2", + "username": "hitautodestruct", + "email": "hitautodestruct@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19234, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923d4", + "username": "Ifnot", + "email": "Ifnot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4746, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923d6", + "username": "node_saini", + "email": "node_saini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 717, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923d7", + "username": "Buu", + "email": "Buu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 48545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ab8"]] + }, + { + "_id": "62f3271c082fcc3049e923d8", + "username": "Hender", + "email": "Hender@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 143, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee7", "62f321c7082fcc3049e90ab1"]] + }, + { + "_id": "62f3271c082fcc3049e923d9", + "username": "milkplus", + "email": "milkplus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31109, + "questionIds": ["62f321bb082fcc3049e8fee7"], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923da", + "username": "Mike Brennan", + "email": "Mike Brennan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2988, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90aba"]] + }, + { + "_id": "62f3271c082fcc3049e923dc", + "username": "erickson", + "email": "erickson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 259267, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923de", + "username": "opteronn", + "email": "opteronn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 166, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923e0", + "username": "kevzettler", + "email": "kevzettler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4734, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923e3", + "username": "fiatjaf", + "email": "fiatjaf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10858, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923e5", + "username": "Tseng", + "email": "Tseng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57876, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3271c082fcc3049e923e8", + "username": "John N", + "email": "John N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32759082fcc3049e923e9", + "username": "serg", + "email": "serg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac2"]] + }, + { + "_id": "62f32759082fcc3049e923ea", + "username": "Mohith Maratt", + "email": "Mohith Maratt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac1"]] + }, + { + "_id": "62f32759082fcc3049e923eb", + "username": "Jonathan Applebaum", + "email": "Jonathan Applebaum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac5"]] + }, + { + "_id": "62f32759082fcc3049e923ec", + "username": "Qback", + "email": "Qback@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3616, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac7"]] + }, + { + "_id": "62f32759082fcc3049e923ed", + "username": "Arthur", + "email": "Arthur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2287, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac6"]] + }, + { + "_id": "62f32759082fcc3049e923ee", + "username": "gurpartap", + "email": "gurpartap@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 155, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90aca"]] + }, + { + "_id": "62f3275a082fcc3049e923ef", + "username": "Pyzard", + "email": "Pyzard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 360, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90acb"]] + }, + { + "_id": "62f3275a082fcc3049e923f0", + "username": "m4heshd", + "email": "m4heshd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 682, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90acc"]] + }, + { + "_id": "62f3275a082fcc3049e923f1", + "username": "Thorben", + "email": "Thorben@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6702, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad2"]] + }, + { + "_id": "62f3275a082fcc3049e923f3", + "username": "Yster", + "email": "Yster@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e923f5", + "username": "twharmon", + "email": "twharmon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3730, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e923f7", + "username": "Sagar Naliyapara", + "email": "Sagar Naliyapara@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3745, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e923f9", + "username": "user9258013", + "email": "user9258013@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e923fb", + "username": "Mark Baijens", + "email": "Mark Baijens@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12670, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e923fc", + "username": "Roy", + "email": "Roy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43148, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad3"]] + }, + { + "_id": "62f3275a082fcc3049e923fd", + "username": "JohnP", + "email": "JohnP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 48731, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad5"]] + }, + { + "_id": "62f3275a082fcc3049e92400", + "username": "Rohit Singh", + "email": "Rohit Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14964, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e92401", + "username": "David", + "email": "David@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 192064, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad4"], + ["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8a"] + ] + }, + { + "_id": "62f3275a082fcc3049e92403", + "username": "mrmillsy", + "email": "mrmillsy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275a082fcc3049e92404", + "username": "SumairIrshad", + "email": "SumairIrshad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1625, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad7"]] + }, + { + "_id": "62f3275a082fcc3049e92406", + "username": "Suleman Mirza", + "email": "Suleman Mirza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 875, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad9"]] + }, + { + "_id": "62f3275a082fcc3049e92408", + "username": "Ionică Bizău", + "email": "Ionică Bizău@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103272, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ad8"]] + }, + { + "_id": "62f3275a082fcc3049e9240a", + "username": "Amal Murali", + "email": "Amal Murali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73525, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275b082fcc3049e9240c", + "username": "Pinch", + "email": "Pinch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3801, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ada"]] + }, + { + "_id": "62f3275b082fcc3049e9240d", + "username": "user762330", + "email": "user762330@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90adb"]] + }, + { + "_id": "62f3275b082fcc3049e9240e", + "username": "Fer To", + "email": "Fer To@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1447, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90adc"]] + }, + { + "_id": "62f3275b082fcc3049e9240f", + "username": "William Entriken", + "email": "William Entriken@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90add"]] + }, + { + "_id": "62f3275b082fcc3049e92410", + "username": "Poorna Senani Gamage", + "email": "Poorna Senani Gamage@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90adf"]] + }, + { + "_id": "62f3275b082fcc3049e92412", + "username": "Junaid Atari", + "email": "Junaid Atari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 535, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275b082fcc3049e92413", + "username": "Mujah Maskey", + "email": "Mujah Maskey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8484, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ade"]] + }, + { + "_id": "62f3275b082fcc3049e92414", + "username": "Frank", + "email": "Frank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2185, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae0"]] + }, + { + "_id": "62f3275b082fcc3049e92415", + "username": "J. Moreno", + "email": "J. Moreno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 151, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae3"]] + }, + { + "_id": "62f3275c082fcc3049e92416", + "username": "Roshana Pitigala", + "email": "Roshana Pitigala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8032, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae5"]] + }, + { + "_id": "62f3275c082fcc3049e92417", + "username": "dcangulo", + "email": "dcangulo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1599, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae4"]] + }, + { + "_id": "62f3275c082fcc3049e92418", + "username": "Na Nonthasen", + "email": "Na Nonthasen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90ae6"]] + }, + { + "_id": "62f3275c082fcc3049e9241a", + "username": "Romain Valeri", + "email": "Romain Valeri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17657, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275c082fcc3049e9241b", + "username": "ankit singh", + "email": "ankit singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 206, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aeb"]] + }, + { + "_id": "62f3275c082fcc3049e9241c", + "username": "infomasud", + "email": "infomasud@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1205, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aea"]] + }, + { + "_id": "62f3275c082fcc3049e9241d", + "username": "user15255379", + "email": "user15255379@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aed"]] + }, + { + "_id": "62f3275d082fcc3049e9241e", + "username": "Tharindu Lakshan", + "email": "Tharindu Lakshan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aee"]] + }, + { + "_id": "62f3275d082fcc3049e9241f", + "username": "Pri Nce", + "email": "Pri Nce@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 371, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feeb", "62f321c8082fcc3049e90aef"]] + }, + { + "_id": "62f3275d082fcc3049e92420", + "username": "luca", + "email": "luca@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35816, + "questionIds": ["62f321bb082fcc3049e8feeb"], + "answerIds": [] + }, + { + "_id": "62f3275d082fcc3049e92421", + "username": "Chris MacDonald", + "email": "Chris MacDonald@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5857, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90af5"]] + }, + { + "_id": "62f3275d082fcc3049e92422", + "username": "Dennis", + "email": "Dennis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3418, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90af8"]] + }, + { + "_id": "62f3275d082fcc3049e92424", + "username": "Joe Lencioni", + "email": "Joe Lencioni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90af7"]] + }, + { + "_id": "62f3275d082fcc3049e92425", + "username": "Erik", + "email": "Erik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90af9"]] + }, + { + "_id": "62f3275d082fcc3049e92426", + "username": "Wolfram", + "email": "Wolfram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7989, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90afb"]] + }, + { + "_id": "62f3275d082fcc3049e92428", + "username": "OzzyTheGiant", + "email": "OzzyTheGiant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 619, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275d082fcc3049e92429", + "username": "user212621", + "email": "user212621@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90afa"]] + }, + { + "_id": "62f3275d082fcc3049e9242a", + "username": "govind", + "email": "govind@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90afc"]] + }, + { + "_id": "62f3275d082fcc3049e9242e", + "username": "Eran Galperin", + "email": "Eran Galperin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85336, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90af6"]] + }, + { + "_id": "62f3275d082fcc3049e92430", + "username": "Niamatullah Bakhshi", + "email": "Niamatullah Bakhshi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1371, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275d082fcc3049e92432", + "username": "Jai Kumaresh", + "email": "Jai Kumaresh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 613, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3275e082fcc3049e92433", + "username": "Chu Yeow", + "email": "Chu Yeow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 977, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90afe"]] + }, + { + "_id": "62f3275e082fcc3049e92435", + "username": "Neco", + "email": "Neco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90afd"]] + }, + { + "_id": "62f3275e082fcc3049e92436", + "username": "webenformasyon", + "email": "webenformasyon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90aff"]] + }, + { + "_id": "62f3275e082fcc3049e92437", + "username": "Rowan", + "email": "Rowan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b02"]] + }, + { + "_id": "62f3275e082fcc3049e92438", + "username": "34m0", + "email": "34m0@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b00"]] + }, + { + "_id": "62f3275e082fcc3049e92439", + "username": "nazar kuliyev", + "email": "nazar kuliyev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1155, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b01"]] + }, + { + "_id": "62f3275e082fcc3049e9243a", + "username": "benb", + "email": "benb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 703, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b04"]] + }, + { + "_id": "62f3275e082fcc3049e9243b", + "username": "Satya Prakash", + "email": "Satya Prakash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3222, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b03"]] + }, + { + "_id": "62f3275e082fcc3049e9243c", + "username": "netdjw", + "email": "netdjw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4456, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b05"]] + }, + { + "_id": "62f3275e082fcc3049e9243d", + "username": "Spencer Fry", + "email": "Spencer Fry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 249, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b06"]] + }, + { + "_id": "62f3275f082fcc3049e9243e", + "username": "Toogy", + "email": "Toogy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b07"]] + }, + { + "_id": "62f3275f082fcc3049e9243f", + "username": "Alexandre T.", + "email": "Alexandre T.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3164, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0a"]] + }, + { + "_id": "62f3275f082fcc3049e92440", + "username": "srinath", + "email": "srinath@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 166, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b09"]] + }, + { + "_id": "62f3275f082fcc3049e92441", + "username": "mlangenberg", + "email": "mlangenberg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1067, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0b"]] + }, + { + "_id": "62f3275f082fcc3049e92443", + "username": "Mahesh Gaikwad", + "email": "Mahesh Gaikwad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1c"]] + }, + { + "_id": "62f3275f082fcc3049e92444", + "username": "Bilal Berjawi", + "email": "Bilal Berjawi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0c"]] + }, + { + "_id": "62f3275f082fcc3049e92445", + "username": "teynon", + "email": "teynon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6844, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0e"]] + }, + { + "_id": "62f3275f082fcc3049e92446", + "username": "constrictus", + "email": "constrictus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0d"]] + }, + { + "_id": "62f3275f082fcc3049e92447", + "username": "maday", + "email": "maday@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b0f"]] + }, + { + "_id": "62f3275f082fcc3049e92448", + "username": "kboom", + "email": "kboom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b10"]] + }, + { + "_id": "62f32760082fcc3049e92449", + "username": "Roei Bahumi", + "email": "Roei Bahumi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3023, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b12"]] + }, + { + "_id": "62f32760082fcc3049e9244a", + "username": "mems", + "email": "mems@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b11"]] + }, + { + "_id": "62f32760082fcc3049e9244b", + "username": "Webmaster G", + "email": "Webmaster G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 482, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b13"]] + }, + { + "_id": "62f32760082fcc3049e9244c", + "username": "lan.ta", + "email": "lan.ta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b14"]] + }, + { + "_id": "62f32760082fcc3049e9244d", + "username": "Awena", + "email": "Awena@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 992, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b16"]] + }, + { + "_id": "62f32760082fcc3049e9244e", + "username": "Yacine Zalouani", + "email": "Yacine Zalouani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7789, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b15"]] + }, + { + "_id": "62f32760082fcc3049e92451", + "username": "KyleMit", + "email": "KyleMit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33881, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b18"]] + }, + { + "_id": "62f32760082fcc3049e92452", + "username": "Bohdan Lyzanets", + "email": "Bohdan Lyzanets@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1582, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b19"]] + }, + { + "_id": "62f32760082fcc3049e92453", + "username": "Manish Shrivastava", + "email": "Manish Shrivastava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28705, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1a"]] + }, + { + "_id": "62f32761082fcc3049e92454", + "username": "shiv", + "email": "shiv@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1b"]] + }, + { + "_id": "62f32761082fcc3049e92456", + "username": "bguiz", + "email": "bguiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25222, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32761082fcc3049e92458", + "username": "Alice", + "email": "Alice@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 495, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32761082fcc3049e92459", + "username": "Iman Sedighi", + "email": "Iman Sedighi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6958, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1e"]] + }, + { + "_id": "62f32761082fcc3049e9245b", + "username": "aroykos", + "email": "aroykos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1d"]] + }, + { + "_id": "62f32761082fcc3049e9245c", + "username": "bbe", + "email": "bbe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b20"]] + }, + { + "_id": "62f32761082fcc3049e9245d", + "username": "Scott Richardson", + "email": "Scott Richardson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b1f"]] + }, + { + "_id": "62f32761082fcc3049e9245e", + "username": "martinedwards", + "email": "martinedwards@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5291, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b21"]] + }, + { + "_id": "62f32761082fcc3049e92460", + "username": "Daniel Tonon", + "email": "Daniel Tonon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8291, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b22"]] + }, + { + "_id": "62f32761082fcc3049e92462", + "username": "dbarth", + "email": "dbarth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 208, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32761082fcc3049e92463", + "username": "Rameez Rami", + "email": "Rameez Rami@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4788, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b23"]] + }, + { + "_id": "62f32761082fcc3049e92465", + "username": "Nitekurs", + "email": "Nitekurs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 371, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b24"]] + }, + { + "_id": "62f32762082fcc3049e92467", + "username": "SoliMoli", + "email": "SoliMoli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 732, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32762082fcc3049e92468", + "username": "Yishu Fang", + "email": "Yishu Fang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8970, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b27"]] + }, + { + "_id": "62f32762082fcc3049e92469", + "username": "wsc", + "email": "wsc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 906, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b26"]] + }, + { + "_id": "62f32762082fcc3049e9246a", + "username": "Matthew Goodwin", + "email": "Matthew Goodwin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1122, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b29"]] + }, + { + "_id": "62f32762082fcc3049e9246b", + "username": "Qwertiy", + "email": "Qwertiy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17618, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b28"]] + }, + { + "_id": "62f32762082fcc3049e9246c", + "username": "Thamaraiselvam", + "email": "Thamaraiselvam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6769, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2b"], + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c72"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93143"] + ] + }, + { + "_id": "62f32762082fcc3049e9246d", + "username": "Waheed", + "email": "Waheed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 578, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2d"]] + }, + { + "_id": "62f32762082fcc3049e9246e", + "username": "froilanq", + "email": "froilanq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 904, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2c"]] + }, + { + "_id": "62f32762082fcc3049e9246f", + "username": "Waltur Buerk", + "email": "Waltur Buerk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1338, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b2e"]] + }, + { + "_id": "62f32763082fcc3049e92470", + "username": "Dan Philip", + "email": "Dan Philip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3944, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b31"]] + }, + { + "_id": "62f32763082fcc3049e92471", + "username": "Karthikeyan Ganesan", + "email": "Karthikeyan Ganesan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1617, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b30"]] + }, + { + "_id": "62f32763082fcc3049e92472", + "username": "Walt", + "email": "Walt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b32"]] + }, + { + "_id": "62f32763082fcc3049e92473", + "username": "Fabian", + "email": "Fabian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b33"]] + }, + { + "_id": "62f32763082fcc3049e92474", + "username": "hienbt88", + "email": "hienbt88@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1249, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b34"], + ["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af0"] + ] + }, + { + "_id": "62f32763082fcc3049e92476", + "username": "Duannx", + "email": "Duannx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6449, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b35"]] + }, + { + "_id": "62f32763082fcc3049e92477", + "username": "Aominé", + "email": "Aominé@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 442, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b36"]] + }, + { + "_id": "62f32763082fcc3049e92478", + "username": "Muhammet Can TONBUL", + "email": "Muhammet Can TONBUL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2810, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b37"]] + }, + { + "_id": "62f32763082fcc3049e92479", + "username": "chea sotheara", + "email": "chea sotheara@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b38"]] + }, + { + "_id": "62f32764082fcc3049e9247a", + "username": "Rinto George", + "email": "Rinto George@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33187, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b39"], + ["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b90"], + ["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c14"] + ] + }, + { + "_id": "62f32764082fcc3049e9247b", + "username": "Jovanni G", + "email": "Jovanni G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 318, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3a"]] + }, + { + "_id": "62f32764082fcc3049e9247c", + "username": "DaniG2k", + "email": "DaniG2k@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4582, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3b"]] + }, + { + "_id": "62f32764082fcc3049e9247d", + "username": "Yair Cohen", + "email": "Yair Cohen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 367, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3c"]] + }, + { + "_id": "62f32764082fcc3049e9247e", + "username": "Marcelo Ribeiro", + "email": "Marcelo Ribeiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 301, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3e"]] + }, + { + "_id": "62f32764082fcc3049e9247f", + "username": "Rivenfall", + "email": "Rivenfall@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1083, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3d"]] + }, + { + "_id": "62f32764082fcc3049e92481", + "username": "Jesse Reza Khorasanee", + "email": "Jesse Reza Khorasanee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2813, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32764082fcc3049e92482", + "username": "Илья Зеленько", + "email": "Илья Зеленько@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6786, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b40"]] + }, + { + "_id": "62f32764082fcc3049e92484", + "username": "Paul Roub", + "email": "Paul Roub@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35942, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32764082fcc3049e92485", + "username": "JBarros", + "email": "JBarros@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b3f"]] + }, + { + "_id": "62f32764082fcc3049e92486", + "username": "online Thomas", + "email": "online Thomas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8690, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b42"]] + }, + { + "_id": "62f32764082fcc3049e92487", + "username": "Mu-Tsun Tsai", + "email": "Mu-Tsun Tsai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b41"]] + }, + { + "_id": "62f32765082fcc3049e92488", + "username": "aksl", + "email": "aksl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b43"]] + }, + { + "_id": "62f32765082fcc3049e9248a", + "username": "WorldOfEmre", + "email": "WorldOfEmre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 57, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32765082fcc3049e9248b", + "username": "Cezar Augusto", + "email": "Cezar Augusto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7847, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b44"]] + }, + { + "_id": "62f32765082fcc3049e9248c", + "username": "axew3", + "email": "axew3@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b45"]] + }, + { + "_id": "62f32765082fcc3049e9248e", + "username": "Vladimir Jovanović", + "email": "Vladimir Jovanović@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2799, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32765082fcc3049e92491", + "username": "catwith", + "email": "catwith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 369, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32765082fcc3049e92492", + "username": "tim-mccurrach", + "email": "tim-mccurrach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6219, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b46"]] + }, + { + "_id": "62f32765082fcc3049e92493", + "username": "Sommelier", + "email": "Sommelier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b48"]] + }, + { + "_id": "62f32765082fcc3049e92494", + "username": "Normajean", + "email": "Normajean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 877, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b47"], + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e81"] + ] + }, + { + "_id": "62f32765082fcc3049e92495", + "username": "jameshfisher", + "email": "jameshfisher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30294, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b49"]] + }, + { + "_id": "62f32765082fcc3049e92496", + "username": "Felix Furtmayr", + "email": "Felix Furtmayr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 321, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee8", "62f321c8082fcc3049e90b4a"]] + }, + { + "_id": "62f32765082fcc3049e92497", + "username": "Sergio del Amo", + "email": "Sergio del Amo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 74765, + "questionIds": ["62f321bb082fcc3049e8fee8", "62f321bb082fcc3049e8fedb"], + "answerIds": [] + }, + { + "_id": "62f32765082fcc3049e92498", + "username": "brandonjp", + "email": "brandonjp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2998, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b4c"]] + }, + { + "_id": "62f32766082fcc3049e92499", + "username": "Ryan Phelan", + "email": "Ryan Phelan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b4e"]] + }, + { + "_id": "62f32766082fcc3049e9249a", + "username": "shanimal", + "email": "shanimal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b50"]] + }, + { + "_id": "62f32766082fcc3049e9249c", + "username": "brauliobo", + "email": "brauliobo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5280, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32766082fcc3049e9249d", + "username": "Vadim", + "email": "Vadim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4994, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b4f"]] + }, + { + "_id": "62f32766082fcc3049e9249e", + "username": "Mic", + "email": "Mic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24264, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b54"]] + }, + { + "_id": "62f32766082fcc3049e9249f", + "username": "Mohammad Arif", + "email": "Mohammad Arif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6401, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b51"]] + }, + { + "_id": "62f32766082fcc3049e924a0", + "username": "Clint", + "email": "Clint@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b52"]] + }, + { + "_id": "62f32766082fcc3049e924a1", + "username": "AlfaTeK", + "email": "AlfaTeK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7077, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b55"]] + }, + { + "_id": "62f32766082fcc3049e924a3", + "username": "dogonaroof", + "email": "dogonaroof@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32766082fcc3049e924a4", + "username": "Eneko Alonso", + "email": "Eneko Alonso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17928, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b56"]] + }, + { + "_id": "62f32767082fcc3049e924a5", + "username": "skaurus", + "email": "skaurus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1474, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b58"]] + }, + { + "_id": "62f32767082fcc3049e924a6", + "username": "mynameistechno", + "email": "mynameistechno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3400, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b57"]] + }, + { + "_id": "62f32767082fcc3049e924a7", + "username": "user981090", + "email": "user981090@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5a"]] + }, + { + "_id": "62f32767082fcc3049e924a8", + "username": "alkos333", + "email": "alkos333@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 591, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5c"]] + }, + { + "_id": "62f32767082fcc3049e924a9", + "username": "Sagiv Ofek", + "email": "Sagiv Ofek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24922, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5d"]] + }, + { + "_id": "62f32767082fcc3049e924aa", + "username": "rodneyrehm", + "email": "rodneyrehm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13209, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5e"]] + }, + { + "_id": "62f32767082fcc3049e924ac", + "username": "adardesign", + "email": "adardesign@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32115, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32767082fcc3049e924ae", + "username": "greg", + "email": "greg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32767082fcc3049e924af", + "username": "BMitch", + "email": "BMitch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 194991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b5f"]] + }, + { + "_id": "62f32768082fcc3049e924b0", + "username": "Tomamais", + "email": "Tomamais@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 95, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b63"]] + }, + { + "_id": "62f32768082fcc3049e924b2", + "username": "chri3g91", + "email": "chri3g91@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32768082fcc3049e924b4", + "username": "luke_mclachlan", + "email": "luke_mclachlan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1019, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32768082fcc3049e924b6", + "username": "IT ppl", + "email": "IT ppl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2578, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b61"]] + }, + { + "_id": "62f32768082fcc3049e924b8", + "username": "Roc Boronat", + "email": "Roc Boronat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10625, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32768082fcc3049e924b9", + "username": "Martin Borthiry", + "email": "Martin Borthiry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5196, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b66"]] + }, + { + "_id": "62f32768082fcc3049e924ba", + "username": "Albireo", + "email": "Albireo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10718, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b67"]] + }, + { + "_id": "62f32769082fcc3049e924bb", + "username": "Nijikokun", + "email": "Nijikokun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1446, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6b"]] + }, + { + "_id": "62f32769082fcc3049e924bc", + "username": "tim", + "email": "tim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3665, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6e"]] + }, + { + "_id": "62f32769082fcc3049e924bd", + "username": "gewel", + "email": "gewel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6c"]] + }, + { + "_id": "62f32769082fcc3049e924be", + "username": "Gabriel Ryan Nahmias", + "email": "Gabriel Ryan Nahmias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2075, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b6f"]] + }, + { + "_id": "62f32769082fcc3049e924bf", + "username": "Roi", + "email": "Roi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b70"]] + }, + { + "_id": "62f32769082fcc3049e924c1", + "username": "Ph0en1x", + "email": "Ph0en1x@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9815, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b72"]] + }, + { + "_id": "62f32769082fcc3049e924c3", + "username": "etlds", + "email": "etlds@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5658, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32769082fcc3049e924c4", + "username": "Kevin Cox", + "email": "Kevin Cox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2888, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b71"]] + }, + { + "_id": "62f32769082fcc3049e924c6", + "username": "Mikhus", + "email": "Mikhus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1069, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b73"]] + }, + { + "_id": "62f32769082fcc3049e924c8", + "username": "Baran Emre", + "email": "Baran Emre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2552, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32769082fcc3049e924ca", + "username": "TheWhiteLlama", + "email": "TheWhiteLlama@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1256, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32769082fcc3049e924cb", + "username": "nkh", + "email": "nkh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5331, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b74"]] + }, + { + "_id": "62f3276a082fcc3049e924cd", + "username": "Rup", + "email": "Rup@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32855, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3276a082fcc3049e924cf", + "username": "soheil bijavar", + "email": "soheil bijavar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b76"]] + }, + { + "_id": "62f3276a082fcc3049e924d0", + "username": "Robert Bolton", + "email": "Robert Bolton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 669, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b75"]] + }, + { + "_id": "62f3276a082fcc3049e924d1", + "username": "Pushkraj P. Lanjekar", + "email": "Pushkraj P. Lanjekar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2183, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b78"]] + }, + { + "_id": "62f3276a082fcc3049e924d2", + "username": "clyfe", + "email": "clyfe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23415, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b79"]] + }, + { + "_id": "62f3276a082fcc3049e924d5", + "username": "allenhwkim", + "email": "allenhwkim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26586, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3276a082fcc3049e924d6", + "username": "BlueMark", + "email": "BlueMark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 932, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7c"]] + }, + { + "_id": "62f3276a082fcc3049e924d8", + "username": "JoshYates1980", + "email": "JoshYates1980@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095b"]] + }, + { + "_id": "62f3276a082fcc3049e924d9", + "username": "user2579312", + "email": "user2579312@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7b"]] + }, + { + "_id": "62f3276a082fcc3049e924da", + "username": "Ankit_Shah55", + "email": "Ankit_Shah55@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 789, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7d"]] + }, + { + "_id": "62f3276a082fcc3049e924db", + "username": "Mat Ryer", + "email": "Mat Ryer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3477, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7e"]] + }, + { + "_id": "62f3276b082fcc3049e924dc", + "username": "Pithikos", + "email": "Pithikos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b80"]] + }, + { + "_id": "62f3276b082fcc3049e924dd", + "username": "acjay", + "email": "acjay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b7f"]] + }, + { + "_id": "62f3276b082fcc3049e924df", + "username": "cocco", + "email": "cocco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16078, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b82"]] + }, + { + "_id": "62f3276b082fcc3049e924e0", + "username": "sonorita", + "email": "sonorita@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 761, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b81"]] + }, + { + "_id": "62f3276b082fcc3049e924e1", + "username": "Saidulu Buchhala", + "email": "Saidulu Buchhala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1363, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b83"]] + }, + { + "_id": "62f327a8082fcc3049e924e3", + "username": "Vladimir Kornea", + "email": "Vladimir Kornea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4029, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b89"]] + }, + { + "_id": "62f327a8082fcc3049e924e5", + "username": "Sudar", + "email": "Sudar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17852, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924e7", + "username": "NiCk Newman", + "email": "NiCk Newman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1606, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924e9", + "username": "hackel", + "email": "hackel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1122, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924ea", + "username": "Konstantin Tarkus", + "email": "Konstantin Tarkus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36787, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b8b"]] + }, + { + "_id": "62f327a8082fcc3049e924eb", + "username": "Peter T Bosse II", + "email": "Peter T Bosse II@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b8c"]] + }, + { + "_id": "62f327a8082fcc3049e924ed", + "username": "toon81", + "email": "toon81@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 888, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924f0", + "username": "Fran Verona", + "email": "Fran Verona@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5360, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b8e"]] + }, + { + "_id": "62f327a8082fcc3049e924f1", + "username": "arikfr", + "email": "arikfr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3235, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b8f"]] + }, + { + "_id": "62f327a8082fcc3049e924f3", + "username": "wieczorek1990", + "email": "wieczorek1990@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6351, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924f5", + "username": "mukuljainx", + "email": "mukuljainx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 673, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924f6", + "username": "loshMiS", + "email": "loshMiS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b92"]] + }, + { + "_id": "62f327a8082fcc3049e924f8", + "username": "Tristanisginger", + "email": "Tristanisginger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1672, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a8082fcc3049e924f9", + "username": "Venkat Kotra", + "email": "Venkat Kotra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9915, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b91"]] + }, + { + "_id": "62f327a9082fcc3049e924fb", + "username": "Steve Meisner", + "email": "Steve Meisner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 489, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a9082fcc3049e924fc", + "username": "karaxuna", + "email": "karaxuna@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26392, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b94"]] + }, + { + "_id": "62f327a9082fcc3049e924fe", + "username": "Nuri Akman", + "email": "Nuri Akman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 782, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327a9082fcc3049e924ff", + "username": "spirinvladimir", + "email": "spirinvladimir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 668, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b95"]] + }, + { + "_id": "62f327a9082fcc3049e92500", + "username": "Luke Alderton", + "email": "Luke Alderton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b96"]] + }, + { + "_id": "62f327a9082fcc3049e92501", + "username": "chipairon", + "email": "chipairon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b97"]] + }, + { + "_id": "62f327a9082fcc3049e92502", + "username": "Kunal", + "email": "Kunal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 470, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9a"]] + }, + { + "_id": "62f327a9082fcc3049e92503", + "username": "vernonner3voltazim", + "email": "vernonner3voltazim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 770, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b99"]] + }, + { + "_id": "62f327a9082fcc3049e92504", + "username": "MannyC", + "email": "MannyC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 555, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9c"]] + }, + { + "_id": "62f327a9082fcc3049e92505", + "username": "Smile4ever", + "email": "Smile4ever@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9b"]] + }, + { + "_id": "62f327aa082fcc3049e92507", + "username": "Love Putin", + "email": "Love Putin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 410, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327aa082fcc3049e92509", + "username": "Avnish alok", + "email": "Avnish alok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2090, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327aa082fcc3049e9250a", + "username": "CG_DEV", + "email": "CG_DEV@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 698, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90b9e"]] + }, + { + "_id": "62f327aa082fcc3049e9250b", + "username": "CodeNinja", + "email": "CodeNinja@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1138, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba0"]] + }, + { + "_id": "62f327aa082fcc3049e9250c", + "username": "Ezequiel García", + "email": "Ezequiel García@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2436, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba1"]] + }, + { + "_id": "62f327aa082fcc3049e9250d", + "username": "Carlos Salazar", + "email": "Carlos Salazar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1630, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba2"]] + }, + { + "_id": "62f327aa082fcc3049e9250e", + "username": "ALZlper", + "email": "ALZlper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba3"]] + }, + { + "_id": "62f327aa082fcc3049e92510", + "username": "Kar.ma", + "email": "Kar.ma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 663, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327aa082fcc3049e92512", + "username": "Saqib Omer", + "email": "Saqib Omer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5211, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327aa082fcc3049e92513", + "username": "Vaha", + "email": "Vaha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1659, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba6"]] + }, + { + "_id": "62f327ab082fcc3049e92516", + "username": "stevex", + "email": "stevex@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5345, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ab082fcc3049e92517", + "username": "attacomsian", + "email": "attacomsian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba8"]] + }, + { + "_id": "62f327ab082fcc3049e92518", + "username": "Predrag Davidovic", + "email": "Predrag Davidovic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 911, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90ba9"]] + }, + { + "_id": "62f327ab082fcc3049e92519", + "username": "c z", + "email": "c z@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6615, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feec", "62f321c8082fcc3049e90baa"]] + }, + { + "_id": "62f327ab082fcc3049e9251b", + "username": "Timothy Perez", + "email": "Timothy Perez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19854, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb4"]] + }, + { + "_id": "62f327ab082fcc3049e9251c", + "username": "Warface", + "email": "Warface@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4949, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb5"]] + }, + { + "_id": "62f327ab082fcc3049e9251d", + "username": "Tejasvi Hegde", + "email": "Tejasvi Hegde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2554, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb7"]] + }, + { + "_id": "62f327ab082fcc3049e9251f", + "username": "WASasquatch", + "email": "WASasquatch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 556, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ab082fcc3049e92521", + "username": "RobW", + "email": "RobW@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ab082fcc3049e92523", + "username": "Brian", + "email": "Brian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1028, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ab082fcc3049e92525", + "username": "Atharva", + "email": "Atharva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6207, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb6"]] + }, + { + "_id": "62f327ac082fcc3049e92526", + "username": "Roman Shamritskiy", + "email": "Roman Shamritskiy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 733, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bb9"]] + }, + { + "_id": "62f327ac082fcc3049e92527", + "username": "davidcondrey", + "email": "davidcondrey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32255, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bba"]] + }, + { + "_id": "62f327ac082fcc3049e92528", + "username": "Rezgar Cadro", + "email": "Rezgar Cadro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 394, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bbb"]] + }, + { + "_id": "62f327ac082fcc3049e92529", + "username": "Benjamin Oakes", + "email": "Benjamin Oakes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11853, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bbc"]] + }, + { + "_id": "62f327ac082fcc3049e9252a", + "username": "vascogaspar", + "email": "vascogaspar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bbd"]] + }, + { + "_id": "62f327ac082fcc3049e9252b", + "username": "hashchange", + "email": "hashchange@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6581, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bbf"], + ["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec2"] + ] + }, + { + "_id": "62f327ac082fcc3049e9252c", + "username": "DevWL", + "email": "DevWL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15124, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc2"]] + }, + { + "_id": "62f327ad082fcc3049e9252d", + "username": "svnm", + "email": "svnm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20768, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc5"]] + }, + { + "_id": "62f327ad082fcc3049e9252e", + "username": "Shahdat", + "email": "Shahdat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5134, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc6"]] + }, + { + "_id": "62f327ad082fcc3049e9252f", + "username": "martinh_kentico", + "email": "martinh_kentico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 793, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc3"]] + }, + { + "_id": "62f327ad082fcc3049e92530", + "username": "a11r", + "email": "a11r@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4282, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc8"]] + }, + { + "_id": "62f327ad082fcc3049e92531", + "username": "user7601056", + "email": "user7601056@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc7"]] + }, + { + "_id": "62f327ad082fcc3049e92532", + "username": "Irshad Khan", + "email": "Irshad Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5180, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bc9"]] + }, + { + "_id": "62f327ad082fcc3049e92533", + "username": "cynya", + "email": "cynya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bca"]] + }, + { + "_id": "62f327ad082fcc3049e92534", + "username": "Polaris", + "email": "Polaris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1189, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bcb"]] + }, + { + "_id": "62f327ad082fcc3049e92535", + "username": "object-Object", + "email": "object-Object@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1397, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bcc"]] + }, + { + "_id": "62f327ae082fcc3049e92536", + "username": "Minguocode", + "email": "Minguocode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 74, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bce"]] + }, + { + "_id": "62f327ae082fcc3049e92537", + "username": "stardust4891", + "email": "stardust4891@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2162, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bcd"]] + }, + { + "_id": "62f327ae082fcc3049e92538", + "username": "Edvard Åkerberg", + "email": "Edvard Åkerberg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2071, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bcf"]] + }, + { + "_id": "62f327ae082fcc3049e9253a", + "username": "Nico Haase", + "email": "Nico Haase@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9727, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9253b", + "username": "Jaber Alshami", + "email": "Jaber Alshami@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 186, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bd1"]] + }, + { + "_id": "62f327ae082fcc3049e9253c", + "username": "Vu Viet Hung", + "email": "Vu Viet Hung@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 140, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feee", "62f321c8082fcc3049e90bd2"]] + }, + { + "_id": "62f327ae082fcc3049e9253d", + "username": "DiegoP.", + "email": "DiegoP.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 44147, + "questionIds": ["62f321bb082fcc3049e8feee"], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9253f", + "username": "jontro", + "email": "jontro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9786, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bd8"]] + }, + { + "_id": "62f327ae082fcc3049e92542", + "username": "jfriend00", + "email": "jfriend00@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 645813, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92544", + "username": "J_rite", + "email": "J_rite@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 532, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92546", + "username": "Jonas Wilms", + "email": "Jonas Wilms@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 122703, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bd6"]] + }, + { + "_id": "62f327ae082fcc3049e92548", + "username": "Ori Shalom", + "email": "Ori Shalom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9254a", + "username": "Isaac Abramowitz", + "email": "Isaac Abramowitz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 522, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9254c", + "username": "Pac0", + "email": "Pac0@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19387, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9254e", + "username": "Sterling Archer", + "email": "Sterling Archer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21418, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92550", + "username": "Kevin B", + "email": "Kevin B@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bd7"]] + }, + { + "_id": "62f327ae082fcc3049e92553", + "username": "AncientSwordRage", + "email": "AncientSwordRage@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92555", + "username": "rkosegi", + "email": "rkosegi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13613, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92557", + "username": "Cruncher", + "email": "Cruncher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7441, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92559", + "username": "ryenus", + "email": "ryenus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14188, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9255d", + "username": "Onur", + "email": "Onur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 589, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e9255f", + "username": "hungrykoala", + "email": "hungrykoala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1040, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92562", + "username": "Taemyr", + "email": "Taemyr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3328, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ae082fcc3049e92564", + "username": "Jeff", + "email": "Jeff@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12160, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bd9"]] + }, + { + "_id": "62f327af082fcc3049e92566", + "username": "Dave C", + "email": "Dave C@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 671, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92567", + "username": "Nina Scholz", + "email": "Nina Scholz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 356744, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bdc"]] + }, + { + "_id": "62f327af082fcc3049e92569", + "username": "Makyen", + "email": "Makyen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30222, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9256d", + "username": "Johannes", + "email": "Johannes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 807, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9256e", + "username": "Patrick Dark", + "email": "Patrick Dark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2117, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bdb"]] + }, + { + "_id": "62f327af082fcc3049e92570", + "username": "user743382", + "email": "user743382@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92572", + "username": "Draco18s no longer trusts SE", + "email": "Draco18s no longer trusts SE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15088, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bde"]] + }, + { + "_id": "62f327af082fcc3049e92573", + "username": "MonkeyZeus", + "email": "MonkeyZeus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19782, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bdd"]] + }, + { + "_id": "62f327af082fcc3049e92575", + "username": "Piyin", + "email": "Piyin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1805, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92577", + "username": "Skeets", + "email": "Skeets@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3944, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92579", + "username": "KyleFairns", + "email": "KyleFairns@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2863, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9257a", + "username": "ocomfd", + "email": "ocomfd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3960, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bda"]] + }, + { + "_id": "62f327af082fcc3049e9257c", + "username": "Abdillah", + "email": "Abdillah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 890, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9257f", + "username": "Aleksey Solovey", + "email": "Aleksey Solovey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4113, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92580", + "username": "Kos", + "email": "Kos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4504, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be0"]] + }, + { + "_id": "62f327af082fcc3049e92583", + "username": "Ben Aubin", + "email": "Ben Aubin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5326, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bdf"]] + }, + { + "_id": "62f327af082fcc3049e92585", + "username": "LukeS", + "email": "LukeS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 481, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92587", + "username": "CAD97", + "email": "CAD97@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1110, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92589", + "username": "Tas", + "email": "Tas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6823, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9258b", + "username": "Gustavo Rodríguez", + "email": "Gustavo Rodríguez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 844, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be1"]] + }, + { + "_id": "62f327af082fcc3049e9258d", + "username": "Jason Carr", + "email": "Jason Carr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 164, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e9258f", + "username": "Eric Duminil", + "email": "Eric Duminil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51133, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327af082fcc3049e92593", + "username": "Théophile", + "email": "Théophile@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 985, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be2"]] + }, + { + "_id": "62f327af082fcc3049e92594", + "username": "BaggersIO", + "email": "BaggersIO@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 828, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be3"]] + }, + { + "_id": "62f327b0082fcc3049e92596", + "username": "qntm", + "email": "qntm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3867, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327b0082fcc3049e92598", + "username": "mehulmpt", + "email": "mehulmpt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14871, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be5"]] + }, + { + "_id": "62f327b0082fcc3049e925a0", + "username": "gotofritz", + "email": "gotofritz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3255, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327b0082fcc3049e925a2", + "username": "Preda7or", + "email": "Preda7or@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 298, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be7"]] + }, + { + "_id": "62f327b0082fcc3049e925a7", + "username": "Alfred Armstrong", + "email": "Alfred Armstrong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 238, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327b0082fcc3049e925a8", + "username": "Frank W. Zammetti", + "email": "Frank W. Zammetti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1221, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be4"]] + }, + { + "_id": "62f327b0082fcc3049e925ac", + "username": "Justin Morgan", + "email": "Justin Morgan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29005, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327b0082fcc3049e925ad", + "username": "georg", + "email": "georg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 205704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be6"]] + }, + { + "_id": "62f327ed082fcc3049e925ae", + "username": "Luke Bennett", + "email": "Luke Bennett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32356, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bef"]] + }, + { + "_id": "62f327ed082fcc3049e925af", + "username": "Nguyễn Văn Phong", + "email": "Nguyễn Văn Phong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12698, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bee"]] + }, + { + "_id": "62f327ed082fcc3049e925b0", + "username": "ChadT", + "email": "ChadT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7373, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf0"]] + }, + { + "_id": "62f327ed082fcc3049e925b2", + "username": "Avron Olshewsky", + "email": "Avron Olshewsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf3"]] + }, + { + "_id": "62f327ed082fcc3049e925b4", + "username": "Yevgeniy Afanasyev", + "email": "Yevgeniy Afanasyev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33535, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b04"]] + }, + { + "_id": "62f327ed082fcc3049e925b5", + "username": "Uzbekjon", + "email": "Uzbekjon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11304, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf1"]] + }, + { + "_id": "62f327ed082fcc3049e925b6", + "username": "Curtor", + "email": "Curtor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 727, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf5"]] + }, + { + "_id": "62f327ed082fcc3049e925b8", + "username": "Nischal", + "email": "Nischal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 938, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf4"]] + }, + { + "_id": "62f327ed082fcc3049e925b9", + "username": "Oshua", + "email": "Oshua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 141, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf7"]] + }, + { + "_id": "62f327ee082fcc3049e925ba", + "username": "Pavel Shkleinik", + "email": "Pavel Shkleinik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6198, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bfa"]] + }, + { + "_id": "62f327ee082fcc3049e925bb", + "username": "Alexey Pavlov", + "email": "Alexey Pavlov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 185, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bfb"]] + }, + { + "_id": "62f327ee082fcc3049e925bd", + "username": "Chaos", + "email": "Chaos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 831, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ee082fcc3049e925be", + "username": "Ricky G", + "email": "Ricky G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3352, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bf9"]] + }, + { + "_id": "62f327ee082fcc3049e925c0", + "username": "shashwat", + "email": "shashwat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7535, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bfc"]] + }, + { + "_id": "62f327ee082fcc3049e925c1", + "username": "Jaid07", + "email": "Jaid07@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bfe"]] + }, + { + "_id": "62f327ee082fcc3049e925c2", + "username": "d.raev", + "email": "d.raev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8640, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90bff"]] + }, + { + "_id": "62f327ee082fcc3049e925c3", + "username": "Vivek S", + "email": "Vivek S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1890, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c01"]] + }, + { + "_id": "62f327ee082fcc3049e925c4", + "username": "Praveena M", + "email": "Praveena M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 502, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c00"]] + }, + { + "_id": "62f327ef082fcc3049e925c5", + "username": "SNEH PANDYA", + "email": "SNEH PANDYA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 797, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c02"]] + }, + { + "_id": "62f327ef082fcc3049e925c6", + "username": "GabrielOshiro", + "email": "GabrielOshiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7590, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c03"]] + }, + { + "_id": "62f327ef082fcc3049e925c9", + "username": "sulest", + "email": "sulest@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 966, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c04"]] + }, + { + "_id": "62f327ef082fcc3049e925cb", + "username": "abarisone", + "email": "abarisone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3619, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ef082fcc3049e925cc", + "username": "Jakir Hossain", + "email": "Jakir Hossain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2443, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c07"]] + }, + { + "_id": "62f327ef082fcc3049e925ce", + "username": "Jayan", + "email": "Jayan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17451, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327ef082fcc3049e925cf", + "username": "Pankaj Garg", + "email": "Pankaj Garg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1162, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c06"]] + }, + { + "_id": "62f327ef082fcc3049e925d0", + "username": "Sumon Sarker", + "email": "Sumon Sarker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2576, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c09"]] + }, + { + "_id": "62f327ef082fcc3049e925d1", + "username": "Daniel Ortegón", + "email": "Daniel Ortegón@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0b"]] + }, + { + "_id": "62f327ef082fcc3049e925d3", + "username": "Prashant", + "email": "Prashant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 375, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0a"]] + }, + { + "_id": "62f327f0082fcc3049e925d4", + "username": "rajmobiapp", + "email": "rajmobiapp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 899, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0f"]] + }, + { + "_id": "62f327f0082fcc3049e925d5", + "username": "Rizwan", + "email": "Rizwan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3237, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0e"]] + }, + { + "_id": "62f327f0082fcc3049e925d7", + "username": "Anton vBR", + "email": "Anton vBR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c0c"]] + }, + { + "_id": "62f327f0082fcc3049e925d8", + "username": "Hien Nguyen", + "email": "Hien Nguyen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23293, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c10"]] + }, + { + "_id": "62f327f0082fcc3049e925d9", + "username": "Hemant N. Karmur", + "email": "Hemant N. Karmur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 924, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c11"]] + }, + { + "_id": "62f327f0082fcc3049e925da", + "username": "Nikhil B", + "email": "Nikhil B@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c12"]] + }, + { + "_id": "62f327f1082fcc3049e925dd", + "username": "Russ Cam", + "email": "Russ Cam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121830, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c18"]] + }, + { + "_id": "62f327f1082fcc3049e925df", + "username": "beerwin", + "email": "beerwin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8754, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925e1", + "username": "Sumesh TG", + "email": "Sumesh TG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2470, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925e3", + "username": "mostafa toloo", + "email": "mostafa toloo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feef", "62f321c8082fcc3049e90c16"]] + }, + { + "_id": "62f327f1082fcc3049e925e5", + "username": "jshbrmn", + "email": "jshbrmn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1711, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1c"]] + }, + { + "_id": "62f327f1082fcc3049e925e6", + "username": "Martin Wantke", + "email": "Martin Wantke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3913, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1d"]] + }, + { + "_id": "62f327f1082fcc3049e925e8", + "username": "DiMono", + "email": "DiMono@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3260, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925ea", + "username": "vivek", + "email": "vivek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 337, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1f"]] + }, + { + "_id": "62f327f1082fcc3049e925ec", + "username": "JasonDavis", + "email": "JasonDavis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47026, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925ee", + "username": "user2039981", + "email": "user2039981@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925f0", + "username": "OsamaBinLogin", + "email": "OsamaBinLogin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 571, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925f1", + "username": "TJ L", + "email": "TJ L@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1a"]] + }, + { + "_id": "62f327f1082fcc3049e925f3", + "username": "Adriano Resende", + "email": "Adriano Resende@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2409, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925f5", + "username": "harryg", + "email": "harryg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22242, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925f8", + "username": "Stephan Bijzitter", + "email": "Stephan Bijzitter@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4240, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925fa", + "username": "aashah7", + "email": "aashah7@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1945, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925fb", + "username": "Takács Zsolt", + "email": "Takács Zsolt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 268, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c1e"]] + }, + { + "_id": "62f327f1082fcc3049e925fd", + "username": "Triang3l", + "email": "Triang3l@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1220, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e925ff", + "username": "Rick Eyre", + "email": "Rick Eyre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2205, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e92601", + "username": "Riveascore", + "email": "Riveascore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e92604", + "username": "cregox", + "email": "cregox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16823, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e92606", + "username": "Diptox", + "email": "Diptox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1809, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e92608", + "username": "bevanb", + "email": "bevanb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7771, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e9260a", + "username": "Sarath S Nair", + "email": "Sarath S Nair@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 573, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f1082fcc3049e9260c", + "username": "Taco タコス", + "email": "Taco タコス@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2528, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f2082fcc3049e9260d", + "username": "Steven Johnston", + "email": "Steven Johnston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1639, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c21"]] + }, + { + "_id": "62f327f2082fcc3049e9260e", + "username": "Thalaivar", + "email": "Thalaivar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22562, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c22"]] + }, + { + "_id": "62f327f2082fcc3049e9260f", + "username": "Sachin Kumar", + "email": "Sachin Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2721, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c23"]] + }, + { + "_id": "62f327f2082fcc3049e92610", + "username": "Doug Coburn", + "email": "Doug Coburn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2317, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c24"]] + }, + { + "_id": "62f327f2082fcc3049e92612", + "username": "MagicLAMP", + "email": "MagicLAMP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 957, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f2082fcc3049e92613", + "username": "dutta", + "email": "dutta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c26"]] + }, + { + "_id": "62f327f2082fcc3049e92614", + "username": "Angel Politis", + "email": "Angel Politis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10563, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c29"]] + }, + { + "_id": "62f327f3082fcc3049e92615", + "username": "Muhammad Rizwan", + "email": "Muhammad Rizwan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 348, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2a"]] + }, + { + "_id": "62f327f3082fcc3049e92616", + "username": "Akrion", + "email": "Akrion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2b"]] + }, + { + "_id": "62f327f3082fcc3049e92617", + "username": "Aman Pandey", + "email": "Aman Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 306, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2d"]] + }, + { + "_id": "62f327f3082fcc3049e92618", + "username": "new QOpenGLWidget", + "email": "new QOpenGLWidget@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1894, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c2e"]] + }, + { + "_id": "62f327f3082fcc3049e92619", + "username": "maxxioso", + "email": "maxxioso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c31"]] + }, + { + "_id": "62f327f3082fcc3049e9261b", + "username": "ggorlen", + "email": "ggorlen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35505, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f3082fcc3049e9261c", + "username": "Engr.Aftab Ufaq", + "email": "Engr.Aftab Ufaq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef0", "62f321c9082fcc3049e90c32"]] + }, + { + "_id": "62f327f3082fcc3049e9261d", + "username": "Tilendor", + "email": "Tilendor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47185, + "questionIds": ["62f321bb082fcc3049e8fef0"], + "answerIds": [] + }, + { + "_id": "62f327f3082fcc3049e9261e", + "username": "user35288", + "email": "user35288@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c33"]] + }, + { + "_id": "62f327f4082fcc3049e9261f", + "username": "Jason S", + "email": "Jason S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179829, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c34"], + ["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e35"] + ] + }, + { + "_id": "62f327f4082fcc3049e92621", + "username": "madprog", + "email": "madprog@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 275, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f4082fcc3049e92622", + "username": "moonshadow", + "email": "moonshadow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 82755, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c35"]] + }, + { + "_id": "62f327f4082fcc3049e92623", + "username": "sky", + "email": "sky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c37"]] + }, + { + "_id": "62f327f4082fcc3049e92624", + "username": "sh1mmer", + "email": "sh1mmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1316, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c36"]] + }, + { + "_id": "62f327f4082fcc3049e92625", + "username": "Pravesh Hajela", + "email": "Pravesh Hajela@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c38"]] + }, + { + "_id": "62f327f4082fcc3049e92626", + "username": "foobar", + "email": "foobar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10124, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c39"]] + }, + { + "_id": "62f327f4082fcc3049e92627", + "username": "Programming Guy", + "email": "Programming Guy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6899, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3b"]] + }, + { + "_id": "62f327f4082fcc3049e92629", + "username": "Rodrigo Pinho Pereira de Souza", + "email": "Rodrigo Pinho Pereira de Souza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 416, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3a"]] + }, + { + "_id": "62f327f5082fcc3049e9262a", + "username": "stay_hungry", + "email": "stay_hungry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3e"]] + }, + { + "_id": "62f327f5082fcc3049e9262b", + "username": "jwchang", + "email": "jwchang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10351, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c3f"]] + }, + { + "_id": "62f327f5082fcc3049e9262c", + "username": "Yukulélé", + "email": "Yukulélé@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c40"]] + }, + { + "_id": "62f327f5082fcc3049e9262d", + "username": "user1931504", + "email": "user1931504@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c42"]] + }, + { + "_id": "62f327f5082fcc3049e9262e", + "username": "Daniel Lidström", + "email": "Daniel Lidström@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c43"]] + }, + { + "_id": "62f327f5082fcc3049e9262f", + "username": "Brijesh", + "email": "Brijesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c45"]] + }, + { + "_id": "62f327f5082fcc3049e92630", + "username": "Júlio Paulillo", + "email": "Júlio Paulillo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 596, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c47"]] + }, + { + "_id": "62f327f6082fcc3049e92632", + "username": "Isochronous", + "email": "Isochronous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1046, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f6082fcc3049e92633", + "username": "iinvole", + "email": "iinvole@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c48"]] + }, + { + "_id": "62f327f6082fcc3049e92634", + "username": "vickisys", + "email": "vickisys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1948, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4b"]] + }, + { + "_id": "62f327f6082fcc3049e92635", + "username": "hex494D49", + "email": "hex494D49@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8767, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4a"]] + }, + { + "_id": "62f327f6082fcc3049e92636", + "username": "Razan Paul", + "email": "Razan Paul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13152, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4d"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee1"], + ["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91038"] + ] + }, + { + "_id": "62f327f6082fcc3049e92637", + "username": "Qasim", + "email": "Qasim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7939, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4c"]] + }, + { + "_id": "62f327f6082fcc3049e92638", + "username": "yoniLavi", + "email": "yoniLavi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2467, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4f"]] + }, + { + "_id": "62f327f6082fcc3049e92639", + "username": "Salahin Rocky", + "email": "Salahin Rocky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 385, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c4e"]] + }, + { + "_id": "62f327f6082fcc3049e9263a", + "username": "user4439128", + "email": "user4439128@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c51"]] + }, + { + "_id": "62f327f6082fcc3049e9263c", + "username": "Herve Mutombo", + "email": "Herve Mutombo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 170, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f6082fcc3049e9263d", + "username": "Sanjeev Singh", + "email": "Sanjeev Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3850, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c50"]] + }, + { + "_id": "62f327f7082fcc3049e9263e", + "username": "bbsimonbb", + "email": "bbsimonbb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24642, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c54"]] + }, + { + "_id": "62f327f7082fcc3049e9263f", + "username": "Bachas", + "email": "Bachas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c53"]] + }, + { + "_id": "62f327f7082fcc3049e92641", + "username": "TheMaster", + "email": "TheMaster@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39152, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92643", + "username": "wrlee", + "email": "wrlee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1877, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92644", + "username": "MICHAEL PRABHU", + "email": "MICHAEL PRABHU@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c55"]] + }, + { + "_id": "62f327f7082fcc3049e92646", + "username": "phoenixstudio", + "email": "phoenixstudio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92647", + "username": "Obot Ernest", + "email": "Obot Ernest@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 167, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c57"]] + }, + { + "_id": "62f327f7082fcc3049e92648", + "username": "Aaquib Jawed", + "email": "Aaquib Jawed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c59"]] + }, + { + "_id": "62f327f7082fcc3049e9264a", + "username": "WebDude0482", + "email": "WebDude0482@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 764, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e9264b", + "username": "Sohail Shahzad", + "email": "Sohail Shahzad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c58"]] + }, + { + "_id": "62f327f7082fcc3049e9264c", + "username": "Guvanch", + "email": "Guvanch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 589, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef1", "62f321c9082fcc3049e90c5a"]] + }, + { + "_id": "62f327f7082fcc3049e9264e", + "username": "Entretoize", + "email": "Entretoize@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2031, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92651", + "username": "Seraf", + "email": "Seraf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 780, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92653", + "username": "Chris Stryczynski", + "email": "Chris Stryczynski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25985, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92656", + "username": "d.popov", + "email": "d.popov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4049, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e92658", + "username": "Ryan Haining", + "email": "Ryan Haining@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33297, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e9265a", + "username": "shamaseen", + "email": "shamaseen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1728, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f7082fcc3049e9265b", + "username": "Sarfraz", + "email": "Sarfraz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 369350, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c68"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93139"] + ] + }, + { + "_id": "62f327f8082fcc3049e9265d", + "username": "Eran Medan", + "email": "Eran Medan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43646, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e9265e", + "username": "Chetan S", + "email": "Chetan S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23257, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c69"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313a"] + ] + }, + { + "_id": "62f327f8082fcc3049e92660", + "username": "Chuck", + "email": "Chuck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 229808, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92663", + "username": "Web_Designer", + "email": "Web_Designer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69336, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92666", + "username": "user664833", + "email": "user664833@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17177, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92667", + "username": "user216441", + "email": "user216441@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6a"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313b"] + ] + }, + { + "_id": "62f327f8082fcc3049e92668", + "username": "Welshboy", + "email": "Welshboy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 292, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6c"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313d"] + ] + }, + { + "_id": "62f327f8082fcc3049e9266a", + "username": "Frozen Crayon", + "email": "Frozen Crayon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4852, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e9266c", + "username": "Abdul Sadik Yalcin", + "email": "Abdul Sadik Yalcin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1614, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e9266e", + "username": "March Ho", + "email": "March Ho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 390, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92670", + "username": "Uriahs Victor", + "email": "Uriahs Victor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 915, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92671", + "username": "jkindwall", + "email": "jkindwall@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3556, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6b"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9313c"] + ] + }, + { + "_id": "62f327f8082fcc3049e92674", + "username": "Mani Gandham", + "email": "Mani Gandham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6646, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92677", + "username": "Pankaj Singh", + "email": "Pankaj Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92679", + "username": "Omer", + "email": "Omer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 524, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e9267c", + "username": "David J", + "email": "David J@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 915, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e9267d", + "username": "Jones Agyemang", + "email": "Jones Agyemang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1009, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c6f"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93140"] + ] + }, + { + "_id": "62f327f8082fcc3049e9267f", + "username": "Kapil", + "email": "Kapil@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 193, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c71"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93142"] + ] + }, + { + "_id": "62f327f8082fcc3049e92681", + "username": "villamejia", + "email": "villamejia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 311, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92683", + "username": "Rudy", + "email": "Rudy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2263, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f8082fcc3049e92686", + "username": "hungerstar", + "email": "hungerstar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20538, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f9082fcc3049e92689", + "username": "Ben McIntyre", + "email": "Ben McIntyre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1894, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327f9082fcc3049e9268a", + "username": "phil294", + "email": "phil294@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9382, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c74"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93145"] + ] + }, + { + "_id": "62f327f9082fcc3049e9268b", + "username": "keshav", + "email": "keshav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 676, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c73"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93144"] + ] + }, + { + "_id": "62f327f9082fcc3049e9268d", + "username": "DanKodi", + "email": "DanKodi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3392, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c75"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93146"] + ] + }, + { + "_id": "62f327f9082fcc3049e9268e", + "username": "Suhail", + "email": "Suhail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 316, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c76"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93147"] + ] + }, + { + "_id": "62f327f9082fcc3049e9268f", + "username": "Nishanth Matha", + "email": "Nishanth Matha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5915, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c77"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93148"] + ] + }, + { + "_id": "62f327f9082fcc3049e92691", + "username": "sadmicrowave", + "email": "sadmicrowave@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38138, + "questionIds": ["62f321bb082fcc3049e8fef5", "62f32a73082fcc3049e93137"], + "answerIds": [] + }, + { + "_id": "62f327f9082fcc3049e92693", + "username": "n1kkou", + "email": "n1kkou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3016, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c79"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314a"] + ] + }, + { + "_id": "62f327f9082fcc3049e92696", + "username": "gideon", + "email": "gideon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7a"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314b"] + ] + }, + { + "_id": "62f327f9082fcc3049e92698", + "username": "Albert.Qing", + "email": "Albert.Qing@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3940, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7b"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314c"] + ] + }, + { + "_id": "62f327f9082fcc3049e9269b", + "username": "Tony Tai Nguyen", + "email": "Tony Tai Nguyen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1394, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7c"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314d"] + ] + }, + { + "_id": "62f327fa082fcc3049e9269c", + "username": "Franklin Pious", + "email": "Franklin Pious@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3390, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7d"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314e"] + ] + }, + { + "_id": "62f327fa082fcc3049e9269e", + "username": "byxor", + "email": "byxor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5354, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fa082fcc3049e926a0", + "username": "M. Arnold", + "email": "M. Arnold@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 361, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7e"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9314f"] + ] + }, + { + "_id": "62f327fa082fcc3049e926a2", + "username": "gast128", + "email": "gast128@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1133, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fa082fcc3049e926a3", + "username": "userPlus", + "email": "userPlus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 301, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c80"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93151"] + ] + }, + { + "_id": "62f327fa082fcc3049e926a4", + "username": "agc", + "email": "agc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c7f"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93150"] + ] + }, + { + "_id": "62f327fa082fcc3049e926a6", + "username": "dhilt", + "email": "dhilt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16382, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c84"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93155"] + ] + }, + { + "_id": "62f327fa082fcc3049e926a8", + "username": "Hardik Desai", + "email": "Hardik Desai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 688, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c81"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93152"] + ] + }, + { + "_id": "62f327fa082fcc3049e926aa", + "username": "IceFire", + "email": "IceFire@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3888, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fa082fcc3049e926ad", + "username": "Anjana Kumari", + "email": "Anjana Kumari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 78, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c83"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93154"] + ] + }, + { + "_id": "62f327fa082fcc3049e926ae", + "username": "Ernesto", + "email": "Ernesto@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3419, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c85"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93156"] + ] + }, + { + "_id": "62f327fa082fcc3049e926b0", + "username": "Joao Oliveira Rocha", + "email": "Joao Oliveira Rocha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 151, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fa082fcc3049e926b1", + "username": "Dante_97", + "email": "Dante_97@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 75, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c86"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93157"] + ] + }, + { + "_id": "62f327fb082fcc3049e926b2", + "username": "dkburd", + "email": "dkburd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c87"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93158"] + ] + }, + { + "_id": "62f327fb082fcc3049e926b3", + "username": "Ashan Priyadarshana", + "email": "Ashan Priyadarshana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2449, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c88"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e93159"] + ] + }, + { + "_id": "62f327fb082fcc3049e926b4", + "username": "noseratio", + "email": "noseratio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58417, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c8a"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9315b"] + ] + }, + { + "_id": "62f327fb082fcc3049e926b5", + "username": "Bhavya Koshiya", + "email": "Bhavya Koshiya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 706, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef5", "62f321ca082fcc3049e90c89"], + ["62f32a73082fcc3049e93137", "62f32a74082fcc3049e9315a"] + ] + }, + { + "_id": "62f327fb082fcc3049e926b6", + "username": "Harini Sekar", + "email": "Harini Sekar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 690, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c8e"]] + }, + { + "_id": "62f327fb082fcc3049e926b8", + "username": "Floww", + "email": "Floww@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 64, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926b9", + "username": "daVe", + "email": "daVe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 899, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c90"]] + }, + { + "_id": "62f327fb082fcc3049e926bb", + "username": "crazymykl", + "email": "crazymykl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 514, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926bd", + "username": "SepehrM", + "email": "SepehrM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1068, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926be", + "username": "geekbuntu", + "email": "geekbuntu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 851, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c8d"]] + }, + { + "_id": "62f327fb082fcc3049e926c0", + "username": "basic6", + "email": "basic6@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3373, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926c2", + "username": "cjsimon", + "email": "cjsimon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1030, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926c4", + "username": "Sajjad Shirazy", + "email": "Sajjad Shirazy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2413, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c8f"]] + }, + { + "_id": "62f327fb082fcc3049e926c6", + "username": "Nicu Surdu", + "email": "Nicu Surdu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7792, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c91"]] + }, + { + "_id": "62f327fb082fcc3049e926c8", + "username": "OneChillDude", + "email": "OneChillDude@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7628, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926ca", + "username": "nullability", + "email": "nullability@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10245, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926cc", + "username": "dbrin", + "email": "dbrin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926ce", + "username": "wings", + "email": "wings@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 771, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926d0", + "username": "Henrik Erlandsson", + "email": "Henrik Erlandsson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3701, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926d2", + "username": "quemeful", + "email": "quemeful@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9027, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fb082fcc3049e926d4", + "username": "Jerry King", + "email": "Jerry King@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 334, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f327fc082fcc3049e926d5", + "username": "user3831708", + "email": "user3831708@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c92"]] + }, + { + "_id": "62f327fc082fcc3049e926d6", + "username": "Atif Hussain", + "email": "Atif Hussain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 850, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c93"]] + }, + { + "_id": "62f327fc082fcc3049e926d7", + "username": "Imants Volkovs", + "email": "Imants Volkovs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 872, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c94"]] + }, + { + "_id": "62f32839082fcc3049e926d8", + "username": "Siddhartha", + "email": "Siddhartha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c9c"]] + }, + { + "_id": "62f32839082fcc3049e926d9", + "username": "mukhtar alam", + "email": "mukhtar alam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 175, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c9e"]] + }, + { + "_id": "62f32839082fcc3049e926db", + "username": "Demonbane", + "email": "Demonbane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 339, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926dd", + "username": "arve0", + "email": "arve0@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3059, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926e1", + "username": "doubleOrt", + "email": "doubleOrt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2309, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926e3", + "username": "leonheess", + "email": "leonheess@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11184, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926e7", + "username": "Griffi", + "email": "Griffi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 233, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926ea", + "username": "Augustin Riedinger", + "email": "Augustin Riedinger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19156, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32839082fcc3049e926ee", + "username": "Hooman Askari", + "email": "Hooman Askari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1355, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca1"]] + }, + { + "_id": "62f32839082fcc3049e926ef", + "username": "Antonio Val", + "email": "Antonio Val@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca0"]] + }, + { + "_id": "62f32839082fcc3049e926f0", + "username": "LeOn - Han Li", + "email": "LeOn - Han Li@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8342, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca3"]] + }, + { + "_id": "62f32839082fcc3049e926f2", + "username": "Jay Edwards", + "email": "Jay Edwards@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 860, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca2"]] + }, + { + "_id": "62f32839082fcc3049e926f3", + "username": "Zachary Ryan Smith", + "email": "Zachary Ryan Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2560, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca4"]] + }, + { + "_id": "62f3283a082fcc3049e926f4", + "username": "Babakness", + "email": "Babakness@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2704, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca6"]] + }, + { + "_id": "62f3283a082fcc3049e926f6", + "username": "Damien Romito", + "email": "Damien Romito@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9422, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e926f8", + "username": "parrker9", + "email": "parrker9@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e926fb", + "username": "Timothy Zorn", + "email": "Timothy Zorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2607, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca8"]] + }, + { + "_id": "62f3283a082fcc3049e926fd", + "username": "user7075574", + "email": "user7075574@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e926ff", + "username": "Remi Mélisson", + "email": "Remi Mélisson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92701", + "username": "Bigyan Devkota", + "email": "Bigyan Devkota@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 70, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92703", + "username": "Andrew Smith", + "email": "Andrew Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1346, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92705", + "username": "Vadim Shvetsov", + "email": "Vadim Shvetsov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1576, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92707", + "username": "jib", + "email": "jib@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38079, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92709", + "username": "user13548229", + "email": "user13548229@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e9270a", + "username": "Cisco", + "email": "Cisco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17812, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90ca9"]] + }, + { + "_id": "62f3283a082fcc3049e9270b", + "username": "Scott Rudiger", + "email": "Scott Rudiger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1146, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90caa"]] + }, + { + "_id": "62f3283a082fcc3049e9270c", + "username": "Beau", + "email": "Beau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cab"]] + }, + { + "_id": "62f3283a082fcc3049e9270e", + "username": "PranavKAndro", + "email": "PranavKAndro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 891, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cad"]] + }, + { + "_id": "62f3283a082fcc3049e9270f", + "username": "master_dodo", + "email": "master_dodo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1082, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cac"]] + }, + { + "_id": "62f3283a082fcc3049e92710", + "username": "gsaandy", + "email": "gsaandy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 516, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cae"]] + }, + { + "_id": "62f3283a082fcc3049e92712", + "username": "JulienRioux", + "email": "JulienRioux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2509, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283a082fcc3049e92713", + "username": "Oliver Dixon", + "email": "Oliver Dixon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6596, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90caf"]] + }, + { + "_id": "62f3283b082fcc3049e92715", + "username": "Mark Odey", + "email": "Mark Odey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283b082fcc3049e92716", + "username": "richytong", + "email": "richytong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2322, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb0"]] + }, + { + "_id": "62f3283b082fcc3049e92717", + "username": "Wojciech Maj", + "email": "Wojciech Maj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 902, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb1"]] + }, + { + "_id": "62f3283b082fcc3049e92719", + "username": "srmark", + "email": "srmark@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7732, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283b082fcc3049e9271a", + "username": "yeah22", + "email": "yeah22@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 308, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb3"]] + }, + { + "_id": "62f3283b082fcc3049e9271b", + "username": "Johnz", + "email": "Johnz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 261, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb5"]] + }, + { + "_id": "62f3283b082fcc3049e9271c", + "username": "ChenZeTong", + "email": "ChenZeTong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 95, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb4"]] + }, + { + "_id": "62f3283b082fcc3049e9271e", + "username": "close", + "email": "close@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283b082fcc3049e9271f", + "username": "krupesh Anadkat", + "email": "krupesh Anadkat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1503, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb6"]] + }, + { + "_id": "62f3283b082fcc3049e92722", + "username": "Matt Janssen", + "email": "Matt Janssen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1346, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb7"]] + }, + { + "_id": "62f3283b082fcc3049e92724", + "username": "Jatin Saradgikar", + "email": "Jatin Saradgikar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 311, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cb8"]] + }, + { + "_id": "62f3283c082fcc3049e92726", + "username": "tenbits", + "email": "tenbits@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6922, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef2", "62f321ca082fcc3049e90cbb"]] + }, + { + "_id": "62f3283c082fcc3049e92727", + "username": "Saad", + "email": "Saad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43219, + "questionIds": ["62f321bb082fcc3049e8fef2"], + "answerIds": [] + }, + { + "_id": "62f3283c082fcc3049e92728", + "username": "Michael Haren", + "email": "Michael Haren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 102274, + "questionIds": ["62f321bb082fcc3049e8fef3"], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cbc"]] + }, + { + "_id": "62f3283c082fcc3049e92729", + "username": "pottedmeat", + "email": "pottedmeat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cbd"]] + }, + { + "_id": "62f3283c082fcc3049e9272a", + "username": "Marius", + "email": "Marius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56215, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cbe"]] + }, + { + "_id": "62f3283c082fcc3049e9272b", + "username": "bubbassauro", + "email": "bubbassauro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3574, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cbf"]] + }, + { + "_id": "62f3283c082fcc3049e9272c", + "username": "Aquatic", + "email": "Aquatic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5019, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc1"]] + }, + { + "_id": "62f3283c082fcc3049e9272d", + "username": "travis", + "email": "travis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34927, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc0"]] + }, + { + "_id": "62f3283c082fcc3049e9272e", + "username": "Joel Coehoorn", + "email": "Joel Coehoorn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 382952, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc2"]] + }, + { + "_id": "62f3283c082fcc3049e9272f", + "username": "camomileCase", + "email": "camomileCase@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1675, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc3"]] + }, + { + "_id": "62f3283d082fcc3049e92730", + "username": "user189277", + "email": "user189277@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc4"]] + }, + { + "_id": "62f3283d082fcc3049e92732", + "username": "InsertNameHere", + "email": "InsertNameHere@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc6"]] + }, + { + "_id": "62f3283d082fcc3049e92733", + "username": "user532188", + "email": "user532188@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc7"]] + }, + { + "_id": "62f3283d082fcc3049e92734", + "username": "jberenguer", + "email": "jberenguer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc8"]] + }, + { + "_id": "62f3283d082fcc3049e92735", + "username": "Manusoftar", + "email": "Manusoftar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cca"]] + }, + { + "_id": "62f3283d082fcc3049e92736", + "username": "jayakumar", + "email": "jayakumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cc9"]] + }, + { + "_id": "62f3283d082fcc3049e92737", + "username": "solidarius", + "email": "solidarius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ccb"]] + }, + { + "_id": "62f3283d082fcc3049e92738", + "username": "Doctor Rudolf", + "email": "Doctor Rudolf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 322, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ccc"]] + }, + { + "_id": "62f3283d082fcc3049e92739", + "username": "Rafael", + "email": "Rafael@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ccd"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d53"] + ] + }, + { + "_id": "62f3283e082fcc3049e9273a", + "username": "Hans Schmucker", + "email": "Hans Schmucker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cce"]] + }, + { + "_id": "62f3283e082fcc3049e9273c", + "username": "Janus Troelsen", + "email": "Janus Troelsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19371, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283e082fcc3049e9273d", + "username": "Ali Gonabadi", + "email": "Ali Gonabadi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 924, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ccf"]] + }, + { + "_id": "62f3283e082fcc3049e9273e", + "username": "Kuf", + "email": "Kuf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd1"]] + }, + { + "_id": "62f3283e082fcc3049e9273f", + "username": "bob", + "email": "bob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6927, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd0"]] + }, + { + "_id": "62f3283e082fcc3049e92740", + "username": "NaveenKumar1410", + "email": "NaveenKumar1410@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1545, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd2"]] + }, + { + "_id": "62f3283e082fcc3049e92741", + "username": "hobs", + "email": "hobs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17066, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd5"]] + }, + { + "_id": "62f3283e082fcc3049e92742", + "username": "Mr Br", + "email": "Mr Br@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3613, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd6"]] + }, + { + "_id": "62f3283e082fcc3049e92745", + "username": "Alexis Wilke", + "email": "Alexis Wilke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17532, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283e082fcc3049e92746", + "username": "Aaron Gong", + "email": "Aaron Gong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 927, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd7"]] + }, + { + "_id": "62f3283f082fcc3049e92747", + "username": "Sean the Bean", + "email": "Sean the Bean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4972, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cd9"]] + }, + { + "_id": "62f3283f082fcc3049e92748", + "username": "donquixote", + "email": "donquixote@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4426, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cda"]] + }, + { + "_id": "62f3283f082fcc3049e9274a", + "username": "jkdev", + "email": "jkdev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10614, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3283f082fcc3049e9274b", + "username": "Nik", + "email": "Nik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6954, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cdb"]] + }, + { + "_id": "62f3283f082fcc3049e9274c", + "username": "Simon Hi", + "email": "Simon Hi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2738, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cdc"]] + }, + { + "_id": "62f3283f082fcc3049e9274d", + "username": "studio-klik", + "email": "studio-klik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 198, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cdf"]] + }, + { + "_id": "62f32840082fcc3049e9274e", + "username": "paulalexandru", + "email": "paulalexandru@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9011, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce3"]] + }, + { + "_id": "62f32840082fcc3049e92750", + "username": "vinoth", + "email": "vinoth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32840082fcc3049e92751", + "username": "chrmcpn", + "email": "chrmcpn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 554, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce5"]] + }, + { + "_id": "62f32840082fcc3049e92752", + "username": "John Mikic", + "email": "John Mikic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 632, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce4"]] + }, + { + "_id": "62f32840082fcc3049e92753", + "username": "Saurabh Chandra Patel", + "email": "Saurabh Chandra Patel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11807, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce7"]] + }, + { + "_id": "62f32840082fcc3049e92754", + "username": "smsmware", + "email": "smsmware@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2886, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce6"]] + }, + { + "_id": "62f32840082fcc3049e92755", + "username": "Vixed", + "email": "Vixed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce8"]] + }, + { + "_id": "62f32840082fcc3049e92756", + "username": "Mhmdrz_A", + "email": "Mhmdrz_A@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4384, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90cea"]] + }, + { + "_id": "62f32840082fcc3049e92757", + "username": "Alston", + "email": "Alston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1126, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ce9"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0d"] + ] + }, + { + "_id": "62f32840082fcc3049e92758", + "username": "MarredCheese", + "email": "MarredCheese@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14188, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ceb"]] + }, + { + "_id": "62f32841082fcc3049e92759", + "username": "Mykola Uspalenko", + "email": "Mykola Uspalenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 88, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef3", "62f321ca082fcc3049e90ced"]] + }, + { + "_id": "62f32841082fcc3049e9275a", + "username": "Vignesh", + "email": "Vignesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf1"]] + }, + { + "_id": "62f32841082fcc3049e9275d", + "username": "Fattie", + "email": "Fattie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33019, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e9275e", + "username": "CaffGeek", + "email": "CaffGeek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21462, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cef"]] + }, + { + "_id": "62f32841082fcc3049e92760", + "username": "kennebec", + "email": "kennebec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99649, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf0"], + ["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90584"] + ] + }, + { + "_id": "62f32841082fcc3049e92761", + "username": "Roderick ", + "email": "Roderick @fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 247, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf2"]] + }, + { + "_id": "62f32841082fcc3049e92763", + "username": "AlexGrafe", + "email": "AlexGrafe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7862, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92767", + "username": "Sagive", + "email": "Sagive@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1544, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92768", + "username": "Gajus", + "email": "Gajus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63842, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf5"]] + }, + { + "_id": "62f32841082fcc3049e9276e", + "username": "csharptest.net", + "email": "csharptest.net@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59148, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cee"]] + }, + { + "_id": "62f32841082fcc3049e92770", + "username": "notrota", + "email": "notrota@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1008, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92772", + "username": "Gerben Rampaart", + "email": "Gerben Rampaart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9753, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92776", + "username": "jberryman", + "email": "jberryman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16020, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92778", + "username": "dragon", + "email": "dragon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1697, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e9277a", + "username": "Nux", + "email": "Nux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8274, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e9277c", + "username": "gertas", + "email": "gertas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e9277e", + "username": "Michael Deal", + "email": "Michael Deal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2788, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92780", + "username": "George Reith", + "email": "George Reith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12802, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92782", + "username": "Briguy37", + "email": "Briguy37@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8222, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92784", + "username": "user1902830", + "email": "user1902830@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92787", + "username": "Mehdi Dehghani", + "email": "Mehdi Dehghani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9881, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32841082fcc3049e92789", + "username": "doubletap", + "email": "doubletap@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29797, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf3"]] + }, + { + "_id": "62f32842082fcc3049e9278a", + "username": "ropsiU", + "email": "ropsiU@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf6"]] + }, + { + "_id": "62f32842082fcc3049e9278c", + "username": "hacklikecrack", + "email": "hacklikecrack@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1340, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32842082fcc3049e9278d", + "username": "Martijn de Milliano", + "email": "Martijn de Milliano@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3758, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf7"]] + }, + { + "_id": "62f32842082fcc3049e9278e", + "username": "Steven DAmico", + "email": "Steven DAmico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 321, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf8"]] + }, + { + "_id": "62f32842082fcc3049e9278f", + "username": "qubyte", + "email": "qubyte@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17008, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cfa"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cfb"] + ] + }, + { + "_id": "62f32842082fcc3049e92790", + "username": "nclu", + "email": "nclu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1059, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cf9"]] + }, + { + "_id": "62f32842082fcc3049e92792", + "username": "erkanyildiz", + "email": "erkanyildiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12908, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32842082fcc3049e92793", + "username": "Adam", + "email": "Adam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 349, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cfc"], + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e54"] + ] + }, + { + "_id": "62f32842082fcc3049e92795", + "username": "brunoais", + "email": "brunoais@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5522, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32842082fcc3049e92796", + "username": "Andrew Plank", + "email": "Andrew Plank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 842, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90cfe"]] + }, + { + "_id": "62f32843082fcc3049e92797", + "username": "mindrones", + "email": "mindrones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1125, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d00"]] + }, + { + "_id": "62f32843082fcc3049e92798", + "username": "Collin Anderson", + "email": "Collin Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13847, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d01"]] + }, + { + "_id": "62f32843082fcc3049e92799", + "username": "bendytree", + "email": "bendytree@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12369, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d02"]] + }, + { + "_id": "62f32843082fcc3049e9279a", + "username": "Dinis Cruz", + "email": "Dinis Cruz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d04"]] + }, + { + "_id": "62f32843082fcc3049e9279b", + "username": "James Harrington", + "email": "James Harrington@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3100, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d03"]] + }, + { + "_id": "62f32843082fcc3049e9279d", + "username": "Ronaldo Bahia", + "email": "Ronaldo Bahia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 526, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e9279f", + "username": "Mulan", + "email": "Mulan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121278, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d06"], + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0e"] + ] + }, + { + "_id": "62f32843082fcc3049e927a1", + "username": "mythicalcoder", + "email": "mythicalcoder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2943, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927a3", + "username": "Abhinav Gauniyal", + "email": "Abhinav Gauniyal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927a5", + "username": "Esko", + "email": "Esko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3998, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927a7", + "username": "Rockallite", + "email": "Rockallite@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15685, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927a9", + "username": "Amadan", + "email": "Amadan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 181498, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927ab", + "username": "jerrymouse", + "email": "jerrymouse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15716, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d31"]] + }, + { + "_id": "62f32843082fcc3049e927ad", + "username": "user1115652", + "email": "user1115652@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d07"]] + }, + { + "_id": "62f32843082fcc3049e927af", + "username": "seniorpreacher", + "email": "seniorpreacher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 656, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927b1", + "username": "user.friendly", + "email": "user.friendly@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927b2", + "username": "MasqueradeCircus", + "email": "MasqueradeCircus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 796, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d08"]] + }, + { + "_id": "62f32843082fcc3049e927b4", + "username": "Yefu", + "email": "Yefu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32843082fcc3049e927b5", + "username": "tiktak", + "email": "tiktak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1771, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d09"]] + }, + { + "_id": "62f32844082fcc3049e927b7", + "username": "x-ray", + "email": "x-ray@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3169, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927b9", + "username": "user1506145", + "email": "user1506145@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5016, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0b"]] + }, + { + "_id": "62f32844082fcc3049e927bb", + "username": "marlo", + "email": "marlo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927bc", + "username": "vineet", + "email": "vineet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13015, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0c"], + ["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5f"] + ] + }, + { + "_id": "62f32844082fcc3049e927be", + "username": "user6445533", + "email": "user6445533@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927c0", + "username": "Michael Litvin", + "email": "Michael Litvin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3724, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927c3", + "username": "rinogo", + "email": "rinogo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7799, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927c5", + "username": "Gooz", + "email": "Gooz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1026, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927c7", + "username": "Silver Ringvee", + "email": "Silver Ringvee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4417, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d0f"]] + }, + { + "_id": "62f32844082fcc3049e927c9", + "username": "RyanNerd", + "email": "RyanNerd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2909, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927cb", + "username": "user285594", + "email": "user285594@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927cc", + "username": "Lukasz Wiktor", + "email": "Lukasz Wiktor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18767, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d12"]] + }, + { + "_id": "62f32844082fcc3049e927cd", + "username": "aleung", + "email": "aleung@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d10"]] + }, + { + "_id": "62f32844082fcc3049e927cf", + "username": "cowbert", + "email": "cowbert@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2979, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32844082fcc3049e927d0", + "username": "yaroslav", + "email": "yaroslav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 168, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d11"]] + }, + { + "_id": "62f32844082fcc3049e927d2", + "username": "CMS", + "email": "CMS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3557, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d13"]] + }, + { + "_id": "62f32845082fcc3049e927d5", + "username": "anil kumar Dyavanapalli", + "email": "anil kumar Dyavanapalli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d15"]] + }, + { + "_id": "62f32845082fcc3049e927d6", + "username": "miodus", + "email": "miodus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 83, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d19"]] + }, + { + "_id": "62f32845082fcc3049e927d7", + "username": "K-Gun", + "email": "K-Gun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10887, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d18"]] + }, + { + "_id": "62f32845082fcc3049e927d8", + "username": "artnikpro", + "email": "artnikpro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5066, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d17"]] + }, + { + "_id": "62f32845082fcc3049e927d9", + "username": "Swergas", + "email": "Swergas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 334, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1a"]] + }, + { + "_id": "62f32845082fcc3049e927da", + "username": "Sparw", + "email": "Sparw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2592, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1b"]] + }, + { + "_id": "62f32845082fcc3049e927db", + "username": "ravishi", + "email": "ravishi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1d"]] + }, + { + "_id": "62f32845082fcc3049e927dd", + "username": "vuza", + "email": "vuza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2324, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32845082fcc3049e927df", + "username": "Nahuel Greco", + "email": "Nahuel Greco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 890, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2a"]] + }, + { + "_id": "62f32845082fcc3049e927e1", + "username": "tam.teixeira", + "email": "tam.teixeira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 731, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32845082fcc3049e927e3", + "username": "Hoxz", + "email": "Hoxz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32845082fcc3049e927e4", + "username": "Or Duan", + "email": "Or Duan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11868, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1c"]] + }, + { + "_id": "62f32846082fcc3049e927e5", + "username": "user1300214", + "email": "user1300214@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1e"]] + }, + { + "_id": "62f32846082fcc3049e927e7", + "username": "Leif", + "email": "Leif@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2073, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32846082fcc3049e927e9", + "username": "Sergio Cabral", + "email": "Sergio Cabral@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5724, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d20"]] + }, + { + "_id": "62f32846082fcc3049e927ea", + "username": "Automatico", + "email": "Automatico@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11642, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d1f"]] + }, + { + "_id": "62f32846082fcc3049e927eb", + "username": "Pascual Muñoz", + "email": "Pascual Muñoz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d21"]] + }, + { + "_id": "62f32846082fcc3049e927ec", + "username": "Waruyama", + "email": "Waruyama@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d22"]] + }, + { + "_id": "62f32846082fcc3049e927ed", + "username": "Elias Zamaria", + "email": "Elias Zamaria@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 90453, + "questionIds": ["62f321bb082fcc3049e8fefb"], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d25"]] + }, + { + "_id": "62f32847082fcc3049e927f0", + "username": "Sibevin Wang", + "email": "Sibevin Wang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4290, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d29"]] + }, + { + "_id": "62f32847082fcc3049e927f1", + "username": "Nixinova", + "email": "Nixinova@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 397, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2b"]] + }, + { + "_id": "62f32847082fcc3049e927f3", + "username": "iamarkadyt", + "email": "iamarkadyt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2090, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2c"]] + }, + { + "_id": "62f32847082fcc3049e927f6", + "username": "Rocky Kev", + "email": "Rocky Kev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 405, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32847082fcc3049e927f8", + "username": "Olowu Abayomi", + "email": "Olowu Abayomi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2d"]] + }, + { + "_id": "62f32847082fcc3049e927f9", + "username": "Marc", + "email": "Marc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11552, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d2e"]] + }, + { + "_id": "62f32848082fcc3049e927fa", + "username": "user7793758", + "email": "user7793758@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d33"]] + }, + { + "_id": "62f32848082fcc3049e927fb", + "username": "Zee", + "email": "Zee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 764, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d34"]] + }, + { + "_id": "62f32848082fcc3049e927fc", + "username": "Dominicentek Gaming", + "email": "Dominicentek Gaming@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d35"]] + }, + { + "_id": "62f32848082fcc3049e927fd", + "username": "gogo", + "email": "gogo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 785, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d36"]] + }, + { + "_id": "62f32848082fcc3049e927fe", + "username": "Don Dilanga", + "email": "Don Dilanga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d37"]] + }, + { + "_id": "62f32848082fcc3049e92800", + "username": "Filip Seman", + "email": "Filip Seman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 788, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3e"], + ["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d95"] + ] + }, + { + "_id": "62f32848082fcc3049e92801", + "username": "Denys Vitali", + "email": "Denys Vitali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 530, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d38"]] + }, + { + "_id": "62f32848082fcc3049e92802", + "username": "TheBotlyNoob", + "email": "TheBotlyNoob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d39"]] + }, + { + "_id": "62f32848082fcc3049e92803", + "username": "Siarhei Dudko", + "email": "Siarhei Dudko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3b"]] + }, + { + "_id": "62f32848082fcc3049e92804", + "username": "denik1981", + "email": "denik1981@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 180, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3a"]] + }, + { + "_id": "62f32849082fcc3049e92805", + "username": "dud3", + "email": "dud3@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3c"]] + }, + { + "_id": "62f32849082fcc3049e92806", + "username": "Rick", + "email": "Rick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d3f"]] + }, + { + "_id": "62f32849082fcc3049e92808", + "username": "Asons", + "email": "Asons@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 82141, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32849082fcc3049e92809", + "username": "Arunprasanth M", + "email": "Arunprasanth M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d42"]] + }, + { + "_id": "62f32849082fcc3049e9280a", + "username": "Tiago Rangel de Sousa", + "email": "Tiago Rangel de Sousa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 706, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef4", "62f321ca082fcc3049e90d43"]] + }, + { + "_id": "62f32849082fcc3049e9280b", + "username": "Tom Lehman", + "email": "Tom Lehman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81271, + "questionIds": ["62f321bb082fcc3049e8fef4"], + "answerIds": [] + }, + { + "_id": "62f32849082fcc3049e9280c", + "username": "kgiannakakis", + "email": "kgiannakakis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101206, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d50"]] + }, + { + "_id": "62f3284a082fcc3049e9280d", + "username": "Zarni", + "email": "Zarni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 317, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d52"]] + }, + { + "_id": "62f3284a082fcc3049e9280f", + "username": "JYX", + "email": "JYX@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2593, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d56"]] + }, + { + "_id": "62f3284a082fcc3049e92810", + "username": "Kirk Liemohn", + "email": "Kirk Liemohn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7473, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d55"]] + }, + { + "_id": "62f3284a082fcc3049e92811", + "username": "Thangamani Palanisamy", + "email": "Thangamani Palanisamy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4962, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d58"]] + }, + { + "_id": "62f3284a082fcc3049e92812", + "username": "Kamrul", + "email": "Kamrul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6979, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d57"]] + }, + { + "_id": "62f3284a082fcc3049e92813", + "username": "Binita Bharati", + "email": "Binita Bharati@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4272, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d59"]] + }, + { + "_id": "62f3284a082fcc3049e92814", + "username": "FAA", + "email": "FAA@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 499, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5a"]] + }, + { + "_id": "62f32888082fcc3049e92815", + "username": "Mojtaba", + "email": "Mojtaba@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d66"]] + }, + { + "_id": "62f32888082fcc3049e92816", + "username": "Bhanu Pratap", + "email": "Bhanu Pratap@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1493, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d67"]] + }, + { + "_id": "62f32888082fcc3049e92817", + "username": "max", + "email": "max@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9000, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d69"]] + }, + { + "_id": "62f32888082fcc3049e92818", + "username": "Kalaivani M", + "email": "Kalaivani M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1220, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d68"]] + }, + { + "_id": "62f32888082fcc3049e92819", + "username": "Vishal Thakur", + "email": "Vishal Thakur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1395, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6a"]] + }, + { + "_id": "62f32888082fcc3049e9281a", + "username": "Curtis Yallop", + "email": "Curtis Yallop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6130, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6b"]] + }, + { + "_id": "62f32888082fcc3049e9281b", + "username": "Manish Singh", + "email": "Manish Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 894, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6c"]] + }, + { + "_id": "62f32888082fcc3049e9281c", + "username": "Muddasir23", + "email": "Muddasir23@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 563, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6d"]] + }, + { + "_id": "62f32888082fcc3049e9281d", + "username": "shyamm", + "email": "shyamm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 466, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d6f"]] + }, + { + "_id": "62f32889082fcc3049e9281e", + "username": "Naveenbos", + "email": "Naveenbos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2486, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d71"]] + }, + { + "_id": "62f32889082fcc3049e92820", + "username": "Dhiaa Al-Shalabi", + "email": "Dhiaa Al-Shalabi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d72"]] + }, + { + "_id": "62f32889082fcc3049e92822", + "username": "Vsevolod Golovanov", + "email": "Vsevolod Golovanov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3833, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92825", + "username": "Thomas", + "email": "Thomas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5553, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92826", + "username": "Pablo Santa Cruz", + "email": "Pablo Santa Cruz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 170926, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d7f"]] + }, + { + "_id": "62f32889082fcc3049e92828", + "username": "Emaborsa", + "email": "Emaborsa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9282a", + "username": "Matt Fletcher", + "email": "Matt Fletcher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7015, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9282b", + "username": "ClearCloud8", + "email": "ClearCloud8@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4852, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d84"]] + }, + { + "_id": "62f32889082fcc3049e9282d", + "username": "svth", + "email": "svth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1295, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92830", + "username": "DRAX", + "email": "DRAX@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27843, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d80"]] + }, + { + "_id": "62f32889082fcc3049e92832", + "username": "Dewi Morgan", + "email": "Dewi Morgan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1058, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92835", + "username": "darcher", + "email": "darcher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2922, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92839", + "username": "SmileyJames", + "email": "SmileyJames@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 97, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9283c", + "username": "user5672998", + "email": "user5672998@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 924, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9283f", + "username": "ToolmakerSteve", + "email": "ToolmakerSteve@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92840", + "username": "Chris Dolphin", + "email": "Chris Dolphin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1510, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d83"]] + }, + { + "_id": "62f32889082fcc3049e92844", + "username": "Utopik", + "email": "Utopik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3753, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92846", + "username": "Volkan", + "email": "Volkan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2192, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92849", + "username": "Orwellophile", + "email": "Orwellophile@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d82"]] + }, + { + "_id": "62f32889082fcc3049e9284b", + "username": "ewh", + "email": "ewh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 984, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9284d", + "username": "Daan", + "email": "Daan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7365, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e9284f", + "username": "StubbornShowaGuy", + "email": "StubbornShowaGuy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 249, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92852", + "username": "Aquarelle", + "email": "Aquarelle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8494, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32889082fcc3049e92854", + "username": "Jonathan H", + "email": "Jonathan H@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7275, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e92855", + "username": "ahmd0", + "email": "ahmd0@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15788, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d86"]] + }, + { + "_id": "62f3288a082fcc3049e92858", + "username": "sarimarton", + "email": "sarimarton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e92859", + "username": "Noris", + "email": "Noris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d87"]] + }, + { + "_id": "62f3288a082fcc3049e9285a", + "username": "Benj Sicam", + "email": "Benj Sicam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d88"]] + }, + { + "_id": "62f3288a082fcc3049e9285c", + "username": "Jules Manson", + "email": "Jules Manson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 184, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e9285e", + "username": "Yin", + "email": "Yin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 538, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d89"]] + }, + { + "_id": "62f3288a082fcc3049e9285f", + "username": "Aryeh Beitz", + "email": "Aryeh Beitz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1836, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8b"]] + }, + { + "_id": "62f3288a082fcc3049e92861", + "username": "Anthony Kong", + "email": "Anthony Kong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34256, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e92863", + "username": "Mr Rogers", + "email": "Mr Rogers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5901, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e92864", + "username": "Rob Brander", + "email": "Rob Brander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3534, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8d"]] + }, + { + "_id": "62f3288a082fcc3049e92867", + "username": "ScottyG", + "email": "ScottyG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3016, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8c"]] + }, + { + "_id": "62f3288a082fcc3049e9286a", + "username": "MikeBeaton", + "email": "MikeBeaton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2809, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288a082fcc3049e9286b", + "username": "Tomozma", + "email": "Tomozma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8f"]] + }, + { + "_id": "62f3288a082fcc3049e9286c", + "username": "Grant Miller", + "email": "Grant Miller@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24867, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d8e"]] + }, + { + "_id": "62f3288b082fcc3049e9286d", + "username": "Pato", + "email": "Pato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 672, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d91"]] + }, + { + "_id": "62f3288b082fcc3049e9286e", + "username": "customcommander", + "email": "customcommander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15557, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d90"]] + }, + { + "_id": "62f3288b082fcc3049e92870", + "username": "Philipp Sumi", + "email": "Philipp Sumi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 827, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288b082fcc3049e92871", + "username": "wheelmaker", + "email": "wheelmaker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2807, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d93"]] + }, + { + "_id": "62f3288b082fcc3049e92872", + "username": "yaya", + "email": "yaya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6609, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d92"]] + }, + { + "_id": "62f3288b082fcc3049e92874", + "username": "Marcel Kohls", + "email": "Marcel Kohls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1477, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d94"]] + }, + { + "_id": "62f3288b082fcc3049e92875", + "username": "Salsabeel", + "email": "Salsabeel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d98"]] + }, + { + "_id": "62f3288b082fcc3049e92876", + "username": "Arnaud F.", + "email": "Arnaud F.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7981, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d99"]] + }, + { + "_id": "62f3288b082fcc3049e92879", + "username": "andreyrk", + "email": "andreyrk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 338, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288b082fcc3049e9287b", + "username": "ZJR", + "email": "ZJR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8872, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef7", "62f321cb082fcc3049e90d97"]] + }, + { + "_id": "62f3288b082fcc3049e9287c", + "username": "Olical", + "email": "Olical@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36995, + "questionIds": ["62f321bb082fcc3049e8fef7"], + "answerIds": [] + }, + { + "_id": "62f3288c082fcc3049e9287d", + "username": "yunzen", + "email": "yunzen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31849, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9b"]] + }, + { + "_id": "62f3288c082fcc3049e9287e", + "username": "Gary Green", + "email": "Gary Green@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9a"]] + }, + { + "_id": "62f3288c082fcc3049e9287f", + "username": "Ciro Santilli Путлер Капут 六四事", + "email": "Ciro Santilli Путлер Капут 六四事@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 309799, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9d"]] + }, + { + "_id": "62f3288c082fcc3049e92880", + "username": "agjmills", + "email": "agjmills@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1186, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9e"]] + }, + { + "_id": "62f3288c082fcc3049e92881", + "username": "zawhtut", + "email": "zawhtut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8085, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da1"]] + }, + { + "_id": "62f3288c082fcc3049e92882", + "username": "Evgenia Karunus", + "email": "Evgenia Karunus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10255, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da0"]] + }, + { + "_id": "62f3288c082fcc3049e92883", + "username": "NkS", + "email": "NkS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1218, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90d9f"]] + }, + { + "_id": "62f3288c082fcc3049e92884", + "username": "user2657778", + "email": "user2657778@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da2"]] + }, + { + "_id": "62f3288c082fcc3049e92885", + "username": "Peter Girnus", + "email": "Peter Girnus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2605, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da3"]] + }, + { + "_id": "62f3288d082fcc3049e92886", + "username": "Parth Trivedi", + "email": "Parth Trivedi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3774, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da5"]] + }, + { + "_id": "62f3288d082fcc3049e92887", + "username": "Kgn-web", + "email": "Kgn-web@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6353, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da4"]] + }, + { + "_id": "62f3288d082fcc3049e92889", + "username": "Rishu Ranjan", + "email": "Rishu Ranjan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 366, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef9", "62f321cb082fcc3049e90da7"]] + }, + { + "_id": "62f3288d082fcc3049e9288b", + "username": "Luca Matteis", + "email": "Luca Matteis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90da8"]] + }, + { + "_id": "62f3288d082fcc3049e9288d", + "username": "Mister Smith", + "email": "Mister Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26297, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288d082fcc3049e9288f", + "username": "speedplane", + "email": "speedplane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15117, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288d082fcc3049e92891", + "username": "hippietrail", + "email": "hippietrail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14907, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288d082fcc3049e92892", + "username": "Nikola Petkanski", + "email": "Nikola Petkanski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4574, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dac"]] + }, + { + "_id": "62f3288d082fcc3049e92894", + "username": "Jacob Dalton", + "email": "Jacob Dalton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1593, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288d082fcc3049e92896", + "username": "anshul", + "email": "anshul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 394, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288d082fcc3049e92897", + "username": "kornfridge", + "email": "kornfridge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5018, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dab"], + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db4"] + ] + }, + { + "_id": "62f3288d082fcc3049e92899", + "username": "Decebal", + "email": "Decebal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90daa"]] + }, + { + "_id": "62f3288d082fcc3049e9289b", + "username": "Camilo Martin", + "email": "Camilo Martin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36147, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288e082fcc3049e9289c", + "username": "Dan Fox", + "email": "Dan Fox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db1"]] + }, + { + "_id": "62f3288e082fcc3049e9289d", + "username": "Torbjörn Nomell", + "email": "Torbjörn Nomell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2982, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db0"]] + }, + { + "_id": "62f3288e082fcc3049e9289e", + "username": "Gabriel Silveira", + "email": "Gabriel Silveira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 364, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dae"]] + }, + { + "_id": "62f3288e082fcc3049e9289f", + "username": "Cœur", + "email": "Cœur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 35190, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90daf"]] + }, + { + "_id": "62f3288e082fcc3049e928a0", + "username": "Kishore Relangi", + "email": "Kishore Relangi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1808, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db2"]] + }, + { + "_id": "62f3288e082fcc3049e928a1", + "username": "Mrchief", + "email": "Mrchief@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73648, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db5"]] + }, + { + "_id": "62f3288e082fcc3049e928a2", + "username": "Seth Holladay", + "email": "Seth Holladay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db7"]] + }, + { + "_id": "62f3288e082fcc3049e928a3", + "username": "PaulL", + "email": "PaulL@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6610, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db6"]] + }, + { + "_id": "62f3288f082fcc3049e928a4", + "username": "Roobie Nuby", + "email": "Roobie Nuby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1219, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db9"]] + }, + { + "_id": "62f3288f082fcc3049e928a5", + "username": "pix", + "email": "pix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4992, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dba"]] + }, + { + "_id": "62f3288f082fcc3049e928a6", + "username": "sergeyz", + "email": "sergeyz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dbb"]] + }, + { + "_id": "62f3288f082fcc3049e928a7", + "username": "Joeytje50", + "email": "Joeytje50@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18188, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90db8"]] + }, + { + "_id": "62f3288f082fcc3049e928a9", + "username": "Max Makhrov", + "email": "Max Makhrov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16314, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dca"]] + }, + { + "_id": "62f3288f082fcc3049e928aa", + "username": "rab", + "email": "rab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4084, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dbd"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f43"] + ] + }, + { + "_id": "62f3288f082fcc3049e928ab", + "username": "ilgam", + "email": "ilgam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3692, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dbe"]] + }, + { + "_id": "62f3288f082fcc3049e928ac", + "username": "Grozz", + "email": "Grozz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dbf"]] + }, + { + "_id": "62f3288f082fcc3049e928ae", + "username": "Michiel van der Blonk", + "email": "Michiel van der Blonk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 690, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288f082fcc3049e928b0", + "username": "webdeb", + "email": "webdeb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12644, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc1"]] + }, + { + "_id": "62f3288f082fcc3049e928b2", + "username": "Alexander Goncharov", + "email": "Alexander Goncharov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1284, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288f082fcc3049e928b4", + "username": "Lee Goddard", + "email": "Lee Goddard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9786, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3288f082fcc3049e928b5", + "username": "ArunT", + "email": "ArunT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22124, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc0"]] + }, + { + "_id": "62f32890082fcc3049e928b6", + "username": "rajesh", + "email": "rajesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc2"]] + }, + { + "_id": "62f32890082fcc3049e928b8", + "username": "Pooja Thapa", + "email": "Pooja Thapa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32890082fcc3049e928b9", + "username": "Saravanan Rajaraman", + "email": "Saravanan Rajaraman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1001, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc3"]] + }, + { + "_id": "62f32890082fcc3049e928ba", + "username": "pgee70", + "email": "pgee70@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3356, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc5"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f51"] + ] + }, + { + "_id": "62f32890082fcc3049e928bb", + "username": "Vamsi", + "email": "Vamsi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9134, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc4"]] + }, + { + "_id": "62f32890082fcc3049e928bd", + "username": "Sahith Vibudhi", + "email": "Sahith Vibudhi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4453, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32890082fcc3049e928be", + "username": "Leonardo", + "email": "Leonardo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 991, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc6"]] + }, + { + "_id": "62f32890082fcc3049e928bf", + "username": "bvmCoder", + "email": "bvmCoder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc7"]] + }, + { + "_id": "62f32890082fcc3049e928c1", + "username": "limeandcoconut", + "email": "limeandcoconut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 408, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32890082fcc3049e928c3", + "username": "Yi Feng Xie", + "email": "Yi Feng Xie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3677, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dc8"]] + }, + { + "_id": "62f32890082fcc3049e928c5", + "username": "Vass", + "email": "Vass@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2483, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32890082fcc3049e928c7", + "username": "Tofandel", + "email": "Tofandel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2448, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32891082fcc3049e928c9", + "username": "NikeshPathania", + "email": "NikeshPathania@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 207, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dcf"]] + }, + { + "_id": "62f32891082fcc3049e928ca", + "username": "chinmayan", + "email": "chinmayan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1224, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dce"]] + }, + { + "_id": "62f32891082fcc3049e928cb", + "username": "BazSTR", + "email": "BazSTR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd1"]] + }, + { + "_id": "62f32891082fcc3049e928cc", + "username": "Morris S", + "email": "Morris S@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1986, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd0"]] + }, + { + "_id": "62f32891082fcc3049e928cd", + "username": "user3591464", + "email": "user3591464@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd5"]] + }, + { + "_id": "62f32891082fcc3049e928cf", + "username": "Ondřej Želazko", + "email": "Ondřej Želazko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 580, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32892082fcc3049e928d0", + "username": "Junaid Khan", + "email": "Junaid Khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 59, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd6"]] + }, + { + "_id": "62f32892082fcc3049e928d1", + "username": "LEMUEL ADANE", + "email": "LEMUEL ADANE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7640, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd7"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb8"] + ] + }, + { + "_id": "62f32892082fcc3049e928d2", + "username": "Nidhal Ben Tahar", + "email": "Nidhal Ben Tahar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 793, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd8"]] + }, + { + "_id": "62f32892082fcc3049e928d3", + "username": "Firas Abd Alrahman", + "email": "Firas Abd Alrahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 167, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dd9"]] + }, + { + "_id": "62f32892082fcc3049e928d4", + "username": "Shridhar Sagari", + "email": "Shridhar Sagari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 257, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dda"]] + }, + { + "_id": "62f32892082fcc3049e928d5", + "username": "Nizmox", + "email": "Nizmox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90ddc"]] + }, + { + "_id": "62f32892082fcc3049e928d6", + "username": "Iven Marquardt", + "email": "Iven Marquardt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3677, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90ddd"]] + }, + { + "_id": "62f32892082fcc3049e928d8", + "username": "Thanwa Ch.", + "email": "Thanwa Ch.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 125, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32892082fcc3049e928da", + "username": "Edgar Quintero", + "email": "Edgar Quintero@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3767, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32893082fcc3049e928dc", + "username": "Nikki Luzader", + "email": "Nikki Luzader@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de0"]] + }, + { + "_id": "62f32893082fcc3049e928dd", + "username": "noor", + "email": "noor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de1"]] + }, + { + "_id": "62f32893082fcc3049e928de", + "username": "WesleyAC", + "email": "WesleyAC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de2"]] + }, + { + "_id": "62f32893082fcc3049e928df", + "username": "Gherciu Gheorghe", + "email": "Gherciu Gheorghe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 182, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de3"]] + }, + { + "_id": "62f32893082fcc3049e928e0", + "username": "Shreyansh Sharma", + "email": "Shreyansh Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 88, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de4"]] + }, + { + "_id": "62f32893082fcc3049e928e2", + "username": "airtonix", + "email": "airtonix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4300, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32893082fcc3049e928e4", + "username": "Roman", + "email": "Roman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16055, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de5"]] + }, + { + "_id": "62f32893082fcc3049e928e6", + "username": "Didier68", + "email": "Didier68@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 797, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de6"]] + }, + { + "_id": "62f32893082fcc3049e928e8", + "username": "Danish", + "email": "Danish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1387, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32893082fcc3049e928e9", + "username": "user239558", + "email": "user239558@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6684, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90de9"]] + }, + { + "_id": "62f32894082fcc3049e928ea", + "username": "Ylama", + "email": "Ylama@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2287, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dec"]] + }, + { + "_id": "62f32894082fcc3049e928eb", + "username": "Ballpin", + "email": "Ballpin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 187, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90deb"]] + }, + { + "_id": "62f32894082fcc3049e928ec", + "username": "Giacomo Casadei", + "email": "Giacomo Casadei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1145, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dee"], + ["62f321bb082fcc3049e8fece", "62f321c1082fcc3049e90581"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f91"] + ] + }, + { + "_id": "62f32894082fcc3049e928ed", + "username": "Artisan72", + "email": "Artisan72@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90ded"]] + }, + { + "_id": "62f32894082fcc3049e928ef", + "username": "Ponciusz", + "email": "Ponciusz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 137, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32894082fcc3049e928f1", + "username": "lonix", + "email": "lonix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8759, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90def"]] + }, + { + "_id": "62f32894082fcc3049e928f2", + "username": "Muhammad Atif Akram", + "email": "Muhammad Atif Akram@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1084, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df0"]] + }, + { + "_id": "62f32894082fcc3049e928f3", + "username": "Mike Reiche", + "email": "Mike Reiche@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df2"]] + }, + { + "_id": "62f32894082fcc3049e928f4", + "username": "Re_p1ay", + "email": "Re_p1ay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 153, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df1"]] + }, + { + "_id": "62f32895082fcc3049e928f5", + "username": "rop", + "email": "rop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df4"]] + }, + { + "_id": "62f32895082fcc3049e928f6", + "username": "Kalana Weerarathne", + "email": "Kalana Weerarathne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 303, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df5"]] + }, + { + "_id": "62f32895082fcc3049e928f7", + "username": "Surbhi Dighe", + "email": "Surbhi Dighe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 631, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df6"]] + }, + { + "_id": "62f32895082fcc3049e928f9", + "username": "Vivekraj K R", + "email": "Vivekraj K R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2158, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df8"]] + }, + { + "_id": "62f32895082fcc3049e928fa", + "username": "rotarydial", + "email": "rotarydial@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1881, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90df9"]] + }, + { + "_id": "62f32895082fcc3049e928fc", + "username": "Aakash", + "email": "Aakash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1795, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32895082fcc3049e928fe", + "username": "coder9833idls", + "email": "coder9833idls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dfb"]] + }, + { + "_id": "62f32895082fcc3049e92900", + "username": "Yadab Sd", + "email": "Yadab Sd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 535, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefa", "62f321cb082fcc3049e90dfa"]] + }, + { + "_id": "62f32895082fcc3049e92902", + "username": "Chris Betti", + "email": "Chris Betti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2604, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32895082fcc3049e92903", + "username": "jasonmp85", + "email": "jasonmp85@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6669, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90dfc"]] + }, + { + "_id": "62f32895082fcc3049e92905", + "username": "Arad", + "email": "Arad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4636, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32895082fcc3049e92906", + "username": "Noah Freitas", + "email": "Noah Freitas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16952, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90dfd"]] + }, + { + "_id": "62f32896082fcc3049e92907", + "username": "user1437663", + "email": "user1437663@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 845, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90dfe"]] + }, + { + "_id": "62f32896082fcc3049e9290b", + "username": "None", + "email": "None@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5329, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90dff"]] + }, + { + "_id": "62f32896082fcc3049e9290c", + "username": "AbhinavRanjan", + "email": "AbhinavRanjan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1578, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e01"]] + }, + { + "_id": "62f32896082fcc3049e9290d", + "username": "bartburkhardt", + "email": "bartburkhardt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7185, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e00"]] + }, + { + "_id": "62f32896082fcc3049e9290e", + "username": "Zakhar", + "email": "Zakhar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 611, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e02"]] + }, + { + "_id": "62f32896082fcc3049e9290f", + "username": "Paulo Buchsbaum", + "email": "Paulo Buchsbaum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2213, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e03"]] + }, + { + "_id": "62f32896082fcc3049e92911", + "username": "wrossmck", + "email": "wrossmck@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 369, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92913", + "username": "fregante", + "email": "fregante@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26352, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92915", + "username": "runsun", + "email": "runsun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 394, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e05"]] + }, + { + "_id": "62f32896082fcc3049e92917", + "username": "ROMANIA_engineer", + "email": "ROMANIA_engineer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51742, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92919", + "username": "hoju", + "email": "hoju@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27015, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e9291c", + "username": "A-Diddy", + "email": "A-Diddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 482, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e9291f", + "username": "mmla", + "email": "mmla@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1510, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92921", + "username": "Im Batman", + "email": "Im Batman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1778, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92923", + "username": "Ivan Juarez", + "email": "Ivan Juarez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1313, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92925", + "username": "pishpish", + "email": "pishpish@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2540, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32896082fcc3049e92926", + "username": "uKolka", + "email": "uKolka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e04"]] + }, + { + "_id": "62f32896082fcc3049e92927", + "username": "xd6_", + "email": "xd6_@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e06"]] + }, + { + "_id": "62f32896082fcc3049e92929", + "username": "Wayne", + "email": "Wayne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58501, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e07"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f21"] + ] + }, + { + "_id": "62f32897082fcc3049e9292b", + "username": "Will Bowman", + "email": "Will Bowman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 407, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e9292c", + "username": "phette23", + "email": "phette23@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 414, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0a"]] + }, + { + "_id": "62f32897082fcc3049e9292f", + "username": "Petr Havlik", + "email": "Petr Havlik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3287, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92932", + "username": "Debashis", + "email": "Debashis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 546, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92934", + "username": "Maor Ben", + "email": "Maor Ben@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 301, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e28"]] + }, + { + "_id": "62f32897082fcc3049e92935", + "username": "Mosiur", + "email": "Mosiur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e09"]] + }, + { + "_id": "62f32897082fcc3049e92936", + "username": "Saksham", + "email": "Saksham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8815, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0b"]] + }, + { + "_id": "62f32897082fcc3049e92938", + "username": "steampowered", + "email": "steampowered@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11369, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92939", + "username": "Beder Acosta Borges", + "email": "Beder Acosta Borges@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5060, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0f"]] + }, + { + "_id": "62f32897082fcc3049e9293c", + "username": "traditional", + "email": "traditional@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 941, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e9293f", + "username": "kenberkeley", + "email": "kenberkeley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6652, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92941", + "username": "Andy Borgmann", + "email": "Andy Borgmann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92942", + "username": "Tutankhamen", + "email": "Tutankhamen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3502, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0d"]] + }, + { + "_id": "62f32897082fcc3049e92944", + "username": "Kyle Chadha", + "email": "Kyle Chadha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3221, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92946", + "username": "Shrijan Tiwari", + "email": "Shrijan Tiwari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 633, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92947", + "username": "user3664916", + "email": "user3664916@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 531, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e10"]] + }, + { + "_id": "62f32897082fcc3049e92949", + "username": "Dennis Heiden", + "email": "Dennis Heiden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 748, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e9294d", + "username": "Felipe Buccioni", + "email": "Felipe Buccioni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18305, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0c"]] + }, + { + "_id": "62f32897082fcc3049e9294f", + "username": "ladieu", + "email": "ladieu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32897082fcc3049e92950", + "username": "Ronnie Overby", + "email": "Ronnie Overby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43809, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e0e"]] + }, + { + "_id": "62f32897082fcc3049e92951", + "username": "Eluny", + "email": "Eluny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 554, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e11"]] + }, + { + "_id": "62f32898082fcc3049e92953", + "username": "Marcin Zukowski", + "email": "Marcin Zukowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3811, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e13"]] + }, + { + "_id": "62f32898082fcc3049e92957", + "username": "Mahmood Afzalzadeh", + "email": "Mahmood Afzalzadeh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 150, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32898082fcc3049e92959", + "username": "Nader Gharibian Fard", + "email": "Nader Gharibian Fard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3971, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32898082fcc3049e9295a", + "username": "Ethan Chen", + "email": "Ethan Chen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 559, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e15"]] + }, + { + "_id": "62f32898082fcc3049e9295c", + "username": "Sinan", + "email": "Sinan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11241, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e14"]] + }, + { + "_id": "62f32898082fcc3049e9295e", + "username": "Peter S McIntyre", + "email": "Peter S McIntyre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e16"]] + }, + { + "_id": "62f32898082fcc3049e92960", + "username": "Ralph David Abernathy", + "email": "Ralph David Abernathy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32898082fcc3049e92961", + "username": "Kiry Meas", + "email": "Kiry Meas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1018, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e17"]] + }, + { + "_id": "62f32898082fcc3049e92962", + "username": "Matt Scheurich", + "email": "Matt Scheurich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 909, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e18"]] + }, + { + "_id": "62f32898082fcc3049e92963", + "username": "arosolino", + "email": "arosolino@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1049, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e19"]] + }, + { + "_id": "62f32898082fcc3049e92965", + "username": "Dustin Sun", + "email": "Dustin Sun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4864, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1a"]] + }, + { + "_id": "62f32898082fcc3049e92967", + "username": "Jared Smith", + "email": "Jared Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32898082fcc3049e92968", + "username": "Du-Lacoste", + "email": "Du-Lacoste@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10102, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1b"], + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9e"] + ] + }, + { + "_id": "62f32899082fcc3049e92969", + "username": "Waruna Manjula", + "email": "Waruna Manjula@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2647, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1c"]] + }, + { + "_id": "62f32899082fcc3049e9296a", + "username": "Sverrisson", + "email": "Sverrisson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1d"]] + }, + { + "_id": "62f32899082fcc3049e9296b", + "username": "Geoffrey Kimani", + "email": "Geoffrey Kimani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1e"]] + }, + { + "_id": "62f32899082fcc3049e9296e", + "username": "aleclarson", + "email": "aleclarson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e1f"]] + }, + { + "_id": "62f32899082fcc3049e92970", + "username": "Bathri Nathan", + "email": "Bathri Nathan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 891, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e20"]] + }, + { + "_id": "62f32899082fcc3049e92972", + "username": "OG Sean", + "email": "OG Sean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 815, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32899082fcc3049e92974", + "username": "serraosays", + "email": "serraosays@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6569, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32899082fcc3049e92975", + "username": "Igor Bykov", + "email": "Igor Bykov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2239, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e21"]] + }, + { + "_id": "62f32899082fcc3049e92977", + "username": "Suraj Dalvi", + "email": "Suraj Dalvi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 748, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32899082fcc3049e92978", + "username": "djulien", + "email": "djulien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e25"]] + }, + { + "_id": "62f32899082fcc3049e92979", + "username": "Javier Elices", + "email": "Javier Elices@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1822, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e24"]] + }, + { + "_id": "62f328d7082fcc3049e9297c", + "username": "Fernando", + "email": "Fernando@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 813, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e9297d", + "username": "dariom", + "email": "dariom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4295, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e32"]] + }, + { + "_id": "62f328d7082fcc3049e92980", + "username": "swift", + "email": "swift@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 270, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e36"]] + }, + { + "_id": "62f328d7082fcc3049e92983", + "username": "reconbot", + "email": "reconbot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5010, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e92985", + "username": "Sameer Alibhai", + "email": "Sameer Alibhai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3012, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e92987", + "username": "pathfinder", + "email": "pathfinder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1556, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e9298a", + "username": "w35l3y", + "email": "w35l3y@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3a"]] + }, + { + "_id": "62f328d7082fcc3049e9298c", + "username": "Design by Adrian", + "email": "Design by Adrian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2163, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e9298d", + "username": "griegs", + "email": "griegs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22346, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e37"]] + }, + { + "_id": "62f328d7082fcc3049e9298e", + "username": "Matthew Flaschen", + "email": "Matthew Flaschen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 269765, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e39"]] + }, + { + "_id": "62f328d7082fcc3049e92991", + "username": "Mahn", + "email": "Mahn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15708, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e92993", + "username": "Mike Graf", + "email": "Mike Graf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4779, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d7082fcc3049e92995", + "username": "O Genthe", + "email": "O Genthe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d8082fcc3049e92996", + "username": "Somwang Souksavatd", + "email": "Somwang Souksavatd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4562, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3e"]] + }, + { + "_id": "62f328d8082fcc3049e92998", + "username": "QueueHammer", + "email": "QueueHammer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10143, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d8082fcc3049e9299a", + "username": "sites", + "email": "sites@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20847, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3d"]] + }, + { + "_id": "62f328d8082fcc3049e9299b", + "username": "kamal", + "email": "kamal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3c"]] + }, + { + "_id": "62f328d8082fcc3049e9299c", + "username": "Akshay Joshi", + "email": "Akshay Joshi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e3f"]] + }, + { + "_id": "62f328d8082fcc3049e9299d", + "username": "uofc", + "email": "uofc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 530, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e40"]] + }, + { + "_id": "62f328d8082fcc3049e9299f", + "username": "Marc477", + "email": "Marc477@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 161, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e41"]] + }, + { + "_id": "62f328d8082fcc3049e929a0", + "username": "bestafubana", + "email": "bestafubana@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 92, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e43"]] + }, + { + "_id": "62f328d8082fcc3049e929a1", + "username": "profile_-1", + "email": "profile_-1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 85, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e42"]] + }, + { + "_id": "62f328d8082fcc3049e929a2", + "username": "Mark Walters", + "email": "Mark Walters@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11635, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e44"]] + }, + { + "_id": "62f328d9082fcc3049e929a3", + "username": "fruitloaf", + "email": "fruitloaf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 666, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefc", "62f321cb082fcc3049e90e47"]] + }, + { + "_id": "62f328d9082fcc3049e929a5", + "username": "jub0bs", + "email": "jub0bs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55059, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929a6", + "username": "jldupont", + "email": "jldupont@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 88898, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e4b"]] + }, + { + "_id": "62f328d9082fcc3049e929a8", + "username": "Kevin Beal", + "email": "Kevin Beal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9834, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929a9", + "username": "Ajain Vivek", + "email": "Ajain Vivek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e4d"]] + }, + { + "_id": "62f328d9082fcc3049e929ab", + "username": "Jerod Venema", + "email": "Jerod Venema@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 43357, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e4c"]] + }, + { + "_id": "62f328d9082fcc3049e929ad", + "username": "JustLearn", + "email": "JustLearn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3555, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929b0", + "username": "Joe Coder", + "email": "Joe Coder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4323, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929b3", + "username": "Cholthi Paul Ttiopic", + "email": "Cholthi Paul Ttiopic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929b4", + "username": "sarath joseph", + "email": "sarath joseph@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e4f"]] + }, + { + "_id": "62f328d9082fcc3049e929b5", + "username": "dardawk", + "email": "dardawk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 446, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e4e"]] + }, + { + "_id": "62f328d9082fcc3049e929b6", + "username": "simhumileco", + "email": "simhumileco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27903, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e51"]] + }, + { + "_id": "62f328d9082fcc3049e929b8", + "username": "RobbyD", + "email": "RobbyD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 493, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328d9082fcc3049e929b9", + "username": "Marcus Thornton", + "email": "Marcus Thornton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5629, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefe", "62f321cb082fcc3049e90e50"]] + }, + { + "_id": "62f328da082fcc3049e929bb", + "username": "BoltClock", + "email": "BoltClock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 670402, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328da082fcc3049e929bc", + "username": "Maxam", + "email": "Maxam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4013, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e53"]] + }, + { + "_id": "62f328da082fcc3049e929bd", + "username": "Cheeso", + "email": "Cheeso@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 185578, + "questionIds": ["62f321bb082fcc3049e8fefe"], + "answerIds": [] + }, + { + "_id": "62f328da082fcc3049e929be", + "username": "philnash", + "email": "philnash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 66028, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e55"]] + }, + { + "_id": "62f328da082fcc3049e929c0", + "username": "Scott Evernden", + "email": "Scott Evernden@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37717, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328da082fcc3049e929c1", + "username": "Roccivic", + "email": "Roccivic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1006, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e57"]] + }, + { + "_id": "62f328da082fcc3049e929c2", + "username": "Rayron Victor", + "email": "Rayron Victor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2338, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e59"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eeb"] + ] + }, + { + "_id": "62f328da082fcc3049e929c3", + "username": "Lalit Kumar Maurya", + "email": "Lalit Kumar Maurya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5335, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5a"]] + }, + { + "_id": "62f328da082fcc3049e929c4", + "username": "Thirumalai murugan", + "email": "Thirumalai murugan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5b"]] + }, + { + "_id": "62f328db082fcc3049e929c5", + "username": "Dennis R", + "email": "Dennis R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3115, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5c"]] + }, + { + "_id": "62f328db082fcc3049e929c6", + "username": "Oskar", + "email": "Oskar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2474, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5d"]] + }, + { + "_id": "62f328db082fcc3049e929c7", + "username": "tetutato", + "email": "tetutato@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 628, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e5e"]] + }, + { + "_id": "62f328db082fcc3049e929c8", + "username": "Sumit Lahiri", + "email": "Sumit Lahiri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 475, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e61"]] + }, + { + "_id": "62f328db082fcc3049e929c9", + "username": "RPichioli", + "email": "RPichioli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e60"]] + }, + { + "_id": "62f328db082fcc3049e929ca", + "username": "Hassan Fayyaz", + "email": "Hassan Fayyaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 487, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e63"]] + }, + { + "_id": "62f328db082fcc3049e929cb", + "username": "Jason Williams", + "email": "Jason Williams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2608, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e62"]] + }, + { + "_id": "62f328db082fcc3049e929cc", + "username": "Vishnu Prasanth G", + "email": "Vishnu Prasanth G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1025, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feff", "62f321cc082fcc3049e90e65"]] + }, + { + "_id": "62f328dc082fcc3049e929cd", + "username": "Chris", + "email": "Chris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2025, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e6d"], + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e95"] + ] + }, + { + "_id": "62f328dc082fcc3049e929d1", + "username": "Gordon Gustafson", + "email": "Gordon Gustafson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38854, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e6c"]] + }, + { + "_id": "62f328dc082fcc3049e929d2", + "username": "user1764199", + "email": "user1764199@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 165, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e6f"]] + }, + { + "_id": "62f328dc082fcc3049e929d4", + "username": "Darin Dimitrov", + "email": "Darin Dimitrov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 999837, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e6e"]] + }, + { + "_id": "62f328dc082fcc3049e929d6", + "username": "originalhat", + "email": "originalhat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1626, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dc082fcc3049e929d7", + "username": "Codler", + "email": "Codler@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e70"]] + }, + { + "_id": "62f328dc082fcc3049e929d9", + "username": "ILMostro_7", + "email": "ILMostro_7@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1302, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dc082fcc3049e929da", + "username": "Learner.js", + "email": "Learner.js@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e71"]] + }, + { + "_id": "62f328dc082fcc3049e929dc", + "username": "brooklynsweb", + "email": "brooklynsweb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1007, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e72"]] + }, + { + "_id": "62f328dc082fcc3049e929de", + "username": "evandrix", + "email": "evandrix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5917, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dc082fcc3049e929e0", + "username": "Ivan Z", + "email": "Ivan Z@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1394, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dc082fcc3049e929e2", + "username": "Shachi", + "email": "Shachi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1768, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dc082fcc3049e929e5", + "username": "Prasobh.Kollattu", + "email": "Prasobh.Kollattu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1549, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e73"]] + }, + { + "_id": "62f328dc082fcc3049e929e7", + "username": "Starkers", + "email": "Starkers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9843, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e74"]] + }, + { + "_id": "62f328dd082fcc3049e929ea", + "username": "Yusuf", + "email": "Yusuf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 685, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e77"]] + }, + { + "_id": "62f328dd082fcc3049e929ec", + "username": "Alexander Farber", + "email": "Alexander Farber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19969, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dd082fcc3049e929ed", + "username": "Goran B.", + "email": "Goran B.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 534, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e78"]] + }, + { + "_id": "62f328dd082fcc3049e929ef", + "username": "happy", + "email": "happy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 474, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e79"]] + }, + { + "_id": "62f328dd082fcc3049e929f2", + "username": "NutCracker", + "email": "NutCracker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10660, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7a"]] + }, + { + "_id": "62f328dd082fcc3049e929f4", + "username": "Pietro Coelho", + "email": "Pietro Coelho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1576, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dd082fcc3049e929f6", + "username": "Sunil B N", + "email": "Sunil B N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4001, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dd082fcc3049e929f7", + "username": "Nilesh Pawar", + "email": "Nilesh Pawar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 637, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7b"]] + }, + { + "_id": "62f328dd082fcc3049e929f9", + "username": "650aa6a2", + "email": "650aa6a2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 122, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dd082fcc3049e929fa", + "username": "Prakhar Mittal", + "email": "Prakhar Mittal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6065, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7d"]] + }, + { + "_id": "62f328dd082fcc3049e929fc", + "username": "Tree", + "email": "Tree@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328dd082fcc3049e929fe", + "username": "Stanislav Vincent", + "email": "Stanislav Vincent@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 581, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7c"]] + }, + { + "_id": "62f328dd082fcc3049e92a00", + "username": "Aylian Craspa", + "email": "Aylian Craspa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 362, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e7e"]] + }, + { + "_id": "62f328de082fcc3049e92a02", + "username": "Javi Marzán", + "email": "Javi Marzán@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 806, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a03", + "username": "Achintha Isuru", + "email": "Achintha Isuru@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1934, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e80"]] + }, + { + "_id": "62f328de082fcc3049e92a06", + "username": "Akin Hwan", + "email": "Akin Hwan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 531, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a08", + "username": "user10294268", + "email": "user10294268@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a09", + "username": "Dani Amsalem", + "email": "Dani Amsalem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 876, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e83"]] + }, + { + "_id": "62f328de082fcc3049e92a0b", + "username": "Nitin Jadhav", + "email": "Nitin Jadhav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5407, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e84"]] + }, + { + "_id": "62f328de082fcc3049e92a0d", + "username": "Max Carroll", + "email": "Max Carroll@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3819, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a0e", + "username": "Aaron Plocharczyk", + "email": "Aaron Plocharczyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2626, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e86"], + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee8"] + ] + }, + { + "_id": "62f328de082fcc3049e92a10", + "username": "Koby Douek", + "email": "Koby Douek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15537, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e85"]] + }, + { + "_id": "62f328de082fcc3049e92a12", + "username": "Moritz Schmidt", + "email": "Moritz Schmidt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2423, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a16", + "username": "Nishant Ghodke", + "email": "Nishant Ghodke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 901, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f328de082fcc3049e92a18", + "username": "Shams Ansari", + "email": "Shams Ansari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 672, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e88"]] + }, + { + "_id": "62f3291b082fcc3049e92a19", + "username": "NewBieCoder", + "email": "NewBieCoder@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 179, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e8b"]] + }, + { + "_id": "62f3291b082fcc3049e92a1a", + "username": "Mr Ernest", + "email": "Mr Ernest@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e8c"]] + }, + { + "_id": "62f3291b082fcc3049e92a1c", + "username": "Johanisma", + "email": "Johanisma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2058, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291b082fcc3049e92a1f", + "username": "epix", + "email": "epix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e8e"]] + }, + { + "_id": "62f3291b082fcc3049e92a22", + "username": "Randolpho", + "email": "Randolpho@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e90"]] + }, + { + "_id": "62f3291b082fcc3049e92a23", + "username": "Gareth", + "email": "Gareth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 126035, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e8f"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91003"] + ] + }, + { + "_id": "62f3291b082fcc3049e92a24", + "username": "Andre 'Fi'", + "email": "Andre 'Fi'@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 619, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e93"]] + }, + { + "_id": "62f3291b082fcc3049e92a26", + "username": "saluce", + "email": "saluce@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12529, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291b082fcc3049e92a29", + "username": "Ky.", + "email": "Ky.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29203, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291b082fcc3049e92a2b", + "username": "George", + "email": "George@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6541, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b87"]] + }, + { + "_id": "62f3291b082fcc3049e92a2d", + "username": "Aaron Franke", + "email": "Aaron Franke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2548, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291b082fcc3049e92a2e", + "username": "Artur Czajka", + "email": "Artur Czajka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17763, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e92"]] + }, + { + "_id": "62f3291c082fcc3049e92a2f", + "username": "Yaroslav", + "email": "Yaroslav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4283, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e94"]] + }, + { + "_id": "62f3291c082fcc3049e92a30", + "username": "David Miró", + "email": "David Miró@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2558, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e96"]] + }, + { + "_id": "62f3291c082fcc3049e92a32", + "username": "David Karlaš", + "email": "David Karlaš@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 932, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291c082fcc3049e92a34", + "username": "parliament", + "email": "parliament@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291c082fcc3049e92a35", + "username": "Rob Hardy", + "email": "Rob Hardy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1804, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e97"]] + }, + { + "_id": "62f3291c082fcc3049e92a39", + "username": "GTF", + "email": "GTF@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7295, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e99"]] + }, + { + "_id": "62f3291c082fcc3049e92a3b", + "username": "Sildoreth", + "email": "Sildoreth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1848, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291c082fcc3049e92a3c", + "username": "user2254487", + "email": "user2254487@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9a"]] + }, + { + "_id": "62f3291c082fcc3049e92a3d", + "username": "arcseldon", + "email": "arcseldon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33245, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9b"]] + }, + { + "_id": "62f3291c082fcc3049e92a3e", + "username": "Andrew Philips", + "email": "Andrew Philips@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1600, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9d"]] + }, + { + "_id": "62f3291c082fcc3049e92a3f", + "username": "Xeltor", + "email": "Xeltor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4527, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9c"]] + }, + { + "_id": "62f3291d082fcc3049e92a40", + "username": "Gildas.Tambo", + "email": "Gildas.Tambo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21373, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9f"]] + }, + { + "_id": "62f3291d082fcc3049e92a42", + "username": "Shivanshu Goyal", + "email": "Shivanshu Goyal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1247, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90e9e"]] + }, + { + "_id": "62f3291d082fcc3049e92a43", + "username": "Gelin Luo", + "email": "Gelin Luo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13985, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea1"]] + }, + { + "_id": "62f3291d082fcc3049e92a44", + "username": "Blake Bowen", + "email": "Blake Bowen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1019, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea0"]] + }, + { + "_id": "62f3291d082fcc3049e92a46", + "username": "Little Alien", + "email": "Little Alien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eaa"]] + }, + { + "_id": "62f3291d082fcc3049e92a47", + "username": "Pylinux", + "email": "Pylinux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10438, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea2"]] + }, + { + "_id": "62f3291d082fcc3049e92a49", + "username": "vbraun", + "email": "vbraun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1646, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291d082fcc3049e92a4b", + "username": "rvighne", + "email": "rvighne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19415, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291d082fcc3049e92a4e", + "username": "Jack G", + "email": "Jack G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4048, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291d082fcc3049e92a4f", + "username": "hvdd", + "email": "hvdd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 504, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea5"]] + }, + { + "_id": "62f3291d082fcc3049e92a51", + "username": "user1228", + "email": "user1228@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291d082fcc3049e92a53", + "username": "Jules Sam. Randolph", + "email": "Jules Sam. Randolph@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2793, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea6"]] + }, + { + "_id": "62f3291e082fcc3049e92a55", + "username": "LNT", + "email": "LNT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 806, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea8"]] + }, + { + "_id": "62f3291e082fcc3049e92a56", + "username": "Ilya Gazman", + "email": "Ilya Gazman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ea9"]] + }, + { + "_id": "62f3291e082fcc3049e92a57", + "username": "mika", + "email": "mika@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eac"]] + }, + { + "_id": "62f3291e082fcc3049e92a59", + "username": "Jon G", + "email": "Jon G@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291e082fcc3049e92a5c", + "username": "Codii", + "email": "Codii@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 853, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291e082fcc3049e92a5e", + "username": "Idemax", + "email": "Idemax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2622, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291e082fcc3049e92a5f", + "username": "Julius Dzidzevičius", + "email": "Julius Dzidzevičius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10437, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eaf"]] + }, + { + "_id": "62f3291e082fcc3049e92a60", + "username": "David Lemon", + "email": "David Lemon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1520, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90ead"]] + }, + { + "_id": "62f3291e082fcc3049e92a61", + "username": "jamess", + "email": "jamess@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 523, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb0"]] + }, + { + "_id": "62f3291e082fcc3049e92a62", + "username": "papiro", + "email": "papiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1846, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb1"]] + }, + { + "_id": "62f3291f082fcc3049e92a64", + "username": "mattsven", + "email": "mattsven@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20749, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a65", + "username": "oluckyman", + "email": "oluckyman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3298, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb2"]] + }, + { + "_id": "62f3291f082fcc3049e92a68", + "username": "beruic", + "email": "beruic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4927, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a69", + "username": "dsanchez", + "email": "dsanchez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 978, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff00", "62f321cc082fcc3049e90eb6"]] + }, + { + "_id": "62f3291f082fcc3049e92a6a", + "username": "David Citron", + "email": "David Citron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42529, + "questionIds": ["62f321bb082fcc3049e8ff00"], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a6b", + "username": "dude", + "email": "dude@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 291, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ebd"]] + }, + { + "_id": "62f3291f082fcc3049e92a6d", + "username": "a paid nerd", + "email": "a paid nerd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29881, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a6f", + "username": "Michael Mikowski", + "email": "Michael Mikowski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1259, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a72", + "username": "wybe", + "email": "wybe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 565, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a73", + "username": "sidonaldson", + "email": "sidonaldson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22770, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ebe"]] + }, + { + "_id": "62f3291f082fcc3049e92a75", + "username": "turtledove", + "email": "turtledove@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24634, + "questionIds": ["62f321bb082fcc3049e8ff01"], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a77", + "username": "Ankit Jaiswal", + "email": "Ankit Jaiswal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22193, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ebc"]] + }, + { + "_id": "62f3291f082fcc3049e92a7a", + "username": "Adi Prasetyo", + "email": "Adi Prasetyo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 940, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3291f082fcc3049e92a7c", + "username": "Adam Arold", + "email": "Adam Arold@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28174, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32920082fcc3049e92a7d", + "username": "serfer2", + "email": "serfer2@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2405, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec0"]] + }, + { + "_id": "62f32920082fcc3049e92a7f", + "username": "confile", + "email": "confile@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31189, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec1"]] + }, + { + "_id": "62f32920082fcc3049e92a80", + "username": "Aabha Pandey", + "email": "Aabha Pandey@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 885, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec3"]] + }, + { + "_id": "62f32920082fcc3049e92a81", + "username": "Achim Krauß", + "email": "Achim Krauß@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 637, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec4"]] + }, + { + "_id": "62f32920082fcc3049e92a82", + "username": "Andi Giga", + "email": "Andi Giga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2918, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec6"]] + }, + { + "_id": "62f32920082fcc3049e92a83", + "username": "Zach Barham", + "email": "Zach Barham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 447, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec5"]] + }, + { + "_id": "62f32920082fcc3049e92a84", + "username": "pyrsmk", + "email": "pyrsmk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 304, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec7"]] + }, + { + "_id": "62f32921082fcc3049e92a85", + "username": "Prashant Yalatwar", + "email": "Prashant Yalatwar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90eca"]] + }, + { + "_id": "62f32921082fcc3049e92a86", + "username": "Hamza Iftikhar", + "email": "Hamza Iftikhar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 506, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ecb"]] + }, + { + "_id": "62f32921082fcc3049e92a87", + "username": "user8202629", + "email": "user8202629@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ec9"]] + }, + { + "_id": "62f32921082fcc3049e92a88", + "username": "Prashant Gupta", + "email": "Prashant Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 726, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ecc"]] + }, + { + "_id": "62f32921082fcc3049e92a89", + "username": "DarkTrick", + "email": "DarkTrick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1451, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff01", "62f321cd082fcc3049e90ecd"]] + }, + { + "_id": "62f32921082fcc3049e92a8a", + "username": "ryebr3ad", + "email": "ryebr3ad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1208, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ed7"]] + }, + { + "_id": "62f32921082fcc3049e92a8b", + "username": "Petter Thowsen", + "email": "Petter Thowsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eda"]] + }, + { + "_id": "62f32921082fcc3049e92a8d", + "username": "DiegoDD", + "email": "DiegoDD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1548, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92a8f", + "username": "kbtz", + "email": "kbtz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12481, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92a91", + "username": "edi9999", + "email": "edi9999@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18408, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92a93", + "username": "The Witness", + "email": "The Witness@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 910, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92a94", + "username": "Vishal", + "email": "Vishal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19097, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ed9"]] + }, + { + "_id": "62f32921082fcc3049e92a97", + "username": "Francisc", + "email": "Francisc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73030, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ed8"]] + }, + { + "_id": "62f32921082fcc3049e92a99", + "username": "Robin Zimmermann", + "email": "Robin Zimmermann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3026, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aa1", + "username": "William Jones", + "email": "William Jones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 669, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aa3", + "username": "RaymondMachira", + "email": "RaymondMachira@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 244, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aa6", + "username": "Lion King", + "email": "Lion King@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31466, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aa9", + "username": "user4463826", + "email": "user4463826@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aab", + "username": "Islam Attrash", + "email": "Islam Attrash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 86, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aad", + "username": "kupendra", + "email": "kupendra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1002, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32921082fcc3049e92aaf", + "username": "M22", + "email": "M22@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 445, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eee"]] + }, + { + "_id": "62f32921082fcc3049e92ab0", + "username": "khr055", + "email": "khr055@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ed6"]] + }, + { + "_id": "62f32922082fcc3049e92ab1", + "username": "vladiim", + "email": "vladiim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1812, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90edd"]] + }, + { + "_id": "62f32922082fcc3049e92ab2", + "username": "Erdi İzgi", + "email": "Erdi İzgi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1162, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90edc"]] + }, + { + "_id": "62f32922082fcc3049e92ab4", + "username": "Sebastien", + "email": "Sebastien@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 944, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32922082fcc3049e92ab6", + "username": "ElChupacabra", + "email": "ElChupacabra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 987, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ede"]] + }, + { + "_id": "62f32922082fcc3049e92ab8", + "username": "Sebastián Lara", + "email": "Sebastián Lara@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5057, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90edf"]] + }, + { + "_id": "62f32922082fcc3049e92aba", + "username": "Arun Joseph", + "email": "Arun Joseph@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2314, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32922082fcc3049e92abb", + "username": "Arun Sharma", + "email": "Arun Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee0"]] + }, + { + "_id": "62f32922082fcc3049e92abc", + "username": "Faiz Mohamed Haneef", + "email": "Faiz Mohamed Haneef@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2954, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee2"], + ["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc3"] + ] + }, + { + "_id": "62f32922082fcc3049e92abd", + "username": "Sabbir Ahmed", + "email": "Sabbir Ahmed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1368, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee4"]] + }, + { + "_id": "62f32922082fcc3049e92abe", + "username": "yonatanmn", + "email": "yonatanmn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1550, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee3"]] + }, + { + "_id": "62f32923082fcc3049e92abf", + "username": "maburdi94", + "email": "maburdi94@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee5"]] + }, + { + "_id": "62f32923082fcc3049e92ac0", + "username": "Husky931", + "email": "Husky931@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 536, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee7"]] + }, + { + "_id": "62f32923082fcc3049e92ac1", + "username": "gnrfan", + "email": "gnrfan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18671, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ee9"]] + }, + { + "_id": "62f32923082fcc3049e92ac2", + "username": "AyushKatiyar", + "email": "AyushKatiyar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 864, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eea"]] + }, + { + "_id": "62f32923082fcc3049e92ac4", + "username": "mesqueeb", + "email": "mesqueeb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4354, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32923082fcc3049e92ac7", + "username": "Ruthe", + "email": "Ruthe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 343, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eec"]] + }, + { + "_id": "62f32923082fcc3049e92ac9", + "username": "The Onin", + "email": "The Onin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4372, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eed"]] + }, + { + "_id": "62f32924082fcc3049e92acb", + "username": "Okiemute Gold", + "email": "Okiemute Gold@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90eef"]] + }, + { + "_id": "62f32924082fcc3049e92acc", + "username": "maxxx", + "email": "maxxx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 567, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ef0"]] + }, + { + "_id": "62f32924082fcc3049e92acd", + "username": "Muluken Getachew", + "email": "Muluken Getachew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 490, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff03", "62f321cd082fcc3049e90ef3"]] + }, + { + "_id": "62f32924082fcc3049e92ace", + "username": "Mirgorod", + "email": "Mirgorod@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29703, + "questionIds": ["62f321bb082fcc3049e8ff03"], + "answerIds": [] + }, + { + "_id": "62f32924082fcc3049e92ad0", + "username": "Jared Insel", + "email": "Jared Insel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 106, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32924082fcc3049e92ad1", + "username": "Chad Grant", + "email": "Chad Grant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 42742, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef4"]] + }, + { + "_id": "62f32924082fcc3049e92ad2", + "username": "tnyfst", + "email": "tnyfst@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1581, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef5"]] + }, + { + "_id": "62f32925082fcc3049e92b13", + "username": "rob", + "email": "rob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9737, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef6"]] + }, + { + "_id": "62f32925082fcc3049e92b14", + "username": "Jakob Sternberg", + "email": "Jakob Sternberg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1710, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90ef9"]] + }, + { + "_id": "62f32925082fcc3049e92b17", + "username": "Zaffy", + "email": "Zaffy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15833, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32925082fcc3049e92b19", + "username": "Teoman shipahi", + "email": "Teoman shipahi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 45922, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32925082fcc3049e92b1a", + "username": "mike", + "email": "mike@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90efa"]] + }, + { + "_id": "62f32925082fcc3049e92b1b", + "username": "Miere", + "email": "Miere@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1389, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90efb"]] + }, + { + "_id": "62f32925082fcc3049e92b1c", + "username": "Jhankar Mahbub", + "email": "Jhankar Mahbub@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9482, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90efd"], + ["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff0"] + ] + }, + { + "_id": "62f32925082fcc3049e92b1d", + "username": "davefrassoni", + "email": "davefrassoni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 310, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90efc"]] + }, + { + "_id": "62f32925082fcc3049e92b1e", + "username": "Whome", + "email": "Whome@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10029, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90efe"]] + }, + { + "_id": "62f32925082fcc3049e92b20", + "username": "Richard J. Ross III", + "email": "Richard J. Ross III@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 54307, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32925082fcc3049e92b22", + "username": "Adrian", + "email": "Adrian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 727, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32925082fcc3049e92b23", + "username": "puchu", + "email": "puchu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3079, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90eff"]] + }, + { + "_id": "62f32926082fcc3049e92b24", + "username": "Dustin Davis", + "email": "Dustin Davis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14312, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f01"]] + }, + { + "_id": "62f32926082fcc3049e92b25", + "username": "Matt Pileggi", + "email": "Matt Pileggi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6918, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f02"]] + }, + { + "_id": "62f32926082fcc3049e92b26", + "username": "Forestrf", + "email": "Forestrf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 364, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f03"]] + }, + { + "_id": "62f32926082fcc3049e92b28", + "username": "malko", + "email": "malko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2214, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f04"]] + }, + { + "_id": "62f32926082fcc3049e92b29", + "username": "Diego Perini", + "email": "Diego Perini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7683, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f05"]] + }, + { + "_id": "62f32926082fcc3049e92b2a", + "username": "Max Heiber", + "email": "Max Heiber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12727, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f07"]] + }, + { + "_id": "62f32926082fcc3049e92b2c", + "username": "chugadie", + "email": "chugadie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2682, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f08"]] + }, + { + "_id": "62f32926082fcc3049e92b2d", + "username": "Antara Roy", + "email": "Antara Roy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 467, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f09"]] + }, + { + "_id": "62f32927082fcc3049e92b2e", + "username": "Manan Sheth", + "email": "Manan Sheth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0a"]] + }, + { + "_id": "62f32927082fcc3049e92b2f", + "username": "user4617883", + "email": "user4617883@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1065, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f0e"]] + }, + { + "_id": "62f32927082fcc3049e92b32", + "username": "dwjohnston", + "email": "dwjohnston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9360, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32927082fcc3049e92b34", + "username": "Null", + "email": "Null@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 801, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f12"]] + }, + { + "_id": "62f32927082fcc3049e92b35", + "username": "Shivam Sharma", + "email": "Shivam Sharma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1057, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f13"]] + }, + { + "_id": "62f32928082fcc3049e92b36", + "username": "Mikser", + "email": "Mikser@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 889, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f14"]] + }, + { + "_id": "62f32928082fcc3049e92b37", + "username": "Chamara Indrajith", + "email": "Chamara Indrajith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 407, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff02", "62f321cd082fcc3049e90f17"]] + }, + { + "_id": "62f32928082fcc3049e92b3b", + "username": "DexieTheSheep", + "email": "DexieTheSheep@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 418, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b3e", + "username": "Connor Simpson", + "email": "Connor Simpson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 477, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b40", + "username": "aron", + "email": "aron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2856, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b41", + "username": "Bill the Lizard", + "email": "Bill the Lizard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 388651, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1a"], + ["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c5"] + ] + }, + { + "_id": "62f32928082fcc3049e92b43", + "username": "Daniel Magliola", + "email": "Daniel Magliola@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29599, + "questionIds": ["62f321bb082fcc3049e8ff04"], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1c"]] + }, + { + "_id": "62f32928082fcc3049e92b45", + "username": "albertein", + "email": "albertein@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25426, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1b"]] + }, + { + "_id": "62f32928082fcc3049e92b49", + "username": "Zaptree", + "email": "Zaptree@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3543, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b4b", + "username": "acorncom", + "email": "acorncom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5925, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b4d", + "username": "Neil Monroe", + "email": "Neil Monroe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1211, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b50", + "username": "oldboy", + "email": "oldboy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5196, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b52", + "username": "acido", + "email": "acido@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 504, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32928082fcc3049e92b54", + "username": "Cegone", + "email": "Cegone@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 437, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b55", + "username": "Richard Parnaby-King", + "email": "Richard Parnaby-King@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14514, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f1f"]] + }, + { + "_id": "62f32929082fcc3049e92b57", + "username": "sohtimsso1970", + "email": "sohtimsso1970@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3196, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b5a", + "username": "Anton P Robul", + "email": "Anton P Robul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 191, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b5c", + "username": "Joseph Lennox", + "email": "Joseph Lennox@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f39"]] + }, + { + "_id": "62f32929082fcc3049e92b5d", + "username": "Miller Medeiros", + "email": "Miller Medeiros@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1256, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f20"]] + }, + { + "_id": "62f32929082fcc3049e92b5f", + "username": "Blaise", + "email": "Blaise@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12451, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b61", + "username": "rsbarro", + "email": "rsbarro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26491, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b64", + "username": "Betty Mock", + "email": "Betty Mock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1363, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b66", + "username": "Chase Sandmann", + "email": "Chase Sandmann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4475, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b69", + "username": "Md. Rejaul Karim", + "email": "Md. Rejaul Karim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b6a", + "username": "jc00ke", + "email": "jc00ke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2335, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f22"]] + }, + { + "_id": "62f32929082fcc3049e92b6c", + "username": "Daniel Fernandez", + "email": "Daniel Fernandez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 111, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f23"]] + }, + { + "_id": "62f32929082fcc3049e92b6f", + "username": "Guy Schalnat", + "email": "Guy Schalnat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1697, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b70", + "username": "daveoncode", + "email": "daveoncode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17910, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f25"]] + }, + { + "_id": "62f32929082fcc3049e92b71", + "username": "Goodeq", + "email": "Goodeq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f24"]] + }, + { + "_id": "62f32929082fcc3049e92b72", + "username": "Julien de Prabère", + "email": "Julien de Prabère@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f27"], + ["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f29"] + ] + }, + { + "_id": "62f32929082fcc3049e92b74", + "username": "vector", + "email": "vector@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7116, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b76", + "username": "Mat Schaffer", + "email": "Mat Schaffer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1589, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32929082fcc3049e92b79", + "username": "GasheK", + "email": "GasheK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 450, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f26"]] + }, + { + "_id": "62f3292a082fcc3049e92b7a", + "username": "troy", + "email": "troy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f28"]] + }, + { + "_id": "62f3292a082fcc3049e92b7c", + "username": "Harrison", + "email": "Harrison@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 175, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3292a082fcc3049e92b7d", + "username": "Diodeus - James MacFarlane", + "email": "Diodeus - James MacFarlane@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 110687, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2a"]] + }, + { + "_id": "62f3292a082fcc3049e92b7f", + "username": "Rocco The Taco", + "email": "Rocco The Taco@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3585, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3292a082fcc3049e92b81", + "username": "crush", + "email": "crush@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16465, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2b"]] + }, + { + "_id": "62f3292a082fcc3049e92b84", + "username": "Ian Dunn", + "email": "Ian Dunn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3452, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3292a082fcc3049e92b85", + "username": "Gate", + "email": "Gate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 495, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2d"]] + }, + { + "_id": "62f3292a082fcc3049e92b88", + "username": "Jonathan M", + "email": "Jonathan M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16845, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2c"]] + }, + { + "_id": "62f3292a082fcc3049e92b8b", + "username": "Serj Sagan", + "email": "Serj Sagan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27192, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3292a082fcc3049e92b8d", + "username": "n8jadams", + "email": "n8jadams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 780, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3292a082fcc3049e92b8e", + "username": "Tim Saylor", + "email": "Tim Saylor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1054, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2e"]] + }, + { + "_id": "62f32967082fcc3049e92b90", + "username": "adamwdraper", + "email": "adamwdraper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 193, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f32"]] + }, + { + "_id": "62f32967082fcc3049e92b91", + "username": "juanOS", + "email": "juanOS@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f33"]] + }, + { + "_id": "62f32967082fcc3049e92b92", + "username": "Kirk Bentley", + "email": "Kirk Bentley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 233, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f36"]] + }, + { + "_id": "62f32967082fcc3049e92b94", + "username": "mendezcode", + "email": "mendezcode@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 757, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f34"]] + }, + { + "_id": "62f32967082fcc3049e92b95", + "username": "Jay Dansand", + "email": "Jay Dansand@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 789, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f37"]] + }, + { + "_id": "62f32967082fcc3049e92b96", + "username": "kalisjoshua", + "email": "kalisjoshua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f38"]] + }, + { + "_id": "62f32967082fcc3049e92b9c", + "username": "Andres Felipe", + "email": "Andres Felipe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4222, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92b9e", + "username": "Mohammed Farooq", + "email": "Mohammed Farooq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 227, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92ba1", + "username": "Matrix", + "email": "Matrix@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3258, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92ba3", + "username": "Besat", + "email": "Besat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1358, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92ba5", + "username": "Faisal", + "email": "Faisal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 298, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92ba7", + "username": "wilmol", + "email": "wilmol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 993, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92ba9", + "username": "MSC", + "email": "MSC@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3168, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92bab", + "username": "Evgeny", + "email": "Evgeny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6161, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92bad", + "username": "Nick Grealy", + "email": "Nick Grealy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21869, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3a"]] + }, + { + "_id": "62f32967082fcc3049e92baf", + "username": "avenmore", + "email": "avenmore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2709, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32967082fcc3049e92bb2", + "username": "Anunay", + "email": "Anunay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 487, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3b"]] + }, + { + "_id": "62f32968082fcc3049e92bb3", + "username": "Chad Kuehn", + "email": "Chad Kuehn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3821, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3c"]] + }, + { + "_id": "62f32968082fcc3049e92bb4", + "username": "Tomas Kubes", + "email": "Tomas Kubes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22232, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3e"]] + }, + { + "_id": "62f32968082fcc3049e92bb6", + "username": "Bhargav Nanekalva", + "email": "Bhargav Nanekalva@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 606, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32968082fcc3049e92bb9", + "username": "iBet7o", + "email": "iBet7o@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 760, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f3f"]] + }, + { + "_id": "62f32968082fcc3049e92bba", + "username": "Mohamed.Abdo", + "email": "Mohamed.Abdo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1776, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f40"]] + }, + { + "_id": "62f32968082fcc3049e92bbc", + "username": "Daniel Barbalace", + "email": "Daniel Barbalace@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1159, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f41"]] + }, + { + "_id": "62f32968082fcc3049e92bbe", + "username": "Lance Anderson", + "email": "Lance Anderson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 493, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32968082fcc3049e92bc0", + "username": "Ethan", + "email": "Ethan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2912, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32968082fcc3049e92bc2", + "username": "Horacio", + "email": "Horacio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1553, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32968082fcc3049e92bc4", + "username": "mp31415", + "email": "mp31415@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6203, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f42"]] + }, + { + "_id": "62f32968082fcc3049e92bc7", + "username": "stackoverfloweth", + "email": "stackoverfloweth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6279, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32968082fcc3049e92bc9", + "username": "Ken Palmer", + "email": "Ken Palmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2225, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f44"]] + }, + { + "_id": "62f32968082fcc3049e92bce", + "username": "jacob", + "email": "jacob@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 295, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f45"]] + }, + { + "_id": "62f32969082fcc3049e92bcf", + "username": "Faysal Haque", + "email": "Faysal Haque@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 411, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f47"]] + }, + { + "_id": "62f32969082fcc3049e92bd2", + "username": "Diego Fernando Villarroel Diaz", + "email": "Diego Fernando Villarroel Diaz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 96, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f46"]] + }, + { + "_id": "62f32969082fcc3049e92bd3", + "username": "James Eames", + "email": "James Eames@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f48"]] + }, + { + "_id": "62f32969082fcc3049e92bd7", + "username": "Daniel Campos", + "email": "Daniel Campos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 961, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4a"]] + }, + { + "_id": "62f32969082fcc3049e92bd8", + "username": "Choylton B. Higginbottom", + "email": "Choylton B. Higginbottom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2166, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4b"]] + }, + { + "_id": "62f32969082fcc3049e92bd9", + "username": "Nicolas Giszpenc", + "email": "Nicolas Giszpenc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 629, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4d"]] + }, + { + "_id": "62f32969082fcc3049e92bda", + "username": "Nick", + "email": "Nick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 126339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4c"]] + }, + { + "_id": "62f32969082fcc3049e92bdb", + "username": "Adam Pery", + "email": "Adam Pery@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1788, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f4e"]] + }, + { + "_id": "62f3296a082fcc3049e92bdc", + "username": "Vinod Kumar", + "email": "Vinod Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1045, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f50"]] + }, + { + "_id": "62f3296a082fcc3049e92bde", + "username": "gadielkalleb", + "email": "gadielkalleb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f52"]] + }, + { + "_id": "62f3296a082fcc3049e92be0", + "username": "Benson", + "email": "Benson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 22191, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f7d"]] + }, + { + "_id": "62f3296a082fcc3049e92be1", + "username": "fisherwebdev", + "email": "fisherwebdev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12638, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f7e"]] + }, + { + "_id": "62f3296a082fcc3049e92be2", + "username": "stewe", + "email": "stewe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40814, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f7f"]] + }, + { + "_id": "62f3296a082fcc3049e92be3", + "username": "shash7", + "email": "shash7@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1689, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f81"]] + }, + { + "_id": "62f3296a082fcc3049e92be5", + "username": "Erik Reppen", + "email": "Erik Reppen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4577, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296a082fcc3049e92be6", + "username": "Ajay Tiwari", + "email": "Ajay Tiwari@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3318, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f80"]] + }, + { + "_id": "62f3296a082fcc3049e92be8", + "username": "BoxerBucks", + "email": "BoxerBucks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3104, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f83"]] + }, + { + "_id": "62f3296a082fcc3049e92be9", + "username": "Joonas", + "email": "Joonas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2192, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f82"]] + }, + { + "_id": "62f3296b082fcc3049e92bea", + "username": "I_Debug_Everything", + "email": "I_Debug_Everything@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3766, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f85"]] + }, + { + "_id": "62f3296b082fcc3049e92bec", + "username": "Wanny Miarelli", + "email": "Wanny Miarelli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 686, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bee", + "username": "matanster", + "email": "matanster@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14692, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f88"], + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fde"] + ] + }, + { + "_id": "62f3296b082fcc3049e92bef", + "username": "Sean Zhao", + "email": "Sean Zhao@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1436, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f84"]] + }, + { + "_id": "62f3296b082fcc3049e92bf1", + "username": "refactor", + "email": "refactor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12456, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bf2", + "username": "Tony O'Hagan", + "email": "Tony O'Hagan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20478, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f86"]] + }, + { + "_id": "62f3296b082fcc3049e92bf3", + "username": "Vinayak Bevinakatti", + "email": "Vinayak Bevinakatti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39723, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f87"]] + }, + { + "_id": "62f3296b082fcc3049e92bf4", + "username": "mbert65", + "email": "mbert65@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 217, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0b", "62f3220b082fcc3049e90f89"]] + }, + { + "_id": "62f3296b082fcc3049e92bf5", + "username": "Legend", + "email": "Legend@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 109904, + "questionIds": ["62f321bb082fcc3049e8ff0b"], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bf7", + "username": "Andrew Koper", + "email": "Andrew Koper@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5286, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bfa", + "username": "The Red Pea", + "email": "The Red Pea@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15343, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bfc", + "username": "Fthr", + "email": "Fthr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 783, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92bfe", + "username": "Massimiliano Kraus", + "email": "Massimiliano Kraus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3451, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92c00", + "username": "Harold_Finch", + "email": "Harold_Finch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 652, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92c02", + "username": "Rishi Dua", + "email": "Rishi Dua@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2256, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92c05", + "username": "Salam", + "email": "Salam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 847, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92c07", + "username": "Fabien Haddadi", + "email": "Fabien Haddadi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1476, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296b082fcc3049e92c09", + "username": "ygoe", + "email": "ygoe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17004, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c0b", + "username": "zeuf", + "email": "zeuf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 379, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c0c", + "username": "Ben Greenaway", + "email": "Ben Greenaway@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 181, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f8e"]] + }, + { + "_id": "62f3296c082fcc3049e92c0f", + "username": "Ruwantha", + "email": "Ruwantha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2555, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c11", + "username": "Christopher", + "email": "Christopher@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3319, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c13", + "username": "Cindy Meister", + "email": "Cindy Meister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24495, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c16", + "username": "Elias", + "email": "Elias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 673, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c17", + "username": "Carl Onager", + "email": "Carl Onager@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f90"]] + }, + { + "_id": "62f3296c082fcc3049e92c19", + "username": "Berci", + "email": "Berci@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 474, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c1a", + "username": "Mohammad Faisal", + "email": "Mohammad Faisal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1939, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f92"]] + }, + { + "_id": "62f3296c082fcc3049e92c1b", + "username": "boateng", + "email": "boateng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 864, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f95"]] + }, + { + "_id": "62f3296c082fcc3049e92c1d", + "username": "David Meza", + "email": "David Meza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2871, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c1e", + "username": "Marvil Joy", + "email": "Marvil Joy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 662, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f93"]] + }, + { + "_id": "62f3296c082fcc3049e92c20", + "username": "Christian Læirbag", + "email": "Christian Læirbag@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 226, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296c082fcc3049e92c21", + "username": "zackify", + "email": "zackify@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5086, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f94"]] + }, + { + "_id": "62f3296c082fcc3049e92c22", + "username": "Frank Conijn - Support Ukraine", + "email": "Frank Conijn - Support Ukraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2944, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f96"]] + }, + { + "_id": "62f3296c082fcc3049e92c23", + "username": "Hector Llorens", + "email": "Hector Llorens@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 125, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f97"]] + }, + { + "_id": "62f3296d082fcc3049e92c24", + "username": "user5846985", + "email": "user5846985@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f98"]] + }, + { + "_id": "62f3296d082fcc3049e92c25", + "username": "Belgacem Ksiksi", + "email": "Belgacem Ksiksi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 274, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f99"]] + }, + { + "_id": "62f3296d082fcc3049e92c26", + "username": "Genko", + "email": "Genko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 345, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9a"]] + }, + { + "_id": "62f3296d082fcc3049e92c28", + "username": "ReinstateMonica3167040", + "email": "ReinstateMonica3167040@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 664, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296d082fcc3049e92c29", + "username": "Ani Menon", + "email": "Ani Menon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25600, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9b"]] + }, + { + "_id": "62f3296d082fcc3049e92c2a", + "username": "Rounin - Standing with Ukraine", + "email": "Rounin - Standing with Ukraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24868, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9c"]] + }, + { + "_id": "62f3296d082fcc3049e92c2c", + "username": "Bharata", + "email": "Bharata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12857, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296d082fcc3049e92c2d", + "username": "Javad Kargar", + "email": "Javad Kargar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1085, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9d"]] + }, + { + "_id": "62f3296d082fcc3049e92c2f", + "username": "Geshan Ravindu", + "email": "Geshan Ravindu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 441, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296d082fcc3049e92c30", + "username": "ThomAce", + "email": "ThomAce@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90f9f"]] + }, + { + "_id": "62f3296d082fcc3049e92c31", + "username": "Knautiluz", + "email": "Knautiluz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 354, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa0"]] + }, + { + "_id": "62f3296e082fcc3049e92c32", + "username": "Pal Singh", + "email": "Pal Singh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1941, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa2"]] + }, + { + "_id": "62f3296e082fcc3049e92c33", + "username": "Olawale Oladiran", + "email": "Olawale Oladiran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 455, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa3"]] + }, + { + "_id": "62f3296e082fcc3049e92c35", + "username": "Thanasis", + "email": "Thanasis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296e082fcc3049e92c37", + "username": "Zack Plauché", + "email": "Zack Plauché@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2220, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296e082fcc3049e92c39", + "username": "Dilip Agheda", + "email": "Dilip Agheda@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2179, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296e082fcc3049e92c3a", + "username": "Clairton Luz", + "email": "Clairton Luz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1940, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa4"]] + }, + { + "_id": "62f3296e082fcc3049e92c3c", + "username": "Dominic Smith", + "email": "Dominic Smith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 542, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296e082fcc3049e92c3d", + "username": "Emmanuel Onah", + "email": "Emmanuel Onah@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 531, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa7"], + ["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9b"] + ] + }, + { + "_id": "62f3296e082fcc3049e92c3e", + "username": "emptywalls", + "email": "emptywalls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1187, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa8"]] + }, + { + "_id": "62f3296e082fcc3049e92c3f", + "username": "Dhairya Lakhera", + "email": "Dhairya Lakhera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3985, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fa9"]] + }, + { + "_id": "62f3296e082fcc3049e92c40", + "username": "Ezra Siton", + "email": "Ezra Siton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5351, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90faa"]] + }, + { + "_id": "62f3296e082fcc3049e92c41", + "username": "questionto42standswithUkraine", + "email": "questionto42standswithUkraine@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4485, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff08", "62f3220b082fcc3049e90fab"]] + }, + { + "_id": "62f3296e082fcc3049e92c42", + "username": "Fire Hand", + "email": "Fire Hand@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24196, + "questionIds": ["62f321bb082fcc3049e8ff08"], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c44", + "username": "Burak", + "email": "Burak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1314, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c46", + "username": "Matt Ball", + "email": "Matt Ball@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 346201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fba"]] + }, + { + "_id": "62f3296f082fcc3049e92c47", + "username": "arb", + "email": "arb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7475, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fbb"]] + }, + { + "_id": "62f3296f082fcc3049e92c49", + "username": "Qqwy", + "email": "Qqwy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4734, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c4a", + "username": "user920041", + "email": "user920041@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fbc"]] + }, + { + "_id": "62f3296f082fcc3049e92c4c", + "username": "Rafay", + "email": "Rafay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23215, + "questionIds": ["62f321bb082fcc3049e8ff06"], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c4e", + "username": "Doug Molineux", + "email": "Doug Molineux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12025, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c50", + "username": "computrius", + "email": "computrius@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 662, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c53", + "username": "David Callanan", + "email": "David Callanan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4852, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c55", + "username": "Tom Russell", + "email": "Tom Russell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 980, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c57", + "username": "Ian Steffy", + "email": "Ian Steffy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1240, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c58", + "username": "Marc B", + "email": "Marc B@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 349932, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fb9"]] + }, + { + "_id": "62f3296f082fcc3049e92c5a", + "username": "Victor Grazi", + "email": "Victor Grazi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14654, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c5c", + "username": "AJ_83", + "email": "AJ_83@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 269, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c5e", + "username": "Daniel Z.", + "email": "Daniel Z.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2520, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c60", + "username": "Umut Çağdaş Coşkun", + "email": "Umut Çağdaş Coşkun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 920, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c62", + "username": "Jona", + "email": "Jona@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 973, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c64", + "username": "corsiKa", + "email": "corsiKa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79627, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c65", + "username": "Danny R", + "email": "Danny R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fbe"]] + }, + { + "_id": "62f3296f082fcc3049e92c67", + "username": "Adria", + "email": "Adria@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8206, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c6b", + "username": "Jadiel de Armas", + "email": "Jadiel de Armas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7717, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc1"]] + }, + { + "_id": "62f3296f082fcc3049e92c6e", + "username": "developerbmw", + "email": "developerbmw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4033, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c70", + "username": "Meghan", + "email": "Meghan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1155, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f3296f082fcc3049e92c71", + "username": "user2417527", + "email": "user2417527@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fbd"]] + }, + { + "_id": "62f3296f082fcc3049e92c72", + "username": "Vappor Washmade", + "email": "Vappor Washmade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc0"]] + }, + { + "_id": "62f3296f082fcc3049e92c73", + "username": "Raman Sohi", + "email": "Raman Sohi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fbf"]] + }, + { + "_id": "62f3296f082fcc3049e92c75", + "username": "Ondrej Svejdar", + "email": "Ondrej Svejdar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20502, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc2"]] + }, + { + "_id": "62f32970082fcc3049e92c77", + "username": "Rob Sedgwick", + "email": "Rob Sedgwick@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4194, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc4"]] + }, + { + "_id": "62f32970082fcc3049e92c7a", + "username": "HovyTech", + "email": "HovyTech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 259, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc5"]] + }, + { + "_id": "62f32970082fcc3049e92c7c", + "username": "gnzg", + "email": "gnzg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 370, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32970082fcc3049e92c7e", + "username": "OzzyCzech", + "email": "OzzyCzech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8837, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32970082fcc3049e92c7f", + "username": "viktarpunko", + "email": "viktarpunko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 354, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc8"]] + }, + { + "_id": "62f32970082fcc3049e92c80", + "username": "Philll_t", + "email": "Philll_t@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4059, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fca"]] + }, + { + "_id": "62f32970082fcc3049e92c82", + "username": "krillgar", + "email": "krillgar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12203, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32970082fcc3049e92c84", + "username": "Frank Roth", + "email": "Frank Roth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5931, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fc9"]] + }, + { + "_id": "62f32970082fcc3049e92c85", + "username": "Konrad Kiss", + "email": "Konrad Kiss@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6796, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fcc"]] + }, + { + "_id": "62f32971082fcc3049e92c86", + "username": "iRohitBhatia", + "email": "iRohitBhatia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3169, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd2"]] + }, + { + "_id": "62f32971082fcc3049e92c89", + "username": "shrekuu", + "email": "shrekuu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1206, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32971082fcc3049e92c8b", + "username": "Cyril N.", + "email": "Cyril N.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37732, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fcf"]] + }, + { + "_id": "62f32971082fcc3049e92c8d", + "username": "Michiel", + "email": "Michiel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4061, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32971082fcc3049e92c8e", + "username": "Dimitar Nikovski", + "email": "Dimitar Nikovski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 905, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd1"]] + }, + { + "_id": "62f32971082fcc3049e92c90", + "username": "Antony Hatchkins", + "email": "Antony Hatchkins@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29232, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32971082fcc3049e92c91", + "username": "dylanh724", + "email": "dylanh724@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 803, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fce"]] + }, + { + "_id": "62f32971082fcc3049e92c92", + "username": "Sunny", + "email": "Sunny@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 987, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd5"]] + }, + { + "_id": "62f32971082fcc3049e92c94", + "username": "Nicolas", + "email": "Nicolas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7844, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92c96", + "username": "user17344058", + "email": "user17344058@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff06", "62f3220c082fcc3049e90fd7"]] + }, + { + "_id": "62f32972082fcc3049e92c98", + "username": "klh", + "email": "klh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 595, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92c9a", + "username": "Undefined", + "email": "Undefined@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10944, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92c9c", + "username": "ErikE", + "email": "ErikE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46992, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92c9e", + "username": "leech", + "email": "leech@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8188, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fda"]] + }, + { + "_id": "62f32972082fcc3049e92c9f", + "username": "David Campbell", + "email": "David Campbell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fdf"]] + }, + { + "_id": "62f32972082fcc3049e92ca1", + "username": "Reza-S4", + "email": "Reza-S4@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1032, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92ca4", + "username": "James Wakefield", + "email": "James Wakefield@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 526, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92ca5", + "username": "kenshou.html", + "email": "kenshou.html@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 950, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe0"]] + }, + { + "_id": "62f32972082fcc3049e92cab", + "username": "Loligans", + "email": "Loligans@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 439, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cac", + "username": "Bendegúz", + "email": "Bendegúz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 748, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fdb"]] + }, + { + "_id": "62f32972082fcc3049e92cae", + "username": "thefourtheye", + "email": "thefourtheye@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 223324, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cb1", + "username": "Iván Pérez", + "email": "Iván Pérez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2003, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cb2", + "username": "tanguy_k", + "email": "tanguy_k@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10440, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fdc"]] + }, + { + "_id": "62f32972082fcc3049e92cb4", + "username": "stricjux", + "email": "stricjux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1384, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cb6", + "username": "Shamasis Bhattacharya", + "email": "Shamasis Bhattacharya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3152, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cb9", + "username": "corwin.amber", + "email": "corwin.amber@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 350, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cba", + "username": "Anthony", + "email": "Anthony@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2621, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fd9"], + ["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91026"] + ] + }, + { + "_id": "62f32972082fcc3049e92cbe", + "username": "LosManos", + "email": "LosManos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6952, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cc2", + "username": "Hatoru Hansou", + "email": "Hatoru Hansou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 299, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cc4", + "username": "NeverGiveUp161", + "email": "NeverGiveUp161@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 806, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cc6", + "username": "magor", + "email": "magor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 649, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92cc9", + "username": "Gaurav Gandhi", + "email": "Gaurav Gandhi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2836, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32972082fcc3049e92ccb", + "username": "Blessing", + "email": "Blessing@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1950, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32973082fcc3049e92cce", + "username": "Diadistis", + "email": "Diadistis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11857, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe2"]] + }, + { + "_id": "62f32973082fcc3049e92cd1", + "username": "Laxmikant Dange", + "email": "Laxmikant Dange@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7437, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe1"]] + }, + { + "_id": "62f32973082fcc3049e92cd4", + "username": "Manish Jain", + "email": "Manish Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9341, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe6"]] + }, + { + "_id": "62f32973082fcc3049e92cd6", + "username": "Ruben", + "email": "Ruben@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 831, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32973082fcc3049e92cd8", + "username": "Cauterite", + "email": "Cauterite@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1518, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0a", "62f3220c082fcc3049e90fe5"]] + }, + { + "_id": "62f32973082fcc3049e92cdb", + "username": "Therichpost", + "email": "Therichpost@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1731, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32973082fcc3049e92cdc", + "username": "akano1", + "email": "akano1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 38628, + "questionIds": ["62f321bb082fcc3049e8ff0a"], + "answerIds": [] + }, + { + "_id": "62f32973082fcc3049e92cdd", + "username": "krosenvold", + "email": "krosenvold@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 73699, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fe9"]] + }, + { + "_id": "62f32973082fcc3049e92cde", + "username": "geowa4", + "email": "geowa4@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39152, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fea"]] + }, + { + "_id": "62f32973082fcc3049e92ce0", + "username": "Kenan Banks", + "email": "Kenan Banks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 199684, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32974082fcc3049e92ce2", + "username": "IgorGanapolsky", + "email": "IgorGanapolsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25027, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32974082fcc3049e92ce4", + "username": "kennytm", + "email": "kennytm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 494855, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90feb"]] + }, + { + "_id": "62f32974082fcc3049e92ce6", + "username": "GazB", + "email": "GazB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3394, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32974082fcc3049e92ce8", + "username": "Mig82", + "email": "Mig82@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fee"]] + }, + { + "_id": "62f32974082fcc3049e92cea", + "username": "mplwork", + "email": "mplwork@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1060, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32974082fcc3049e92cec", + "username": "Ben Aston", + "email": "Ben Aston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49846, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32974082fcc3049e92ced", + "username": "austincheney", + "email": "austincheney@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fed"]] + }, + { + "_id": "62f32974082fcc3049e92cee", + "username": "James McMahon", + "email": "James McMahon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 47157, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90fec"]] + }, + { + "_id": "62f32974082fcc3049e92cf1", + "username": "Yeasin Abedin", + "email": "Yeasin Abedin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1699, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff2"]] + }, + { + "_id": "62f32974082fcc3049e92cf2", + "username": "koredalin", + "email": "koredalin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 446, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff3"]] + }, + { + "_id": "62f32974082fcc3049e92cf4", + "username": "A. Randhawa", + "email": "A. Randhawa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 49, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff4"]] + }, + { + "_id": "62f32975082fcc3049e92cf5", + "username": "jackbean818", + "email": "jackbean818@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 174, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff5"]] + }, + { + "_id": "62f32975082fcc3049e92cf6", + "username": "mrmaclean89", + "email": "mrmaclean89@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 538, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff7"]] + }, + { + "_id": "62f32975082fcc3049e92cf7", + "username": "Abdur Rahman", + "email": "Abdur Rahman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 821, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff9"]] + }, + { + "_id": "62f32975082fcc3049e92cf8", + "username": "Gibolt", + "email": "Gibolt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34943, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ff8"]] + }, + { + "_id": "62f32975082fcc3049e92cf9", + "username": "Vivek Mehta", + "email": "Vivek Mehta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 664, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ffa"]] + }, + { + "_id": "62f32975082fcc3049e92cfb", + "username": "iAmOren", + "email": "iAmOren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2678, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32975082fcc3049e92cfc", + "username": "Davaakhuu Erdenekhuu", + "email": "Davaakhuu Erdenekhuu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 228, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ffb"]] + }, + { + "_id": "62f32975082fcc3049e92cfd", + "username": "ludy", + "email": "ludy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ffd"]] + }, + { + "_id": "62f32975082fcc3049e92cfe", + "username": "Ahmed Khashaba", + "email": "Ahmed Khashaba@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff0c", "62f3220c082fcc3049e90ffc"]] + }, + { + "_id": "62f32975082fcc3049e92cff", + "username": "lYriCAlsSH", + "email": "lYriCAlsSH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56326, + "questionIds": ["62f321bb082fcc3049e8ff0c"], + "answerIds": [] + }, + { + "_id": "62f32975082fcc3049e92d01", + "username": "Ricket", + "email": "Ricket@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32359, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32975082fcc3049e92d03", + "username": "Cesar Vega", + "email": "Cesar Vega@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 445, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32975082fcc3049e92d04", + "username": "Ady", + "email": "Ady@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4726, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e90ffe"]] + }, + { + "_id": "62f32976082fcc3049e92d07", + "username": "Michiel Overeem", + "email": "Michiel Overeem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3724, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e90fff"]] + }, + { + "_id": "62f32976082fcc3049e92d09", + "username": "Kramii", + "email": "Kramii@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8269, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91001"]] + }, + { + "_id": "62f32976082fcc3049e92d0c", + "username": "Andrew Hedges", + "email": "Andrew Hedges@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91002"]] + }, + { + "_id": "62f32976082fcc3049e92d0e", + "username": "SgtPooki", + "email": "SgtPooki@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10249, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32976082fcc3049e92d11", + "username": "jjg", + "email": "jjg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 749, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32976082fcc3049e92d14", + "username": "Chris Calo", + "email": "Chris Calo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7029, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32976082fcc3049e92d17", + "username": "Jozzeh", + "email": "Jozzeh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 823, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91005"]] + }, + { + "_id": "62f32976082fcc3049e92d19", + "username": "Tom Lianza", + "email": "Tom Lianza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3952, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91006"]] + }, + { + "_id": "62f32976082fcc3049e92d1b", + "username": "pilavdzice", + "email": "pilavdzice@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32976082fcc3049e92d1d", + "username": "mauro", + "email": "mauro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91008"]] + }, + { + "_id": "62f32976082fcc3049e92d1f", + "username": "sudhAnsu63", + "email": "sudhAnsu63@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5790, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d21", + "username": "Schmuli", + "email": "Schmuli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 733, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d23", + "username": "jkd", + "email": "jkd@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1045, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d24", + "username": "Arne", + "email": "Arne@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2926, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100a"]] + }, + { + "_id": "62f32977082fcc3049e92d27", + "username": "BadHorsie", + "email": "BadHorsie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13742, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d28", + "username": "Dodzi Dzakuma", + "email": "Dodzi Dzakuma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1386, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100c"]] + }, + { + "_id": "62f32977082fcc3049e92d2a", + "username": "Lauri", + "email": "Lauri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1190, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d2c", + "username": "Bastiaan Linders", + "email": "Bastiaan Linders@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1676, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91009"]] + }, + { + "_id": "62f32977082fcc3049e92d2e", + "username": "ma11hew28", + "email": "ma11hew28@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 115088, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d30", + "username": "Mohsen", + "email": "Mohsen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 62256, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100b"]] + }, + { + "_id": "62f32977082fcc3049e92d33", + "username": "aelgoa", + "email": "aelgoa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1123, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100e"]] + }, + { + "_id": "62f32977082fcc3049e92d36", + "username": "Eric Rini", + "email": "Eric Rini@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1810, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32977082fcc3049e92d3a", + "username": "Mariusz Nowak", + "email": "Mariusz Nowak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30682, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100d"]] + }, + { + "_id": "62f32977082fcc3049e92d3b", + "username": "cavalcade", + "email": "cavalcade@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1371, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91010"]] + }, + { + "_id": "62f32977082fcc3049e92d3c", + "username": "mariotti", + "email": "mariotti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 412, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9100f"]] + }, + { + "_id": "62f32977082fcc3049e92d3f", + "username": "Frederic", + "email": "Frederic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91011"]] + }, + { + "_id": "62f32978082fcc3049e92d41", + "username": "Spets", + "email": "Spets@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2381, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32978082fcc3049e92d42", + "username": "Eugene Ramirez", + "email": "Eugene Ramirez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2933, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91013"]] + }, + { + "_id": "62f32978082fcc3049e92d44", + "username": "zkent", + "email": "zkent@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 980, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32978082fcc3049e92d46", + "username": "Faither", + "email": "Faither@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 760, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91029"]] + }, + { + "_id": "62f32978082fcc3049e92d47", + "username": "garysb", + "email": "garysb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91014"]] + }, + { + "_id": "62f32978082fcc3049e92d48", + "username": "moomoo", + "email": "moomoo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 736, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91015"]] + }, + { + "_id": "62f32978082fcc3049e92d4a", + "username": "dudewad", + "email": "dudewad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12092, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32978082fcc3049e92d4c", + "username": "Davina Leong", + "email": "Davina Leong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 727, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32978082fcc3049e92d4e", + "username": "OCDev", + "email": "OCDev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91027"]] + }, + { + "_id": "62f32978082fcc3049e92d4f", + "username": "user1429980", + "email": "user1429980@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6498, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91016"]] + }, + { + "_id": "62f32978082fcc3049e92d50", + "username": "Kat Lim Ruiz", + "email": "Kat Lim Ruiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91018"]] + }, + { + "_id": "62f32978082fcc3049e92d51", + "username": "Jules", + "email": "Jules@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91017"]] + }, + { + "_id": "62f32978082fcc3049e92d52", + "username": "John Kurlak", + "email": "John Kurlak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6361, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101a"]] + }, + { + "_id": "62f32978082fcc3049e92d53", + "username": "Juan C. Roldán", + "email": "Juan C. Roldán@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 828, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91019"]] + }, + { + "_id": "62f32978082fcc3049e92d54", + "username": "MetallimaX", + "email": "MetallimaX@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 594, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101b"]] + }, + { + "_id": "62f32979082fcc3049e92d56", + "username": "Simone Poggi", + "email": "Simone Poggi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1378, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32979082fcc3049e92d57", + "username": "GijsjanB", + "email": "GijsjanB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9820, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101f"]] + }, + { + "_id": "62f32979082fcc3049e92d58", + "username": "JJTalik", + "email": "JJTalik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 86, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e9101e"]] + }, + { + "_id": "62f32979082fcc3049e92d5a", + "username": "Adam Calvet Bohl", + "email": "Adam Calvet Bohl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 999, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32979082fcc3049e92d5c", + "username": "Suraj Jain", + "email": "Suraj Jain@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32979082fcc3049e92d5f", + "username": "styfle", + "email": "styfle@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19867, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32979082fcc3049e92d61", + "username": "Alec Mev", + "email": "Alec Mev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4421, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91020"]] + }, + { + "_id": "62f32979082fcc3049e92d64", + "username": "Mikko Rantalainen", + "email": "Mikko Rantalainen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11975, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d66", + "username": "lolol", + "email": "lolol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4201, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91028"]] + }, + { + "_id": "62f329b6082fcc3049e92d69", + "username": "Alan Geleynse", + "email": "Alan Geleynse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24321, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9102c"]] + }, + { + "_id": "62f329b6082fcc3049e92d6d", + "username": "scotts", + "email": "scotts@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3960, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d6f", + "username": "squarecandy", + "email": "squarecandy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4536, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d72", + "username": "Jim Puls", + "email": "Jim Puls@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 76328, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9102b"]] + }, + { + "_id": "62f329b6082fcc3049e92d74", + "username": "Ilker Cat", + "email": "Ilker Cat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1732, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d77", + "username": "Maddy", + "email": "Maddy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 473, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d7a", + "username": "Liglo App", + "email": "Liglo App@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3611, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d7c", + "username": "David Tang", + "email": "David Tang@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 90124, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9102d"]] + }, + { + "_id": "62f329b6082fcc3049e92d7e", + "username": "demisx", + "email": "demisx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6331, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d7f", + "username": "Jith", + "email": "Jith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1679, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9102e"]] + }, + { + "_id": "62f329b6082fcc3049e92d81", + "username": "jpsimons", + "email": "jpsimons@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26026, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9102f"]] + }, + { + "_id": "62f329b6082fcc3049e92d83", + "username": "Zelphir Kaltstahl", + "email": "Zelphir Kaltstahl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5177, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b6082fcc3049e92d85", + "username": "Fuseteam", + "email": "Fuseteam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 366, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d87", + "username": "john ktejik", + "email": "john ktejik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5637, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d88", + "username": "skalee", + "email": "skalee@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11701, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91033"]] + }, + { + "_id": "62f329b7082fcc3049e92d8b", + "username": "Broxzier", + "email": "Broxzier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2811, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d8c", + "username": "boslior", + "email": "boslior@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91032"]] + }, + { + "_id": "62f329b7082fcc3049e92d8d", + "username": "shadowstorm", + "email": "shadowstorm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 231, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91034"]] + }, + { + "_id": "62f329b7082fcc3049e92d8f", + "username": "qwertzguy", + "email": "qwertzguy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13395, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d92", + "username": "asdf3.14159", + "email": "asdf3.14159@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 506, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d94", + "username": "T Tse", + "email": "T Tse@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 717, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b7082fcc3049e92d96", + "username": "Brian Kelley", + "email": "Brian Kelley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2364, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91031"]] + }, + { + "_id": "62f329b7082fcc3049e92d97", + "username": "Ravindra Miyani", + "email": "Ravindra Miyani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 342, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91035"]] + }, + { + "_id": "62f329b7082fcc3049e92d98", + "username": "Zv_oDD", + "email": "Zv_oDD@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1744, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91036"]] + }, + { + "_id": "62f329b7082fcc3049e92d99", + "username": "SoEzPz", + "email": "SoEzPz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91037"]] + }, + { + "_id": "62f329b7082fcc3049e92d9a", + "username": "curtwphillips", + "email": "curtwphillips@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5073, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91039"]] + }, + { + "_id": "62f329b7082fcc3049e92d9b", + "username": "de3", + "email": "de3@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1836, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103a"]] + }, + { + "_id": "62f329b8082fcc3049e92d9d", + "username": "Fareed Alnamrouti", + "email": "Fareed Alnamrouti@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28973, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92d9e", + "username": "RajeshKdev", + "email": "RajeshKdev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6207, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103b"]] + }, + { + "_id": "62f329b8082fcc3049e92da1", + "username": "Melab", + "email": "Melab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2366, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92da4", + "username": "Aidan Welch", + "email": "Aidan Welch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 372, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92da5", + "username": "Ferran Maylinch", + "email": "Ferran Maylinch@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10387, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e9103d"]] + }, + { + "_id": "62f329b8082fcc3049e92da7", + "username": "Thiago Yoithi", + "email": "Thiago Yoithi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 475, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92dab", + "username": "ropo", + "email": "ropo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1496, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92dad", + "username": "Fanky", + "email": "Fanky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1458, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92db0", + "username": "johnsnails", + "email": "johnsnails@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1934, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92db3", + "username": "ha9u63ar", + "email": "ha9u63ar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5587, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b8082fcc3049e92db4", + "username": "user2878850", + "email": "user2878850@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2236, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91041"]] + }, + { + "_id": "62f329b8082fcc3049e92db5", + "username": "John Lord", + "email": "John Lord@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1721, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91044"]] + }, + { + "_id": "62f329b9082fcc3049e92db7", + "username": "mckeed", + "email": "mckeed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9615, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b9082fcc3049e92db8", + "username": "Praveen Kumar", + "email": "Praveen Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 643, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff07", "62f3220c082fcc3049e91048"]] + }, + { + "_id": "62f329b9082fcc3049e92dba", + "username": "DanCat", + "email": "DanCat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2324, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b9082fcc3049e92dbc", + "username": "jcaron", + "email": "jcaron@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16791, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329b9082fcc3049e92dbd", + "username": "seth", + "email": "seth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36099, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feae", "62f321bc082fcc3049e8ff4f"]] + }, + { + "_id": "62f329b9082fcc3049e92dbe", + "username": "Mark Rogers", + "email": "Mark Rogers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94331, + "questionIds": ["62f321bb082fcc3049e8feae"], + "answerIds": [] + }, + { + "_id": "62f329ba082fcc3049e92dc0", + "username": "Yohannes Kristiawan", + "email": "Yohannes Kristiawan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 223, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8fff0"]] + }, + { + "_id": "62f329ba082fcc3049e92dc1", + "username": "user8331407", + "email": "user8331407@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f321bc082fcc3049e8ffef"]] + }, + { + "_id": "62f329ba082fcc3049e92dc2", + "username": "Evgeniy Miroshnichenko", + "email": "Evgeniy Miroshnichenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1648, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ee"]] + }, + { + "_id": "62f329ba082fcc3049e92dc3", + "username": "xgqfrms", + "email": "xgqfrms@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7717, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900ef"]] + }, + { + "_id": "62f329ba082fcc3049e92dc4", + "username": "gabriel211", + "email": "gabriel211@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f1"]] + }, + { + "_id": "62f329ba082fcc3049e92dc5", + "username": "Mesut Yiğit", + "email": "Mesut Yiğit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 629, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f0"]] + }, + { + "_id": "62f329ba082fcc3049e92dc6", + "username": "gm2008", + "email": "gm2008@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4115, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f2"]] + }, + { + "_id": "62f329ba082fcc3049e92dc7", + "username": "Yairopro", + "email": "Yairopro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7301, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb5", "62f321bd082fcc3049e900f3"]] + }, + { + "_id": "62f329ba082fcc3049e92dc8", + "username": "Alec Smart", + "email": "Alec Smart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91006, + "questionIds": ["62f321bb082fcc3049e8feb5"], + "answerIds": [] + }, + { + "_id": "62f329ba082fcc3049e92dc9", + "username": "cyberrspiritt", + "email": "cyberrspiritt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 848, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a1"]] + }, + { + "_id": "62f329ba082fcc3049e92dcb", + "username": "Kamal Nayan", + "email": "Kamal Nayan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1772, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329ba082fcc3049e92dcc", + "username": "Vinod Ranga", + "email": "Vinod Ranga@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 577, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a4"]] + }, + { + "_id": "62f329ba082fcc3049e92dce", + "username": "Brijeshkumar", + "email": "Brijeshkumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 107, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a3"]] + }, + { + "_id": "62f329bb082fcc3049e92ddb", + "username": "Etienne Martin", + "email": "Etienne Martin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8521, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bb082fcc3049e92ddc", + "username": "Shubham Kumar", + "email": "Shubham Kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1183, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a6"]] + }, + { + "_id": "62f329bb082fcc3049e92ddd", + "username": "Priya", + "email": "Priya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 126, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a5"]] + }, + { + "_id": "62f329bb082fcc3049e92dde", + "username": "Kentonbmax", + "email": "Kentonbmax@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 908, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a8"]] + }, + { + "_id": "62f329bb082fcc3049e92ddf", + "username": "CVE-RICK", + "email": "CVE-RICK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 166, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a7"]] + }, + { + "_id": "62f329bb082fcc3049e92de0", + "username": "chemic", + "email": "chemic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 790, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901a9"]] + }, + { + "_id": "62f329bb082fcc3049e92de1", + "username": "Guy", + "email": "Guy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63464, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020a"]] + }, + { + "_id": "62f329bb082fcc3049e92de2", + "username": "Prabhat Kasera", + "email": "Prabhat Kasera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1109, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f321be082fcc3049e901aa"]] + }, + { + "_id": "62f329bb082fcc3049e92de3", + "username": "Cole Lawrence", + "email": "Cole Lawrence@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 625, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90208"]] + }, + { + "_id": "62f329bb082fcc3049e92de4", + "username": "zdennis", + "email": "zdennis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 177, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e90209"]] + }, + { + "_id": "62f329bb082fcc3049e92de5", + "username": "Tim Rivoli", + "email": "Tim Rivoli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 210, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020c"]] + }, + { + "_id": "62f329bc082fcc3049e92de6", + "username": "SamGoody", + "email": "SamGoody@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12904, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020b"]] + }, + { + "_id": "62f329bc082fcc3049e92de7", + "username": "Chris Rosete", + "email": "Chris Rosete@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1240, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020e"]] + }, + { + "_id": "62f329bc082fcc3049e92de8", + "username": "JTeam", + "email": "JTeam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1377, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b8"]] + }, + { + "_id": "62f329bc082fcc3049e92de9", + "username": "Reza Fahmi", + "email": "Reza Fahmi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 270, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feba", "62f321be082fcc3049e9020d"]] + }, + { + "_id": "62f329bc082fcc3049e92deb", + "username": "tommyalvarez", + "email": "tommyalvarez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 595, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bc082fcc3049e92dec", + "username": "Daniel Barde", + "email": "Daniel Barde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2403, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febb", "62f321bf082fcc3049e902b6"]] + }, + { + "_id": "62f329bc082fcc3049e92ded", + "username": "jschrab", + "email": "jschrab@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11035, + "questionIds": ["62f321bb082fcc3049e8febb"], + "answerIds": [] + }, + { + "_id": "62f329bc082fcc3049e92def", + "username": "Egor Litvinchuk", + "email": "Egor Litvinchuk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1720, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bc082fcc3049e92df0", + "username": "Matthew Riches", + "email": "Matthew Riches@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2288, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037c"]] + }, + { + "_id": "62f329bc082fcc3049e92df1", + "username": "robocat", + "email": "robocat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037b"]] + }, + { + "_id": "62f329bc082fcc3049e92df2", + "username": "Kyros Koh", + "email": "Kyros Koh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 188, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037d"]] + }, + { + "_id": "62f329bd082fcc3049e92e08", + "username": "MaxPRafferty", + "email": "MaxPRafferty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4631, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e9037f"]] + }, + { + "_id": "62f329bd082fcc3049e92e09", + "username": "Ashish Yadav", + "email": "Ashish Yadav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13, + "questionIds": [], + "answerIds": [ + ["62f321bb082fcc3049e8febd", "62f321bf082fcc3049e90380"], + ["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e9081d"] + ] + }, + { + "_id": "62f329bd082fcc3049e92e0a", + "username": "Jason Cohen", + "email": "Jason Cohen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79067, + "questionIds": ["62f321bb082fcc3049e8febd"], + "answerIds": [] + }, + { + "_id": "62f329bd082fcc3049e92e0c", + "username": "Pablo Cabrera", + "email": "Pablo Cabrera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5654, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041c"]] + }, + { + "_id": "62f329bd082fcc3049e92e0d", + "username": "Fczbkk", + "email": "Fczbkk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1437, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041d"]] + }, + { + "_id": "62f329bd082fcc3049e92e0e", + "username": "Andrew Moore", + "email": "Andrew Moore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91142, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041f"]] + }, + { + "_id": "62f329bd082fcc3049e92e10", + "username": "treat your mods well", + "email": "treat your mods well@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2664, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec4", "62f321c0082fcc3049e9041e"]] + }, + { + "_id": "62f329bd082fcc3049e92e14", + "username": "2cBGj7vsfp", + "email": "2cBGj7vsfp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2737, + "questionIds": ["62f321bb082fcc3049e8fec4"], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e16", + "username": "Seamus", + "email": "Seamus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1187, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e19", + "username": "placeybordeaux", + "email": "placeybordeaux@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2008, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e1a", + "username": "Sergey Ilinsky", + "email": "Sergey Ilinsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30775, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904c9"]] + }, + { + "_id": "62f329be082fcc3049e92e1f", + "username": "Yashwin Munsadwala", + "email": "Yashwin Munsadwala@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 414, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e21", + "username": "rob_james", + "email": "rob_james@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1212, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904cb"]] + }, + { + "_id": "62f329be082fcc3049e92e23", + "username": "nalply", + "email": "nalply@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24344, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e24", + "username": "Prakash", + "email": "Prakash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6462, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fec6", "62f321c0082fcc3049e904ca"]] + }, + { + "_id": "62f329be082fcc3049e92e25", + "username": "Hexagon Theory", + "email": "Hexagon Theory@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41521, + "questionIds": ["62f321bb082fcc3049e8fec6"], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e26", + "username": "dougoftheabaci", + "email": "dougoftheabaci@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": ["62f321bb082fcc3049e8fece"], + "answerIds": [] + }, + { + "_id": "62f329be082fcc3049e92e29", + "username": "Cerbrus", + "email": "Cerbrus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 66594, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e2c", + "username": "Vinay Aggarwal", + "email": "Vinay Aggarwal@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1487, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90588"]] + }, + { + "_id": "62f329bf082fcc3049e92e2d", + "username": "Behnam Shomali", + "email": "Behnam Shomali@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 843, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90589"]] + }, + { + "_id": "62f329bf082fcc3049e92e2f", + "username": "Ege Özcan", + "email": "Ege Özcan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13441, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90586"]] + }, + { + "_id": "62f329bf082fcc3049e92e31", + "username": "Mike R", + "email": "Mike R@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 191, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e9058a"]] + }, + { + "_id": "62f329bf082fcc3049e92e33", + "username": "BLaZuRE", + "email": "BLaZuRE@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2335, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e35", + "username": "public override", + "email": "public override@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 954, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90651"]] + }, + { + "_id": "62f329bf082fcc3049e92e37", + "username": "Erdal G.", + "email": "Erdal G.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2312, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e39", + "username": "WoJ", + "email": "WoJ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23753, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e3b", + "username": "Lys", + "email": "Lys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 454, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e3c", + "username": "David Morrow", + "email": "David Morrow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8311, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feca", "62f321c1082fcc3049e90587"]] + }, + { + "_id": "62f329bf082fcc3049e92e3d", + "username": "Tyrone Slothrop", + "email": "Tyrone Slothrop@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 37801, + "questionIds": ["62f321bb082fcc3049e8feca"], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e3f", + "username": "Beni Cherniavsky-Paskin", + "email": "Beni Cherniavsky-Paskin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9006, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329bf082fcc3049e92e40", + "username": "Jeremy", + "email": "Jeremy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90652"]] + }, + { + "_id": "62f329c0082fcc3049e92e43", + "username": "Alex Che", + "email": "Alex Che@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fecd", "62f321c2082fcc3049e90650"]] + }, + { + "_id": "62f329c0082fcc3049e92e44", + "username": "ChaosPandion", + "email": "ChaosPandion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 75907, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d5"]] + }, + { + "_id": "62f329c0082fcc3049e92e46", + "username": "TheComputerGuy", + "email": "TheComputerGuy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075c"]] + }, + { + "_id": "62f329c0082fcc3049e92e47", + "username": "O_Z", + "email": "O_Z@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075f"]] + }, + { + "_id": "62f329c0082fcc3049e92e49", + "username": "Guang-De Lin", + "email": "Guang-De Lin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e9075e"]] + }, + { + "_id": "62f329c0082fcc3049e92e4b", + "username": "mcfedr", + "email": "mcfedr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7475, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c0082fcc3049e92e4c", + "username": "user113716", + "email": "user113716@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 311975, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed2", "62f321c3082fcc3049e906d4"]] + }, + { + "_id": "62f329c0082fcc3049e92e4d", + "username": "bitifet", + "email": "bitifet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3316, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90760"]] + }, + { + "_id": "62f329c0082fcc3049e92e4f", + "username": "Taulant", + "email": "Taulant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 175, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90762"]] + }, + { + "_id": "62f329c0082fcc3049e92e52", + "username": "melMass", + "email": "melMass@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2767, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed1", "62f321c3082fcc3049e90761"]] + }, + { + "_id": "62f329c0082fcc3049e92e54", + "username": "Hardik Mandankaa", + "email": "Hardik Mandankaa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2671, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c1082fcc3049e92e56", + "username": "swogger", + "email": "swogger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1000, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90820"]] + }, + { + "_id": "62f329c1082fcc3049e92e57", + "username": "B G Hari Prasad", + "email": "B G Hari Prasad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 158, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fed8", "62f321c4082fcc3049e90821"]] + }, + { + "_id": "62f329c1082fcc3049e92e58", + "username": "Alister", + "email": "Alister@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24982, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908eb"]] + }, + { + "_id": "62f329c1082fcc3049e92e59", + "username": "Joe Clinton", + "email": "Joe Clinton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 135, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908ec"]] + }, + { + "_id": "62f329c1082fcc3049e92e5a", + "username": "Karthik Ravichandran", + "email": "Karthik Ravichandran@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1105, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908ed"]] + }, + { + "_id": "62f329c1082fcc3049e92e5c", + "username": "Nilesh", + "email": "Nilesh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 197, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90957"]] + }, + { + "_id": "62f329c1082fcc3049e92e5d", + "username": "kvz", + "email": "kvz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4947, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedb", "62f321c5082fcc3049e908ee"]] + }, + { + "_id": "62f329c2082fcc3049e92e5e", + "username": "Manoj", + "email": "Manoj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4813, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095a"]] + }, + { + "_id": "62f329c2082fcc3049e92e5f", + "username": "Gautam Rai", + "email": "Gautam Rai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2335, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e9095c"]] + }, + { + "_id": "62f329c2082fcc3049e92e61", + "username": "Lafif Astahdziq", + "email": "Lafif Astahdziq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3642, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fedf", "62f321c5082fcc3049e90959"]] + }, + { + "_id": "62f329c2082fcc3049e92e62", + "username": "juan", + "email": "juan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 77850, + "questionIds": ["62f321bb082fcc3049e8fedf"], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e63", + "username": "herostwist", + "email": "herostwist@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3590, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a03"]] + }, + { + "_id": "62f329c2082fcc3049e92e64", + "username": "tcooc", + "email": "tcooc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19909, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a02"]] + }, + { + "_id": "62f329c2082fcc3049e92e66", + "username": "Bryan Downing", + "email": "Bryan Downing@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14756, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e69", + "username": "Sharky", + "email": "Sharky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5956, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e6b", + "username": "rmcsharry", + "email": "rmcsharry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4948, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e6d", + "username": "Lucio Crusca", + "email": "Lucio Crusca@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1209, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e6e", + "username": "guya", + "email": "guya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4783, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a04"]] + }, + { + "_id": "62f329c2082fcc3049e92e70", + "username": "shaosh", + "email": "shaosh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 639, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c2082fcc3049e92e73", + "username": "krupar", + "email": "krupar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a06"]] + }, + { + "_id": "62f329c3082fcc3049e92e74", + "username": "Asif Ashraf", + "email": "Asif Ashraf@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 635, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90abb"]] + }, + { + "_id": "62f329c3082fcc3049e92e75", + "username": "DavidWainwright", + "email": "DavidWainwright@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2777, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee3", "62f321c7082fcc3049e90a05"]] + }, + { + "_id": "62f329c3082fcc3049e92e76", + "username": "Narayan Yerrabachu", + "email": "Narayan Yerrabachu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1596, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90abe"]] + }, + { + "_id": "62f329c3082fcc3049e92e77", + "username": "Adam Fischer", + "email": "Adam Fischer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1046, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90ac0"]] + }, + { + "_id": "62f329c3082fcc3049e92e78", + "username": "filip", + "email": "filip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3323, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b85"]] + }, + { + "_id": "62f329c3082fcc3049e92e7a", + "username": "Cyril Duchon-Doris", + "email": "Cyril Duchon-Doris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11999, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c3082fcc3049e92e7c", + "username": "JohanTG", + "email": "JohanTG@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1326, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c3082fcc3049e92e7d", + "username": "Maksym Kozlenko", + "email": "Maksym Kozlenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9971, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fee9", "62f321c7082fcc3049e90abd"]] + }, + { + "_id": "62f329c4082fcc3049e92e7f", + "username": "Shlomi Hassid", + "email": "Shlomi Hassid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6398, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b86"]] + }, + { + "_id": "62f329c4082fcc3049e92e80", + "username": "krisrak", + "email": "krisrak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12762, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feea", "62f321c8082fcc3049e90b88"]] + }, + { + "_id": "62f329c4082fcc3049e92e81", + "username": "Subliminal Hash", + "email": "Subliminal Hash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13477, + "questionIds": ["62f321bb082fcc3049e8feea"], + "answerIds": [] + }, + { + "_id": "62f329c4082fcc3049e92e83", + "username": "IceCreamYou", + "email": "IceCreamYou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1842, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bea"]] + }, + { + "_id": "62f329c4082fcc3049e92e84", + "username": "Omar Alshaker", + "email": "Omar Alshaker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 859, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90be8"]] + }, + { + "_id": "62f329c4082fcc3049e92e85", + "username": "SPARTAN", + "email": "SPARTAN@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c98"]] + }, + { + "_id": "62f329c4082fcc3049e92e86", + "username": "rap-2-h", + "email": "rap-2-h@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27327, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c97"]] + }, + { + "_id": "62f329c4082fcc3049e92e88", + "username": "Jonathan Kuhl", + "email": "Jonathan Kuhl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 699, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90bed"]] + }, + { + "_id": "62f329c4082fcc3049e92e8a", + "username": "muthukumar selvaraj", + "email": "muthukumar selvaraj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 867, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c4082fcc3049e92e8c", + "username": "Ramin Bateni", + "email": "Ramin Bateni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15523, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feed", "62f321c8082fcc3049e90beb"]] + }, + { + "_id": "62f329c4082fcc3049e92e8e", + "username": "Forivin", + "email": "Forivin@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13634, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c4082fcc3049e92e91", + "username": "Clearer", + "email": "Clearer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2056, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c4082fcc3049e92e93", + "username": "Dimpu Aravind Buddha", + "email": "Dimpu Aravind Buddha@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9007, + "questionIds": ["62f321bb082fcc3049e8feed"], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92e96", + "username": "kevingilbert100", + "email": "kevingilbert100@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1273, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92e98", + "username": "Eurig Jones", + "email": "Eurig Jones@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92e9a", + "username": "Kip", + "email": "Kip@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 104056, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92e9d", + "username": "rampion", + "email": "rampion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 84938, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ea0", + "username": "B T", + "email": "B T@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53335, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ea2", + "username": "GalacticCowboy", + "email": "GalacticCowboy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11535, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ea4", + "username": "ericmjl", + "email": "ericmjl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12293, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ea7", + "username": "Shiraz", + "email": "Shiraz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1746, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ea9", + "username": "Iker Jimenez", + "email": "Iker Jimenez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6915, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eab", + "username": "Mxyk", + "email": "Mxyk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10570, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ead", + "username": "Bengt", + "email": "Bengt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13451, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eaf", + "username": "lony", + "email": "lony@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6045, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eb2", + "username": "Ben Clayton", + "email": "Ben Clayton@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79148, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eb4", + "username": "Peter Wippermann", + "email": "Peter Wippermann@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3869, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eb6", + "username": "markj", + "email": "markj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 127, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eb8", + "username": "jkeys", + "email": "jkeys@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3600, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eba", + "username": "dokaspar", + "email": "dokaspar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ebd", + "username": "Frank Nocke", + "email": "Frank Nocke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9028, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ebf", + "username": "Martin Beckett", + "email": "Martin Beckett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 93155, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ec2", + "username": "Matej", + "email": "Matej@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 548, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ec4", + "username": "drogon", + "email": "drogon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1765, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ec6", + "username": "Kurt Zhong", + "email": "Kurt Zhong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7148, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ec8", + "username": "jlembke", + "email": "jlembke@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12854, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eca", + "username": "user168715", + "email": "user168715@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5371, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ecc", + "username": "Cameron MacFarland", + "email": "Cameron MacFarland@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 68747, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ece", + "username": "Dustin Graham", + "email": "Dustin Graham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2024, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ed1", + "username": "user959690", + "email": "user959690@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 584, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ed4", + "username": "biphobe", + "email": "biphobe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4052, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ed6", + "username": "Richard Collette", + "email": "Richard Collette@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5140, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ed8", + "username": "jfreak53", + "email": "jfreak53@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2159, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eda", + "username": "John MacIntyre", + "email": "John MacIntyre@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12848, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92edc", + "username": "Kokodoko", + "email": "Kokodoko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23542, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ede", + "username": "Tien Do", + "email": "Tien Do@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9363, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ee0", + "username": "TheTXI", + "email": "TheTXI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36981, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ee2", + "username": "Shawn Mclean", + "email": "Shawn Mclean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 55705, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ee4", + "username": "mvndaai", + "email": "mvndaai@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3097, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ee6", + "username": "Berislav Lopac", + "email": "Berislav Lopac@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15729, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ee8", + "username": "Georg Schölly", + "email": "Georg Schölly@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 121362, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93202"]] + }, + { + "_id": "62f329c5082fcc3049e92eea", + "username": "Eduardo", + "email": "Eduardo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4192, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eec", + "username": "Guillermo Gutiérrez", + "email": "Guillermo Gutiérrez@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16644, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92eee", + "username": "hughes", + "email": "hughes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5255, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ef0", + "username": "unbeknown", + "email": "unbeknown@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ef2", + "username": "Tomer Ben David", + "email": "Tomer Ben David@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7507, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c9a"]] + }, + { + "_id": "62f329c5082fcc3049e92ef3", + "username": "124", + "email": "124@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2679, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d5c"]] + }, + { + "_id": "62f329c5082fcc3049e92ef4", + "username": "Abdus Salam Azad", + "email": "Abdus Salam Azad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4414, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef6", "62f321ca082fcc3049e90c9b"]] + }, + { + "_id": "62f329c5082fcc3049e92ef5", + "username": "omg", + "email": "omg@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 131574, + "questionIds": ["62f321bb082fcc3049e8fef6"], + "answerIds": [] + }, + { + "_id": "62f329c5082fcc3049e92ef6", + "username": "Nikul", + "email": "Nikul@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1015, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d60"]] + }, + { + "_id": "62f329c5082fcc3049e92ef7", + "username": "Mohammed Shaheen MK", + "email": "Mohammed Shaheen MK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d61"]] + }, + { + "_id": "62f329c6082fcc3049e92ef8", + "username": "Mhandzkie", + "email": "Mhandzkie@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 209, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d62"]] + }, + { + "_id": "62f329c6082fcc3049e92ef9", + "username": "Sandeep Shekhawat", + "email": "Sandeep Shekhawat@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 657, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d65"]] + }, + { + "_id": "62f329c6082fcc3049e92efa", + "username": "Mohammad Dayyan", + "email": "Mohammad Dayyan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20336, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fef8", "62f321ca082fcc3049e90d64"]] + }, + { + "_id": "62f329c6082fcc3049e92efb", + "username": "haddar", + "email": "haddar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24837, + "questionIds": ["62f321bb082fcc3049e8fef8"], + "answerIds": [] + }, + { + "_id": "62f329c6082fcc3049e92efc", + "username": "Nadir Abbas", + "email": "Nadir Abbas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1035, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e26"]] + }, + { + "_id": "62f329c6082fcc3049e92efe", + "username": "anarchist", + "email": "anarchist@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e2a"]] + }, + { + "_id": "62f329c6082fcc3049e92eff", + "username": "Shubham Gupta", + "email": "Shubham Gupta@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 199, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e2b"]] + }, + { + "_id": "62f329c6082fcc3049e92f02", + "username": "Robo Robok", + "email": "Robo Robok@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18553, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e27"]] + }, + { + "_id": "62f32a05082fcc3049e92f06", + "username": "ivarni", + "email": "ivarni@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17318, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f07", + "username": "Sven N", + "email": "Sven N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92adf"]] + }, + { + "_id": "62f32a05082fcc3049e92f09", + "username": "xiao 啸", + "email": "xiao 啸@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6140, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f0b", + "username": "user437231", + "email": "user437231@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae1"]] + }, + { + "_id": "62f32a05082fcc3049e92f0d", + "username": "BrunoLM", + "email": "BrunoLM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 94666, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f0f", + "username": "guilin 桂林", + "email": "guilin 桂林@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16618, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f11", + "username": "Pauan", + "email": "Pauan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2406, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f12", + "username": "Zippo", + "email": "Zippo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14910, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae0"]] + }, + { + "_id": "62f32a05082fcc3049e92f13", + "username": "jsxt", + "email": "jsxt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1077, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae2"]] + }, + { + "_id": "62f32a05082fcc3049e92f15", + "username": "AndiDog", + "email": "AndiDog@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 66362, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f17", + "username": "Artem Balianytsia", + "email": "Artem Balianytsia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f18", + "username": "Filipiz", + "email": "Filipiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1031, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae3"]] + }, + { + "_id": "62f32a05082fcc3049e92f1a", + "username": "Dongdong Kong", + "email": "Dongdong Kong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 320, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f1b", + "username": "bart", + "email": "bart@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7494, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae5"]] + }, + { + "_id": "62f32a05082fcc3049e92f1c", + "username": "Spudley", + "email": "Spudley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 162533, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae6"]] + }, + { + "_id": "62f32a05082fcc3049e92f1e", + "username": "fserb", + "email": "fserb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3807, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f20", + "username": "Daan Mortier", + "email": "Daan Mortier@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1838, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f23", + "username": "user2233706", + "email": "user2233706@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4911, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f25", + "username": "Boris", + "email": "Boris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 159, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f27", + "username": "fearphage", + "email": "fearphage@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16650, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae4"]] + }, + { + "_id": "62f32a05082fcc3049e92f28", + "username": "Mehmet Aydemir", + "email": "Mehmet Aydemir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae7"]] + }, + { + "_id": "62f32a05082fcc3049e92f29", + "username": "Craig Stuntz", + "email": "Craig Stuntz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 125009, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae8"]] + }, + { + "_id": "62f32a05082fcc3049e92f2a", + "username": "lior hakim", + "email": "lior hakim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 114, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aea"]] + }, + { + "_id": "62f32a05082fcc3049e92f2c", + "username": "rmobis", + "email": "rmobis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24691, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f2e", + "username": "rescdsk", + "email": "rescdsk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8540, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92ae9"]] + }, + { + "_id": "62f32a05082fcc3049e92f30", + "username": "Peter Jaric", + "email": "Peter Jaric@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5001, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a05082fcc3049e92f32", + "username": "moechofe", + "email": "moechofe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aec"]] + }, + { + "_id": "62f32a05082fcc3049e92f33", + "username": "Rtlprmft", + "email": "Rtlprmft@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 19, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aef"]] + }, + { + "_id": "62f32a05082fcc3049e92f34", + "username": "Raymond Powell", + "email": "Raymond Powell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 39, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af1"]] + }, + { + "_id": "62f32a05082fcc3049e92f35", + "username": "krichard", + "email": "krichard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3609, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af2"]] + }, + { + "_id": "62f32a06082fcc3049e92f37", + "username": "Tengiz", + "email": "Tengiz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7556, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af4"]] + }, + { + "_id": "62f32a06082fcc3049e92f38", + "username": "user2240578", + "email": "user2240578@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 67, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af5"]] + }, + { + "_id": "62f32a06082fcc3049e92f39", + "username": "eces", + "email": "eces@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af6"]] + }, + { + "_id": "62f32a06082fcc3049e92f3a", + "username": "qbolec", + "email": "qbolec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5116, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af7"]] + }, + { + "_id": "62f32a06082fcc3049e92f3b", + "username": "redestructa", + "email": "redestructa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1163, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af9"]] + }, + { + "_id": "62f32a06082fcc3049e92f3e", + "username": "James Manning", + "email": "James Manning@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13299, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f40", + "username": "RainChen", + "email": "RainChen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 501, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f42", + "username": "Dinei", + "email": "Dinei@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3558, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f44", + "username": "toddmo", + "email": "toddmo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18769, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f46", + "username": "sam hocevar", + "email": "sam hocevar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11607, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f48", + "username": "mwardm", + "email": "mwardm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1913, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f4b", + "username": "Sarath S Menon", + "email": "Sarath S Menon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1850, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a06082fcc3049e92f4c", + "username": "Gabriel Nahmias", + "email": "Gabriel Nahmias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 850, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92af8"]] + }, + { + "_id": "62f32a07082fcc3049e92f4e", + "username": "rawiro", + "email": "rawiro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 226, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f4f", + "username": "Thiago Mata", + "email": "Thiago Mata@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2623, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92afb"]] + }, + { + "_id": "62f32a07082fcc3049e92f51", + "username": "Max Krohn", + "email": "Max Krohn@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 509, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f53", + "username": "FGM", + "email": "FGM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2780, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f55", + "username": "George Eracleous", + "email": "George Eracleous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4099, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92afa"]] + }, + { + "_id": "62f32a07082fcc3049e92f56", + "username": "Bovard", + "email": "Bovard@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1145, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92afc"]] + }, + { + "_id": "62f32a07082fcc3049e92f58", + "username": "Dragas", + "email": "Dragas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 913, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f5a", + "username": "Rob Audenaerde", + "email": "Rob Audenaerde@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18149, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92afd"]] + }, + { + "_id": "62f32a07082fcc3049e92f5b", + "username": "Afshin Mehrabani", + "email": "Afshin Mehrabani@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92afe"]] + }, + { + "_id": "62f32a07082fcc3049e92f5d", + "username": "dtasev", + "email": "dtasev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 461, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f5e", + "username": "Monarch Wadia", + "email": "Monarch Wadia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3772, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92aff"]] + }, + { + "_id": "62f32a07082fcc3049e92f60", + "username": "user993954", + "email": "user993954@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 393, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a07082fcc3049e92f61", + "username": "Kim", + "email": "Kim@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b00"]] + }, + { + "_id": "62f32a07082fcc3049e92f62", + "username": "Evgeny Gerbut", + "email": "Evgeny Gerbut@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 350, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b01"]] + }, + { + "_id": "62f32a07082fcc3049e92f63", + "username": "AnyWhichWay", + "email": "AnyWhichWay@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 606, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b03"]] + }, + { + "_id": "62f32a07082fcc3049e92f64", + "username": "NISHANK KUMAR", + "email": "NISHANK KUMAR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 428, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b05"]] + }, + { + "_id": "62f32a08082fcc3049e92f65", + "username": "Mahendra Hirapra", + "email": "Mahendra Hirapra@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 341, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b07"]] + }, + { + "_id": "62f32a08082fcc3049e92f66", + "username": "TheMisir", + "email": "TheMisir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3724, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b08"]] + }, + { + "_id": "62f32a08082fcc3049e92f68", + "username": "David Schmitt", + "email": "David Schmitt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 56903, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a08082fcc3049e92f69", + "username": "Lucas Breitembach", + "email": "Lucas Breitembach@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1156, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b09"]] + }, + { + "_id": "62f32a08082fcc3049e92f6a", + "username": "user7090116", + "email": "user7090116@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0a"]] + }, + { + "_id": "62f32a08082fcc3049e92f6b", + "username": "GHosT", + "email": "GHosT@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0c"]] + }, + { + "_id": "62f32a08082fcc3049e92f6c", + "username": "Nero", + "email": "Nero@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1465, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0b"]] + }, + { + "_id": "62f32a08082fcc3049e92f6e", + "username": "Param Siddharth", + "email": "Param Siddharth@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 713, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a08082fcc3049e92f6f", + "username": "ßiansor Å. Ålmerol", + "email": "ßiansor Å. Ålmerol@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2464, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0e"]] + }, + { + "_id": "62f32a08082fcc3049e92f70", + "username": "Alex Povolotsky", + "email": "Alex Povolotsky@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 326, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0d"]] + }, + { + "_id": "62f32a08082fcc3049e92f72", + "username": "Amila Senadheera", + "email": "Amila Senadheera@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9034, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a08082fcc3049e92f74", + "username": "Prid", + "email": "Prid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 768, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a08082fcc3049e92f75", + "username": "Sean Morris", + "email": "Sean Morris@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 284, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b0f"]] + }, + { + "_id": "62f32a09082fcc3049e92f76", + "username": "Mkoes", + "email": "Mkoes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 70, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b12"]] + }, + { + "_id": "62f32a09082fcc3049e92f77", + "username": "Jens Törnell", + "email": "Jens Törnell@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21142, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92b11"]] + }, + { + "_id": "62f32a09082fcc3049e92f79", + "username": "Ebubekir Dirican", + "email": "Ebubekir Dirican@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 386, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f2f"]] + }, + { + "_id": "62f32a09082fcc3049e92f7a", + "username": "DanielEli", + "email": "DanielEli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3323, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f31"]] + }, + { + "_id": "62f32a09082fcc3049e92f7b", + "username": "abhisekp", + "email": "abhisekp@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3262, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91021"]] + }, + { + "_id": "62f32a09082fcc3049e92f7d", + "username": "Rich Bradshaw", + "email": "Rich Bradshaw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69983, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a09082fcc3049e92f80", + "username": "XML", + "email": "XML@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 18868, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff04", "62f321cd082fcc3049e90f30"]] + }, + { + "_id": "62f32a0a082fcc3049e92f81", + "username": "ovidb", + "email": "ovidb@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1626, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91023"]] + }, + { + "_id": "62f32a0a082fcc3049e92f82", + "username": "Gordon", + "email": "Gordon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 69, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91025"]] + }, + { + "_id": "62f32a0a082fcc3049e92f84", + "username": "Bruno Jennrich", + "email": "Bruno Jennrich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 348, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff09", "62f3220c082fcc3049e91024"]] + }, + { + "_id": "62f32a0b082fcc3049e92f85", + "username": "Samuel Kiroko N", + "email": "Samuel Kiroko N@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 41, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df3"]] + }, + { + "_id": "62f32a0b082fcc3049e92f87", + "username": "Manuel del Pozo", + "email": "Manuel del Pozo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df4"]] + }, + { + "_id": "62f32a0b082fcc3049e92f88", + "username": "Teymur", + "email": "Teymur@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 98, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df5"]] + }, + { + "_id": "62f32a0b082fcc3049e92f8a", + "username": "Sai Ranjit", + "email": "Sai Ranjit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0b082fcc3049e92f8b", + "username": "Faisal Nadeem", + "email": "Faisal Nadeem@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df6"]] + }, + { + "_id": "62f32a0b082fcc3049e92f8c", + "username": "Sreehari Ballampalli", + "email": "Sreehari Ballampalli@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df8"]] + }, + { + "_id": "62f32a0b082fcc3049e92f90", + "username": "German", + "email": "German@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1044, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df7"]] + }, + { + "_id": "62f32a0b082fcc3049e92fb2", + "username": "Devendra Kumbhkar", + "email": "Devendra Kumbhkar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 234, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92df9"]] + }, + { + "_id": "62f32a0b082fcc3049e92fb3", + "username": "Ahmad Moghazi", + "email": "Ahmad Moghazi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1053, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dfa"]] + }, + { + "_id": "62f32a0b082fcc3049e92fb4", + "username": "H.Mustafa", + "email": "H.Mustafa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 339, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dfb"]] + }, + { + "_id": "62f32a0b082fcc3049e92fb5", + "username": "Konstantin XFlash Stratigenas", + "email": "Konstantin XFlash Stratigenas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 554, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dfd"]] + }, + { + "_id": "62f32a0b082fcc3049e92fb8", + "username": "Saad Zahoor", + "email": "Saad Zahoor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 80, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0b082fcc3049e92fb9", + "username": "Cava", + "email": "Cava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4914, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dfc"]] + }, + { + "_id": "62f32a0b082fcc3049e92fba", + "username": "Mohcine BAADI", + "email": "Mohcine BAADI@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dfe"]] + }, + { + "_id": "62f32a0b082fcc3049e92fbc", + "username": "Amir", + "email": "Amir@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 79, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92dff"]] + }, + { + "_id": "62f32a0b082fcc3049e92fbe", + "username": "davidchoo12", + "email": "davidchoo12@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1239, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e00"]] + }, + { + "_id": "62f32a0b082fcc3049e92fc0", + "username": "user1075296", + "email": "user1075296@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 189, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e01"]] + }, + { + "_id": "62f32a0b082fcc3049e92fc1", + "username": "Mr.Sabin ", + "email": "Mr.Sabin @fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e02"]] + }, + { + "_id": "62f32a0c082fcc3049e92fc7", + "username": "Arsonik", + "email": "Arsonik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2237, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e03"]] + }, + { + "_id": "62f32a0c082fcc3049e92fc8", + "username": "Dawood F.M Kaundama", + "email": "Dawood F.M Kaundama@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 101, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e04"]] + }, + { + "_id": "62f32a0c082fcc3049e92fc9", + "username": "Manbir Judge", + "email": "Manbir Judge@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 63, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e06"]] + }, + { + "_id": "62f32a0c082fcc3049e92fca", + "username": "Maher Aldous", + "email": "Maher Aldous@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 776, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8febe", "62f329bc082fcc3049e92e07"]] + }, + { + "_id": "62f32a0d082fcc3049e92fcb", + "username": "areg_noid", + "email": "areg_noid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 71, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e2e"]] + }, + { + "_id": "62f32a0d082fcc3049e92fcc", + "username": "The Lazy Dev Otaku", + "email": "The Lazy Dev Otaku@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 129, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e2d"]] + }, + { + "_id": "62f32a0d082fcc3049e92fce", + "username": "Ice_mank", + "email": "Ice_mank@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 376, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefb", "62f321cb082fcc3049e90e2c"]] + }, + { + "_id": "62f32a0d082fcc3049e92fd1", + "username": "rorypicko", + "email": "rorypicko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4573, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd4", + "username": "Izzy", + "email": "Izzy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 402, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd6", + "username": "Bernardo Dal Corno", + "email": "Bernardo Dal Corno@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1507, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd7", + "username": "Phill Pafford", + "email": "Phill Pafford@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 81411, + "questionIds": ["62f321bb082fcc3049e8fefc"], + "answerIds": [] + }, + { + "_id": "62f32a0d082fcc3049e92fd8", + "username": "mattias", + "email": "mattias@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1098, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e89"]] + }, + { + "_id": "62f32a0d082fcc3049e92fd9", + "username": "samin ", + "email": "samin @fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 363, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8fefd", "62f321cc082fcc3049e90e87"]] + }, + { + "_id": "62f32a0d082fcc3049e92fda", + "username": "zacharyliu", + "email": "zacharyliu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 24364, + "questionIds": ["62f321bb082fcc3049e8fefd"], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fdc", + "username": "heltonbiker", + "email": "heltonbiker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25429, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fde", + "username": "Evan Carroll", + "email": "Evan Carroll@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 72833, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe1", + "username": "Octavia Togami", + "email": "Octavia Togami@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3976, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe3", + "username": "Dude Pascalou", + "email": "Dude Pascalou@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2800, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe5", + "username": "HugoPoi", + "email": "HugoPoi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 359, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a0e082fcc3049e92fe7", + "username": "hrzafer", + "email": "hrzafer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1092, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92fe9", + "username": "Manu Masson", + "email": "Manu Masson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1457, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92feb", + "username": "youri", + "email": "youri@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3466, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92fed", + "username": "Dmitry Zaytsev", + "email": "Dmitry Zaytsev@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23615, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92fef", + "username": "Bill K", + "email": "Bill K@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61353, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ff1", + "username": "Daniel Kmak", + "email": "Daniel Kmak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17504, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ff3", + "username": "erikkallen", + "email": "erikkallen@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32954, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ff5", + "username": "Jens Kohl", + "email": "Jens Kohl@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5821, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ff9", + "username": "John Kraft", + "email": "John Kraft@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6691, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ffb", + "username": "MSalters", + "email": "MSalters@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 168928, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92ffd", + "username": "Lenar Hoyt", + "email": "Lenar Hoyt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5762, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e92fff", + "username": "Amalgovinus", + "email": "Amalgovinus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4005, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93003", + "username": "Jared Nedzel", + "email": "Jared Nedzel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 769, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93006", + "username": "ceklock", + "email": "ceklock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5921, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93008", + "username": "dfsq", + "email": "dfsq@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 188385, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9300a", + "username": "Blender", + "email": "Blender@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 277776, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9300c", + "username": "rybo111", + "email": "rybo111@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11872, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9300e", + "username": "user9993", + "email": "user9993@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5395, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93010", + "username": "Rag", + "email": "Rag@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6201, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93012", + "username": "LarryBud", + "email": "LarryBud@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 922, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93014", + "username": "keks", + "email": "keks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1042, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93017", + "username": "Joachim Sauer", + "email": "Joachim Sauer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 293345, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93019", + "username": "Rok Strniša", + "email": "Rok Strniša@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6478, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9301b", + "username": "Ram Rachum", + "email": "Ram Rachum@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 78926, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9301e", + "username": "JohannesH", + "email": "JohannesH@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6330, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93021", + "username": "user1303718", + "email": "user1303718@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93023", + "username": "jimt", + "email": "jimt@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23787, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93025", + "username": "SLaks", + "email": "SLaks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 842963, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93027", + "username": "Jeremy Holovacs", + "email": "Jeremy Holovacs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21445, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93029", + "username": "Robin J", + "email": "Robin J@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 156, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9302b", + "username": "Joao Carlos", + "email": "Joao Carlos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 719, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9302e", + "username": "demented hedgehog", + "email": "demented hedgehog@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6481, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93030", + "username": "Surya", + "email": "Surya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4880, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93032", + "username": "dragonroot", + "email": "dragonroot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5424, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93034", + "username": "TheSoftwareJedi", + "email": "TheSoftwareJedi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 33689, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93036", + "username": "rfunduk", + "email": "rfunduk@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 29575, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93038", + "username": "Holger", + "email": "Holger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 269964, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9303b", + "username": "Justin L.", + "email": "Justin L.@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3811, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9303e", + "username": "damian", + "email": "damian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3594, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93041", + "username": "UpTheCreek", + "email": "UpTheCreek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30450, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93044", + "username": "botbot", + "email": "botbot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7089, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93046", + "username": "Mars Robertson", + "email": "Mars Robertson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12083, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e93048", + "username": "Strawberry", + "email": "Strawberry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 62968, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9304a", + "username": "Jean-François Corbett", + "email": "Jean-François Corbett@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36250, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a12082fcc3049e9304c", + "username": "maskacovnik", + "email": "maskacovnik@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3039, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4a082fcc3049e9304e", + "username": "Dishant Chanchad", + "email": "Dishant Chanchad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 580, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f94"]] + }, + { + "_id": "62f32a4a082fcc3049e93050", + "username": "Asker", + "email": "Asker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1088, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f96"]] + }, + { + "_id": "62f32a4a082fcc3049e93053", + "username": "Victor Gavro", + "email": "Victor Gavro@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1277, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4a082fcc3049e93054", + "username": "oriadam", + "email": "oriadam@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6923, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f95"]] + }, + { + "_id": "62f32a4a082fcc3049e93056", + "username": "Larphoid", + "email": "Larphoid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1006, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4a082fcc3049e93057", + "username": "shweta ghanate", + "email": "shweta ghanate@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 271, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f98"]] + }, + { + "_id": "62f32a4b082fcc3049e93058", + "username": "Julio Vinachi", + "email": "Julio Vinachi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f99"]] + }, + { + "_id": "62f32a4b082fcc3049e93059", + "username": "jnbm", + "email": "jnbm@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 118, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9a"]] + }, + { + "_id": "62f32a4b082fcc3049e9305a", + "username": "k26dr", + "email": "k26dr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1099, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9c"]] + }, + { + "_id": "62f32a4b082fcc3049e9305b", + "username": "user17401373", + "email": "user17401373@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9d"]] + }, + { + "_id": "62f32a4b082fcc3049e9305c", + "username": "Erçin Dedeoğlu", + "email": "Erçin Dedeoğlu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4420, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa0"]] + }, + { + "_id": "62f32a4c082fcc3049e9305d", + "username": "shekhar chander", + "email": "shekhar chander@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 490, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa2"]] + }, + { + "_id": "62f32a4c082fcc3049e9305e", + "username": "Ali Raza", + "email": "Ali Raza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 743, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa4"]] + }, + { + "_id": "62f32a4c082fcc3049e9305f", + "username": "Hangover Sound Sound", + "email": "Hangover Sound Sound@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 184, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa5"]] + }, + { + "_id": "62f32a4c082fcc3049e93060", + "username": "guogangj", + "email": "guogangj@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2165, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92f9f"]] + }, + { + "_id": "62f32a4c082fcc3049e93062", + "username": "radrow", + "email": "radrow@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5562, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4c082fcc3049e93063", + "username": "Mohammed Shabeer k", + "email": "Mohammed Shabeer k@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa6"]] + }, + { + "_id": "62f32a4c082fcc3049e93064", + "username": "Aayush Bhattacharya", + "email": "Aayush Bhattacharya@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1168, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa7"]] + }, + { + "_id": "62f32a4c082fcc3049e93065", + "username": "amir yeganeh", + "email": "amir yeganeh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 477, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fa9"]] + }, + { + "_id": "62f32a4c082fcc3049e93067", + "username": "JaeIL Ryu", + "email": "JaeIL Ryu@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 124, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4c082fcc3049e93069", + "username": "Artur Müller Romanov", + "email": "Artur Müller Romanov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2330, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fab"]] + }, + { + "_id": "62f32a4c082fcc3049e9306a", + "username": "Jose Pedro Febian", + "email": "Jose Pedro Febian@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 177, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fac"]] + }, + { + "_id": "62f32a4d082fcc3049e9306b", + "username": "user6689187", + "email": "user6689187@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 61, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fae"]] + }, + { + "_id": "62f32a4d082fcc3049e9306c", + "username": "Navin Singh rangar", + "email": "Navin Singh rangar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 99, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fad"]] + }, + { + "_id": "62f32a4d082fcc3049e9306d", + "username": "Himanshu Jangid", + "email": "Himanshu Jangid@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 139, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92faf"]] + }, + { + "_id": "62f32a4d082fcc3049e9306e", + "username": "Shavleg Kakulia", + "email": "Shavleg Kakulia@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fb0"]] + }, + { + "_id": "62f32a4d082fcc3049e9306f", + "username": "Aslam khan", + "email": "Aslam khan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 146, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feac", "62f32a0b082fcc3049e92fb1"]] + }, + { + "_id": "62f32a4d082fcc3049e93070", + "username": "Pooria", + "email": "Pooria@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 780, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8ff05", "62f32924082fcc3049e92adc"]] + }, + { + "_id": "62f32a4e082fcc3049e93073", + "username": "Bhushan Kawadkar", + "email": "Bhushan Kawadkar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27961, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a4e082fcc3049e93074", + "username": "Yauheni Prakapenka", + "email": "Yauheni Prakapenka@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 481, + "questionIds": [], + "answerIds": [["62f321bb082fcc3049e8feb9", "62f32a0b082fcc3049e92f8e"]] + }, + { + "_id": "62f32a4e082fcc3049e93075", + "username": "pix0r", + "email": "pix0r@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 30951, + "questionIds": ["62f321bb082fcc3049e8feb9"], + "answerIds": [] + }, + { + "_id": "62f32a4e082fcc3049e93076", + "username": "Walker", + "email": "Walker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 119777, + "questionIds": ["62f321bb082fcc3049e8feac"], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93079", + "username": "Dapeng", + "email": "Dapeng@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1674, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9307b", + "username": "Preston", + "email": "Preston@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1251, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9307d", + "username": "weddingcakes", + "email": "weddingcakes@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 643, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93080", + "username": "JeremyP", + "email": "JeremyP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 82132, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93083", + "username": "JohnnyM", + "email": "JohnnyM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1223, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93085", + "username": "wormsparty", + "email": "wormsparty@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2453, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93087", + "username": "dinigo", + "email": "dinigo@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6112, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93089", + "username": "Daniel Earwicker", + "email": "Daniel Earwicker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 112445, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9308b", + "username": "tasmaniski", + "email": "tasmaniski@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4667, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9308d", + "username": "sscirrus", + "email": "sscirrus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53781, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9308f", + "username": "Maroun", + "email": "Maroun@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 91672, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93091", + "username": "PL_kolek", + "email": "PL_kolek@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 315, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93093", + "username": "Tomáš Zato - Reinstate Monica", + "email": "Tomáš Zato - Reinstate Monica@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46644, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93095", + "username": "slartibartfast", + "email": "slartibartfast@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 522, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93097", + "username": "huysentruitw", + "email": "huysentruitw@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26367, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e93099", + "username": "Dean", + "email": "Dean@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8204, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9309b", + "username": "Vanja", + "email": "Vanja@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4145, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9309d", + "username": "Lloyd Moore", + "email": "Lloyd Moore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3079, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e9309f", + "username": "jjujuma", + "email": "jjujuma@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21175, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930a1", + "username": "Lucretiel", + "email": "Lucretiel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2778, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930a3", + "username": "MAK", + "email": "MAK@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 25430, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930a5", + "username": "rickcnagy", + "email": "rickcnagy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1744, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930a7", + "username": "Andy Ross", + "email": "Andy Ross@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11299, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930a9", + "username": "Ziggy", + "email": "Ziggy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21275, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930ab", + "username": "intrepidis", + "email": "intrepidis@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2791, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930ae", + "username": "Vishnu Haridas", + "email": "Vishnu Haridas@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7237, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930b0", + "username": "Jordan Parmer", + "email": "Jordan Parmer@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 34770, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930b2", + "username": "lensovet", + "email": "lensovet@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5149, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930b4", + "username": "rubdottocom", + "email": "rubdottocom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7930, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930b7", + "username": "AndaP", + "email": "AndaP@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1258, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930b9", + "username": "ChrisW", + "email": "ChrisW@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 53706, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930bb", + "username": "volley", + "email": "volley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6585, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930bd", + "username": "Frank Schwieterman", + "email": "Frank Schwieterman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23855, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930bf", + "username": "Hoang Pham", + "email": "Hoang Pham@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6849, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930c1", + "username": "WaelJ", + "email": "WaelJ@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2882, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930c3", + "username": "Manav", + "email": "Manav@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9716, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930c5", + "username": "Milovan Zogovic", + "email": "Milovan Zogovic@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1491, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930c7", + "username": "Tim Sullivan", + "email": "Tim Sullivan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 16660, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930c9", + "username": "Tim MB", + "email": "Tim MB@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4245, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930cb", + "username": "batman", + "email": "batman@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4724, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930cd", + "username": "林果皞", + "email": "林果皞@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7141, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930cf", + "username": "karmakaze", + "email": "karmakaze@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 32771, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930d1", + "username": "Mongus Pong", + "email": "Mongus Pong@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11077, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930d3", + "username": "GAgnew", + "email": "GAgnew@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3641, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930d6", + "username": "Run", + "email": "Run@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 51834, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930d8", + "username": "theman_on_vista", + "email": "theman_on_vista@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930da", + "username": "isabelle martz", + "email": "isabelle martz@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 171, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930dc", + "username": "JuanZe", + "email": "JuanZe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7823, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930de", + "username": "lurks", + "email": "lurks@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2506, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930e0", + "username": "Dkyc", + "email": "Dkyc@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 89, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930e3", + "username": "Jon Kruger", + "email": "Jon Kruger@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3919, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a56082fcc3049e930e6", + "username": "rustyx", + "email": "rustyx@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 74549, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930e8", + "username": "endolith", + "email": "endolith@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23968, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930ea", + "username": "Mechanical snail", + "email": "Mechanical snail@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28713, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930ec", + "username": "edsioufi", + "email": "edsioufi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 8237, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930ee", + "username": "Roman Nikitchenko", + "email": "Roman Nikitchenko@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12474, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930f0", + "username": "TankorSmash", + "email": "TankorSmash@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11755, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930f2", + "username": "Sec", + "email": "Sec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6809, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930f4", + "username": "wisnij", + "email": "wisnij@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930f9", + "username": "Jeffrey Jose", + "email": "Jeffrey Jose@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1944, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930fb", + "username": "BrainSlugs83", + "email": "BrainSlugs83@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 5946, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930fd", + "username": "Nilzor", + "email": "Nilzor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 17495, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e930ff", + "username": "thSoft", + "email": "thSoft@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 20930, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93101", + "username": "Anorov", + "email": "Anorov@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1950, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93103", + "username": "keithjgrant", + "email": "keithjgrant@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12072, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93105", + "username": "5lava", + "email": "5lava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1138, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93109", + "username": "Vili", + "email": "Vili@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1507, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9310b", + "username": "Ignacio Vazquez-Abrams", + "email": "Ignacio Vazquez-Abrams@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 746270, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9310d", + "username": "Jack M", + "email": "Jack M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3980, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9310f", + "username": "Johan", + "email": "Johan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2372, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93111", + "username": "javajavajavajavajava", + "email": "javajavajavajavajava@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1173, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93114", + "username": "Chase", + "email": "Chase@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11121, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93117", + "username": "gen_Eric", + "email": "gen_Eric@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 216147, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9311a", + "username": "awe", + "email": "awe@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 21306, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9311c", + "username": "Jack O'Connor", + "email": "Jack O'Connor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 9082, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9311e", + "username": "John Gardner", + "email": "John Gardner@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 23262, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93121", + "username": "jaygooby", + "email": "jaygooby@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2396, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93123", + "username": "Simon Nickerson", + "email": "Simon Nickerson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 40863, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93126", + "username": "bukzor", + "email": "bukzor@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36502, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93128", + "username": "Michal Illich", + "email": "Michal Illich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1785, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9312a", + "username": "Steve McLeod", + "email": "Steve McLeod@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 50792, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9312c", + "username": "Jiminy", + "email": "Jiminy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 605, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e9312e", + "username": "Sethi", + "email": "Sethi@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1378, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93130", + "username": "Matt Montag", + "email": "Matt Montag@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6677, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93132", + "username": "Jonathon", + "email": "Jonathon@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 155, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93134", + "username": "neworld", + "email": "neworld@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7639, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a68082fcc3049e93136", + "username": "Mark Ransom", + "email": "Mark Ransom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 288556, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931a2", + "username": "Charlie Parker", + "email": "Charlie Parker@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 12992, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931a4", + "username": "Kamran Ahmed", + "email": "Kamran Ahmed@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10989, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931a6", + "username": "ruakh", + "email": "ruakh@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 168128, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931a8", + "username": "kdlannoy", + "email": "kdlannoy@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 103, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931aa", + "username": "ICR", + "email": "ICR@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13598, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931ad", + "username": "Elazar Leibovich", + "email": "Elazar Leibovich@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 31740, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931af", + "username": "mgalgs", + "email": "mgalgs@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 14641, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931b1", + "username": "DavidWinterbottom", + "email": "DavidWinterbottom@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 6268, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931b3", + "username": "Fábio Santos", + "email": "Fábio Santos@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3661, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931b6", + "username": "Alan Curry", + "email": "Alan Curry@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 13635, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931b8", + "username": "decasteljau", + "email": "decasteljau@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7335, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931ba", + "username": "joshcomley", + "email": "joshcomley@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27472, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931bc", + "username": "juanchopanza", + "email": "juanchopanza@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 218144, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931be", + "username": "Viktor Apoyan", + "email": "Viktor Apoyan@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10537, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931c0", + "username": "vy32", + "email": "vy32@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 26666, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931c3", + "username": "Warpzit", + "email": "Warpzit@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 27718, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931c6", + "username": "Cascabel", + "email": "Cascabel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 456557, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931c8", + "username": "Michael Myers", + "email": "Michael Myers@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 184808, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931ca", + "username": "miguel", + "email": "miguel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 15385, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931cc", + "username": "ObscureRobot", + "email": "ObscureRobot@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7198, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931d0", + "username": "intrepion", + "email": "intrepion@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 36789, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931d2", + "username": "Joel", + "email": "Joel@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 28540, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931d4", + "username": "Michael Burr", + "email": "Michael Burr@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 323310, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931d6", + "username": "Caio", + "email": "Caio@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 362, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32a7a082fcc3049e931d8", + "username": "Johnny Everson", + "email": "Johnny Everson@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 7993, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32ab8082fcc3049e93213", + "username": "some", + "email": "some@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 46557, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32ab8082fcc3049e93214", + "username": "dirkgently", + "email": "dirkgently@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 105303, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93201"]] + }, + { + "_id": "62f32ab8082fcc3049e93215", + "username": "rockXrock", + "email": "rockXrock@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3363, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93204"]] + }, + { + "_id": "62f32ab8082fcc3049e93217", + "username": "Palec", + "email": "Palec@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11764, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32ab8082fcc3049e9321a", + "username": "Aravind", + "email": "Aravind@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3119, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93207"]] + }, + { + "_id": "62f32ab8082fcc3049e9321b", + "username": "B M", + "email": "B M@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 3723, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93206"]] + }, + { + "_id": "62f32ab8082fcc3049e9321c", + "username": "rus1", + "email": "rus1@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 512, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93208"]] + }, + { + "_id": "62f32ab8082fcc3049e9321e", + "username": "lukeaus", + "email": "lukeaus@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 10317, + "questionIds": [], + "answerIds": [] + }, + { + "_id": "62f32ab8082fcc3049e9321f", + "username": "Bad", + "email": "Bad@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 4657, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93209"]] + }, + { + "_id": "62f32ab9082fcc3049e93220", + "username": "Louis Moore", + "email": "Louis Moore@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 587, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320b"]] + }, + { + "_id": "62f32ab9082fcc3049e93221", + "username": "IvanM", + "email": "IvanM@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 2731, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320d"]] + }, + { + "_id": "62f32ab9082fcc3049e93222", + "username": "shiva kumar", + "email": "shiva kumar@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 11014, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e9320c"]] + }, + { + "_id": "62f32ab9082fcc3049e93223", + "username": "Baraa Al-Tabbaa", + "email": "Baraa Al-Tabbaa@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 743, + "questionIds": [], + "answerIds": [["62f32ab6082fcc3049e931fb", "62f32ab7082fcc3049e93210"]] + }, + { + "_id": "62f32ab9082fcc3049e93224", + "username": "John Leidegren", + "email": "John Leidegren@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 58603, + "questionIds": ["62f32ab6082fcc3049e931fb"], + "answerIds": [] + }, + { + "_id": "62f32b12082fcc3049e9322d", + "username": "Hordak", + "email": "Hordak@fake-email.com", + "salt": "c3f238fcf3d084ba7edbd544bce09704", + "key": "48329c03c71895a5424adec3a61bccc85b79c9972268b1bdaaa4d98817c646eab8007469ae7fda8308db21e47773fe597599833c8e38a9b3581dd694a9890b20", + "points": 1234567, + "questionIds": ["62f321b9082fcc3049e8feab"], + "answerIds": [] + } + ], + "cache": { + "complete": true + } +} diff --git a/external-scripts/initialize-data/api-test.ts b/external-scripts/initialize-data/api-test.ts new file mode 100644 index 0000000..70ae4fc --- /dev/null +++ b/external-scripts/initialize-data/api-test.ts @@ -0,0 +1,220 @@ +import { GuruMeditationError } from 'named-app-errors'; +import { toss } from 'toss-expression'; + +import type { RequestInspector } from 'multiverse/throttled-fetch'; + +type RequestLike = { url: string | URL }; + +const idToInt = (id: string) => { + return Number.parseInt( + id + .split('-') + .at(-1)! + .replace(/[^0-9]/g, '') + ); +}; + +const reqParamToNumber = (req: RequestLike, parameter: string) => { + return Number.parseInt( + new URL(req.url).searchParams.get(parameter) || + toss(new GuruMeditationError(`missing ${parameter} parameter`)) + ); +}; + +export const dummyRequestInspector: RequestInspector = ({ requestInfo }) => { + const url = new URL(requestInfo.toString()); + + if (/\/questions\//.test(url.pathname)) { + if (/\/answers/.test(url.pathname)) { + return getDummyQuestionAnswers({ url }); + } else { + return getDummyQuestionComments({ url }); + } + } else if (/\/answers\//.test(url.pathname)) { + return getDummyAnswerComments({ url }); + } else if (/\/questions/.test(url.pathname)) { + return getDummyQuestions({ url }); + } else if (/\/answers/.test(url.pathname)) { + return getDummyAnswers({ url }); + } else if (/\/comments/.test(url.pathname)) { + return getDummyComments({ url }); + } else { + throw new GuruMeditationError('unknown request url'); + } +}; + +export const getDummyQuestionAnswers = (req: RequestLike) => { + const question_id = + req.url.toString().match(/questions\/([^/]+)\/answers/)?.[1] || + toss(new GuruMeditationError('id not found')); + + const idInt = idToInt(question_id); + const numPages = idInt % 75 == 0 ? 3 : idInt % 50 == 0 ? 2 : 1; + const pageParam = reqParamToNumber(req, 'page'); + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + + const startCount = (pageParam - 1) * pageSizeParam; + const has_more = pageParam < numPages; + + return { + items: Array.from({ + length: has_more ? pageSizeParam : (idInt % pageSizeParam) + 1 + }).map((_, ndx) => { + const count = startCount + ndx; + const answer_id = `${question_id}-a${count}`; + + return { + owner: { + display_name: `user-a${count}`, + reputation: count + }, + is_accepted: count % 2 ? true : false, + score: count % 2 ? count : -count, + creation_date: count, + answer_id, + question_id, + body: `body-${answer_id}` + }; + }), + has_more + }; +}; + +export const getDummyQuestionComments = (req: RequestLike) => { + const question_id = + req.url.toString().match(/questions\/([^/]+)\/comments/)?.[1] || + toss(new GuruMeditationError('id not found')); + + const idInt = idToInt(question_id); + const numPages = idInt % 25 == 0 ? 2 : 1; + const pageParam = reqParamToNumber(req, 'page'); + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + + const startCount = (pageParam - 1) * pageSizeParam; + const has_more = pageParam < numPages; + + return { + items: Array.from({ + length: has_more ? pageSizeParam : (idInt % pageSizeParam) - 1 + }).map((_, ndx) => { + const count = startCount + ndx; + const comment_id = `${question_id}-c${count}`; + + return { + owner: { + display_name: `user-${question_id}`, + reputation: count + }, + score: count % 2 ? count : -count, + creation_date: count, + comment_id, + body: `body-${comment_id}` + }; + }), + has_more + }; +}; + +export const getDummyAnswerComments = (req: RequestLike) => { + const answer_id = + req.url.toString().match(/answers\/([^/]+)\/comments/)?.[1] || + toss(new GuruMeditationError('id not found')); + + const idInt = idToInt(answer_id); + const numPages = 1; + const pageParam = reqParamToNumber(req, 'page'); + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + + const startCount = (pageParam - 1) * pageSizeParam; + const has_more = pageParam < numPages; + + return { + items: Array.from({ + length: has_more ? pageSizeParam : (idInt % pageSizeParam) - 1 + }).map((_, ndx) => { + const count = startCount + ndx; + const comment_id = `${answer_id}-c${count}`; + + return { + owner: { + display_name: `user-${answer_id}`, + reputation: count + }, + score: count % 2 ? count : -count, + creation_date: count, + comment_id, + body: `body-${comment_id}` + }; + }), + has_more + }; +}; + +export const getDummyQuestions = (req: RequestLike) => { + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + const startCount = (reqParamToNumber(req, 'page') - 1) * pageSizeParam; + + return { + items: Array.from({ length: pageSizeParam }).map((_, ndx) => { + const count = startCount + ndx; + const question_id = `q${count}`; + + return { + owner: { display_name: `user-${question_id}`, reputation: count }, + is_answered: count % 2 ? true : false, + view_count: count, + answer_count: count, + score: count % 2 ? count : -count, + creation_date: count, + question_id, + title: `title-${question_id}`, + body: `body-${question_id}` + }; + }), + has_more: true + }; +}; + +export const getDummyAnswers = (req: RequestLike) => { + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + const startCount = (reqParamToNumber(req, 'page') - 1) * pageSizeParam; + + return { + items: Array.from({ length: pageSizeParam }).map((_, ndx) => { + const count = startCount + ndx; + const answer_id = `a${count}`; + + return { + owner: { display_name: `user-${answer_id}`, reputation: count }, + is_accepted: count % 2 ? true : false, + score: count % 2 ? count : -count, + creation_date: count, + answer_id, + question_id: count, + body: `body-${answer_id}` + }; + }), + has_more: true + }; +}; + +export const getDummyComments = (req: RequestLike) => { + const pageSizeParam = reqParamToNumber(req, 'pagesize'); + const startCount = (reqParamToNumber(req, 'page') - 1) * pageSizeParam; + + return { + items: Array.from({ length: pageSizeParam }).map((_, ndx) => { + const count = startCount + ndx; + const comment_id = `c${count}`; + + return { + owner: { display_name: `user-${comment_id}`, reputation: count }, + score: count % 2 ? count : -count, + creation_date: count, + comment_id, + body: `body-${comment_id}` + }; + }), + has_more: true + }; +}; diff --git a/external-scripts/initialize-data/api.ts b/external-scripts/initialize-data/api.ts new file mode 100644 index 0000000..8eeef06 --- /dev/null +++ b/external-scripts/initialize-data/api.ts @@ -0,0 +1,80 @@ +export const STACKX_API_URI = 'https://api.stackexchange.com/2.3'; +//export const STACKX_API_URI = 'http://0.0.0.0:8000'; +export const STACKX_API_SHARED_PARAMS = 'site=stackoverflow&filter=withbody'; + +const uriBuilder = (endpoint: string, params: string) => + `${STACKX_API_URI}/${endpoint}?${STACKX_API_SHARED_PARAMS}&${params}`; + +/** + * Represents the StackExchange API interface. + */ +export type Api = { + questions: { + (params: { page: number; pageSize: number }): string; + answers: (params: { + question_id: number; + page: number; + pageSize: number; + }) => string; + comments: (params: { + question_id: number; + page: number; + pageSize: number; + }) => string; + }; + + answers: { + (params: { page: number; pageSize: number }): string; + comments: (params: { + answer_id: number; + page: number; + pageSize: number; + }) => string; + }; + + comments: (params: { page: number; pageSize: number }) => string; +}; + +/** + * Get StackExchange API endpoint URIs with respect to authentication + * credentials and pagination parameters. + */ +export const getApi = (stackExAuthKey: string) => { + const api: Api = { + questions: (({ page, pageSize }) => + uriBuilder( + 'questions', + `sort=votes&order=desc&tagged=javascript&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + )) as Api['questions'], + answers: (({ page, pageSize }) => + uriBuilder( + 'answers', + `sort=votes&order=desc&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + )) as Api['answers'], + comments: ({ page, pageSize }) => + uriBuilder( + 'comments', + `sort=votes&order=desc&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + ) + }; + + api.questions.answers = ({ question_id, page, pageSize }) => + uriBuilder( + `questions/${question_id}/answers`, + `sort=creation&order=asc&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + ); + + api.questions.comments = ({ question_id, page, pageSize }) => + uriBuilder( + `questions/${question_id}/comments`, + `sort=creation&order=asc&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + ); + + api.answers.comments = ({ answer_id, page, pageSize }) => + uriBuilder( + `answers/${answer_id}/comments`, + `sort=creation&order=asc&page=${page}&pagesize=${pageSize}&key=${stackExAuthKey}` + ); + + return api; +}; diff --git a/external-scripts/initialize-data/crypto.ts b/external-scripts/initialize-data/crypto.ts new file mode 100644 index 0000000..6b27667 --- /dev/null +++ b/external-scripts/initialize-data/crypto.ts @@ -0,0 +1,58 @@ +import { webcrypto as crypto } from 'node:crypto'; + +/** + * We use 64 byte keys (128 hex digits long) + */ +const KEY_SIZE_BYTES = 64; + +/** + * We use 16 byte salts (32 hex digits long) + */ +const SALT_SIZE_BYTES = 16; + +/** + * A function that converts a ByteArray or any other array of bytes into a + * string of hexadecimal digits + */ +export const convertBufferToHex = (buffer: ArrayBufferLike) => { + return [...new Uint8Array(buffer)] + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(''); +}; + +/** + * Turns a password (string) and salt (buffer) into a key and salt (hex strings) + */ +export const deriveKeyFromPassword = async ( + passwordString: string, + saltBuffer?: Uint8Array +) => { + const textEncoder = new TextEncoder(); + const passwordBuffer = textEncoder.encode(passwordString); + + saltBuffer = saltBuffer || crypto.getRandomValues(new Uint8Array(SALT_SIZE_BYTES)); + + const plaintextKey = await crypto.subtle.importKey( + 'raw', + passwordBuffer, + 'PBKDF2', + false, + ['deriveBits'] + ); + + const pbkdf2Buffer = await crypto.subtle.deriveBits( + { + name: 'PBKDF2', + salt: saltBuffer, + iterations: 100000, + hash: 'SHA-256' + }, + plaintextKey, + KEY_SIZE_BYTES * 8 + ); + + const saltString = convertBufferToHex(saltBuffer); + const keyString = convertBufferToHex(pbkdf2Buffer); + + return { keyString, saltString }; +}; diff --git a/external-scripts/initialize-data/index.ts b/external-scripts/initialize-data/index.ts new file mode 100644 index 0000000..df117c2 --- /dev/null +++ b/external-scripts/initialize-data/index.ts @@ -0,0 +1,1480 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + AppError, + GuruMeditationError, + HttpError, + InvalidAppEnvironmentError +} from 'named-app-errors'; + +import jsonFile from 'jsonfile'; +import { AnyBulkWriteOperation, ObjectId } from 'mongodb'; +import { toss } from 'toss-expression'; +import { setTimeout as wait } from 'node:timers/promises'; +import inquirer, { PromptModule } from 'inquirer'; +import { decode as decodeEntities } from 'html-entities'; + +import { debugNamespace as namespace } from 'universe/constants'; +import { getEnv } from 'universe/backend/env'; + +import { itemToObjectId } from 'multiverse/mongo-item'; +import { RequestQueue } from 'multiverse/throttled-fetch'; +import { debugFactory } from 'multiverse/debug-extended'; +import { getDb } from 'multiverse/mongo-schema'; +import { hydrateDb } from 'multiverse/mongo-test'; + +import { dummyAppData } from 'testverse/db'; + +import { getApi } from 'externals/initialize-data/api'; +//import { dummyRequestInspector } from 'externals/initialize-data/api-test'; +import { deriveKeyFromPassword } from 'externals/initialize-data/crypto'; + +import type { + AnswerId, + InternalAnswer, + InternalComment, + InternalMail, + InternalQuestion, + InternalUser, + QuestionId +} from 'universe/backend/db'; + +import type { + StackExchangeAnswer, + StackExchangeApiResponse, + StackExchangeComment, + StackExchangeQuestion +} from 'types/stackexchange-api'; + +const debugNamespace = `${namespace}:initialize-data`; +const cachePath = `${__dirname}/initialize-data-cache.json`; + +const log = debugFactory(debugNamespace); +const debug = debugFactory(`${debugNamespace}:debug`); +const logOrDebug = () => { + return log.enabled ? log : debug; +}; + +/** + * Represents one second in milliseconds. + */ +const oneSecondInMs = 1000; + +/** + * Maximum number of times to retry certain failed requests. + */ +const maxRequestRetries = 10; + +/** + * Delay (in ms) after a rate limit is detected. + */ +const delayAfterRequestRateLimitMs = + (process.env.NODE_ENV == 'test' ? 0.1 : 180) * oneSecondInMs; + +/** + * Delay (in ms) after a server error is detected. + */ +const delayAfterRequestErrorMs = + (process.env.NODE_ENV == 'test' ? 0.1 : 60) * oneSecondInMs; + +// eslint-disable-next-line no-console +log.log = console.info.bind(console); + +// ? Ensure this next line survives Webpack +if (!globalThis.process.env.DEBUG && getEnv().NODE_ENV != 'test') { + debugFactory.enable( + `${debugNamespace},${debugNamespace}:*,-${debugNamespace}:debug` + ); +} + +/** + * Returns the `inquirer` instance unless a string `testPrompterParams` is given + * (passed to URLSearchParams, usually provided by a TEST_PROMPTER_X environment + * variable), in which case a passthrough promise that resolves to a simulated + * answer object based on `testPrompterParams` is returned as the resolved + * result of calling `prompt()` instead. + */ +const getPrompter = (testPrompterParams?: string): { prompt: PromptModule } => { + return testPrompterParams + ? { + prompt: (() => { + debug( + `using simulated inquirer prompt based on params: ${testPrompterParams}` + ); + + return Promise.resolve( + Array.from(new URLSearchParams(testPrompterParams).entries()).reduce< + Record + >((obj, [k, v]) => { + obj[k] = v; + return obj; + }, {}) + ); + }) as unknown as PromptModule + } + : inquirer; +}; + +/** + * Transforms a positive, negative, or zero net score into upvotes and + * downvotes. + */ +const getUpvotesDownvotesFromScore = (score: number) => { + const aVotes = Math.floor( + Math.random() * score + + (score == 0 ? Math.random() * 10 ** (Math.random() * 4) : 0) + ); + const bVotes = score + aVotes; + return [Math.abs(Math.max(aVotes, bVotes)), Math.abs(Math.min(aVotes, bVotes))] as [ + upvotes: number, + downvotes: number + ]; +}; + +/** + * Writes out cache data to a well-known location on disk. + */ +const cacheDataToDisk = async (data: Data | null) => { + if (!data) { + throw new GuruMeditationError('cannot cache null data'); + } + + logOrDebug()( + `writing out cache keys (${Object.keys(data).join(', ')}) to ${cachePath}` + ); + + await jsonFile.writeFile(cachePath, data, { spaces: 2 }); +}; + +/** + * Reports statistics about the cached data. + */ +const getDataStats = (data: Data | null) => { + if (!data) { + throw new GuruMeditationError('cannot generate stats from null data'); + } + + let questionsDataSizeBytes = 0; + let userDataSizeBytes = 0; + let totalAnswers = 0; + let totalComments = 0; + + data.questions.forEach((question) => { + questionsDataSizeBytes += Buffer.byteLength(JSON.stringify(question), 'utf-8'); + totalComments += question.commentItems.length; + question.answerItems.forEach((answer) => { + totalAnswers++; + totalComments += answer.commentItems.length; + }); + }); + + data.users.forEach((user) => { + userDataSizeBytes += Buffer.byteLength(JSON.stringify(user), 'utf-8'); + }); + + return { + cacheMetadata: data.cache, + totalQuestions: data.questions.length, + totalAnswers, + totalComments, + totalUsers: data.users.length, + dataSizesInBytes: { + questions: questionsDataSizeBytes, + users: userDataSizeBytes + } + }; +}; + +const commitApiDataToDb = async (data: Data | null) => { + if (!data) { + throw new GuruMeditationError('cannot commit null data'); + } + + logOrDebug()('committing StackExchange API data to database'); + + const appDb = await getDb({ name: 'app' }); + + const [mailResult, questionsResult, usersResult] = await Promise.all([ + data.users.length && dummyAppData.mail.length + ? appDb.collection('mail').insertMany( + data.users + .slice(0, Math.min(data.users.length, dummyAppData.mail.length * 2)) + .map((user, ndx, users) => { + return ndx % 2 == 0 + ? { + _id: new ObjectId(), + createdAt: Date.now(), + sender: user.username, + receiver: users[ndx + 1]?.username || user.username, + subject: dummyAppData.mail[ndx / 2]?.subject || 'no subject', + text: + dummyAppData.mail[ndx / 2]?.text || + 'I could not think of a message...' + } + : null; + }) + .filter((value): value is InternalMail => Boolean(value)) + ) + : { insertedCount: '0 (empty)' }, + data.questions.length + ? appDb.collection('questions').insertMany( + data.questions.map((question) => { + const decodedTitle = decodeEntities(question.title); + + return { + ...question, + commentItems: question.commentItems.map((comment) => { + return { + ...comment, + text: decodeEntities(comment.text) + }; + }), + answerItems: question.answerItems.map((answer) => { + return { + ...answer, + commentItems: answer.commentItems.map((comment) => { + return { + ...comment, + text: decodeEntities(comment.text) + }; + }) + }; + }), + title: decodeEntities(question.title), + 'title-lowercase': decodedTitle.toLowerCase() + }; + }) + ) + : { insertedCount: '0 (empty)' }, + data.users.length + ? appDb.collection('users').bulkWrite( + data.users.map>((user) => { + return { + updateOne: { + filter: { username: user.username }, + update: { + $addToSet: { + questionIds: { $each: user.questionIds }, + answerIds: { $each: user.answerIds } + }, + $setOnInsert: { + _id: user._id, + username: user.username, + email: user.email, + key: user.key, + salt: user.salt, + points: user.points + } + }, + upsert: true + } + }; + }) + ) + : { nUpserted: '0 (empty)', nModified: '0 (empty)' } + ]); + + logOrDebug()(`inserted ${mailResult.insertedCount} mail documents`); + logOrDebug()(`inserted ${questionsResult.insertedCount} question documents`); + logOrDebug()( + `upserted ${usersResult.nUpserted}, updated ${usersResult.nModified} user documents` + ); +}; + +const commitRootDataToDb = async (data: Data | null) => { + if (!data) { + throw new GuruMeditationError('cannot commit null data'); + } + + logOrDebug()('committing dummy root data to database via hydration'); + + await hydrateDb({ name: 'root' }); +}; + +/** + * Setups up a database from scratch by creating collections (only if they do + * not already exist) and populating them with a large amount of data. Suitable + * for initializing local machines or production instances alike. + * + * This function is idempotent (can be called multiple times without changing + * anything) and data-preserving (all actions are non-destructive: data is never + * overwritten or deleted) + */ +const invoked = async () => { + try { + const { + MAX_ANSWER_BODY_LENGTH_BYTES: maxAnswerLen, + MAX_COMMENT_LENGTH: maxCommentLen, + MAX_QUESTION_BODY_LENGTH_BYTES: maxQuestionBodyLen, + MAX_QUESTION_TITLE_LENGTH: maxQuestionTitleLen, + STACKAPPS_INTERVAL_PERIOD_MS: intervalPeriodMs, + STACKAPPS_MAX_REQUESTS_PER_INTERVAL: maxRequestsPerInterval, + STACKAPPS_TOTAL_API_GENERATED_QUESTIONS: desiredApiGeneratedQuestionsCount, + STACKAPPS_COLLECTALL_QUESTION_ANSWERS: collectAllQuestionAnswersCount, + STACKAPPS_COLLECTALL_QUESTION_COMMENTS: collectAllQuestionCommentsCount, + STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS: collectAllFirstAnswerCommentsCount, + STACKAPPS_MAX_PAGE_SIZE: maxPageSize, + STACKAPPS_AUTH_KEY: stackExAuthKey + } = getEnv(); + + if (typeof intervalPeriodMs != 'number' || !(intervalPeriodMs >= 0)) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_INTERVAL_PERIOD_MS must be greater than or equal to 0' + ); + } + + if (!maxRequestsPerInterval || !(maxRequestsPerInterval > 0)) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_MAX_REQUESTS_PER_INTERVAL must be greater than zero' + ); + } + + if ( + !desiredApiGeneratedQuestionsCount || + !(desiredApiGeneratedQuestionsCount > 0) + ) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_TOTAL_API_GENERATED_QUESTIONS must be greater than zero' + ); + } + + if (!collectAllQuestionAnswersCount || !(collectAllQuestionAnswersCount > 0)) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_COLLECTALL_QUESTION_ANSWERS must be greater than zero' + ); + } + + if (!collectAllQuestionCommentsCount || !(collectAllQuestionCommentsCount > 0)) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_COLLECTALL_QUESTION_COMMENTS must be greater than zero' + ); + } + + if ( + !collectAllFirstAnswerCommentsCount || + !(collectAllFirstAnswerCommentsCount > 0) + ) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS must be greater than zero' + ); + } + + if (!maxPageSize || !(maxPageSize > 0) || !(maxPageSize <= 100)) { + throw new InvalidAppEnvironmentError( + 'STACKAPPS_MAX_PAGE_SIZE must be greater than zero and less than or equal to 100' + ); + } + + if (!stackExAuthKey) { + throw new InvalidAppEnvironmentError('STACKAPPS_AUTH_KEY must be provided'); + } + + let interrogateApi = true; + let usedStackExAuthKey = stackExAuthKey; + let data: Data | null = null; + + try { + debug( + process.env.TEST_SKIP_REQUESTS + ? 'saw debug env var TEST_SKIP_REQUESTS. No cache read performed' + : `reading in results from cache at ${cachePath}` + ); + + data = process.env.TEST_SKIP_REQUESTS + ? null + : await jsonFile.readFile(cachePath, { + reviver(key, value) { + return key == '_id' ? itemToObjectId(value) : value; + } + }); + } catch { + data = null; + } + + debug(`found cached results: ${data ? 'yes' : 'no'}`); + + debug( + `cached data is: ${ + typeof data?.cache.complete != 'boolean' + ? 'non-existent' + : data?.cache.complete + ? 'complete' + : 'incomplete' + }` + ); + + await getPrompter(process.env.TEST_PROMPTER_INITIALIZER) + .prompt<{ action: string; token: string }>([ + { + name: 'action', + message: 'select an initializer action', + type: 'list', + choices: [ + ...(typeof data?.cache.complete == 'boolean' + ? [ + { + name: 'reinterrogate API using cache', + value: 'hit' + }, + { + name: 'reinterrogate API using cache and a custom token', + value: 'hit-custom' + }, + { + name: 'ignore cache and reinterrogate API', + value: 'hit-ignore' + }, + { + name: 'ignore cache and reinterrogate API using a custom token', + value: 'hit-ignore-custom' + }, + { + name: data?.cache.complete + ? 'commit completed cache data to database' + : 'commit incomplete cache data to database', + value: 'commit' + } + ] + : [ + { + name: 'interrogate API', + value: 'hit' + }, + { + name: 'interrogate API using a custom token', + value: 'hit-custom' + } + ]), + { name: 'exit', value: 'exit' } + ] + }, + { + name: 'token', + message: 'enter new token', + type: 'input', + when: (answers) => answers.action.includes('custom') + } + ]) + .then(async (answers) => { + if (answers.action == 'exit') { + logOrDebug()('execution complete'); + process.exit(0); + } + + if (answers.action.startsWith('commit')) { + await commitApiDataToDb(data); + + logOrDebug()('execution complete'); + process.exit(0); + } + + interrogateApi = answers.action.startsWith('hit'); + usedStackExAuthKey = answers.token ?? usedStackExAuthKey; + + if (answers.action.startsWith('hit-ignore')) { + logOrDebug()('cache ignored'); + data = null; + } + }); + + if (interrogateApi) { + logOrDebug()('interrogating StackExchange API...'); + + data = + data?.cache?.complete === false + ? data + : { + questions: [], + users: [], + cache: { + complete: false + } + }; + + const { keyString, saltString } = await deriveKeyFromPassword('password'); + + const addOrUpdateUser = async ( + username: string, + { + points, + newQuestionId: newQuestionId, + newAnswerId: newAnswerId + }: { points: number; newQuestionId?: QuestionId; newAnswerId?: AnswerId } + ) => { + let user = data!.users.find((u) => u.username == username); + + if (!user) { + user = { + _id: new ObjectId(), + username, + email: `${username}@fake-email.com`, + salt: saltString, + key: keyString, + points, + questionIds: [], + answerIds: [] + }; + + data!.users.push(user); + } + + if (newAnswerId) { + if (!newQuestionId) { + throw new GuruMeditationError( + 'localUpsertUser cannot use answerId without corresponding questionId' + ); + } + + user.answerIds.push([newQuestionId, newAnswerId]); + } else if (newQuestionId) { + user.questionIds.push(newQuestionId); + } + + return user; + }; + + const endpointBackoffs: Record = {}; + + // * max 30req/1000ms https://api.stackexchange.com/docs/throttle + const queue = new RequestQueue({ + intervalPeriodMs, + maxRequestsPerInterval, + // requestInspector: dummyRequestInspector + fetchErrorInspector: ({ + error: e, + queue: q, + requestInfo, + requestInit, + state + }) => { + const subDebug = logOrDebug().extend('err'); + + const apiEndpoint = + (state.apiEndpoint as string) || + toss(new GuruMeditationError('missing apiEndpoint metadata')); + + const tries = (state.tries as number) ?? 1; + const retriedTooManyTimes = tries >= maxRequestRetries; + + subDebug( + `handling fetch error for [${apiEndpoint}] (try #${tries}): ${requestInfo}` + ); + + if (retriedTooManyTimes) { + subDebug.warn('request retried too many times'); + throw new HttpError(`${e}`); + } + + subDebug(`requeuing errored request: ${requestInfo}`); + return q.addRequestToQueue(requestInfo, requestInit, { + ...state, + rescheduled: true, + tries: tries + 1 + }); + }, + requestInspector: ({ queue: q, requestInfo, requestInit, state }) => { + const subDebug = logOrDebug().extend('req'); + + const apiEndpoint = + (state.apiEndpoint as string) || + toss(new GuruMeditationError('missing apiEndpoint metadata')); + const tries = (state.tries as number) ?? 1; + + subDebug(`saw [${apiEndpoint}] (try #${tries}): ${requestInfo}`); + + if ( + typeof endpointBackoffs[apiEndpoint] == 'number' && + Date.now() <= endpointBackoffs[apiEndpoint] + ) { + subDebug.warn( + `request will be delayed until ${new Date( + endpointBackoffs[apiEndpoint] + ).toLocaleString()}` + ); + + return wait( + Math.max(0, endpointBackoffs[apiEndpoint] - Date.now()), + undefined, + { + signal: requestInit.signal as AbortSignal + } + ).then(() => { + subDebug(`requeuing delayed request: ${requestInfo}`); + return q.addRequestToQueue(requestInfo, requestInit, { + ...state, + rescheduled: true + }); + }); + } + }, + responseInspector: async ({ + response, + queue: q, + requestInfo, + requestInit, + state + }) => { + const subDebug = logOrDebug().extend('res'); + const res = response as Response; + + if (state.rescheduled as boolean) { + subDebug(`passing through rescheduled request to ${requestInfo}`); + return res; + } else { + const apiEndpoint = + (state.apiEndpoint as string) || + toss(new GuruMeditationError('missing apiEndpoint metadata')); + const tries = (state.tries as number) ?? 1; + const retriedTooManyTimes = tries >= maxRequestRetries; + + const json: Partial> = await res + .json() + .catch(() => ({})); + + subDebug(`saw [${apiEndpoint}] (try #${tries}): ${requestInfo}`); + + if (retriedTooManyTimes) { + subDebug.warn('request retried too many times'); + } + + if (json.backoff) { + endpointBackoffs[apiEndpoint] = + Date.now() + json.backoff * oneSecondInMs; + + subDebug.warn( + `saw backoff demand in response, delaying further requests to endpoint "${apiEndpoint}" until ${new Date( + endpointBackoffs[apiEndpoint] + ).toLocaleString()}` + ); + } + + if (retriedTooManyTimes && json.error_message) { + throw new HttpError(res, json.error_message); + } else if (res.ok) { + return json; + } else { + if (retriedTooManyTimes || (res.status < 500 && res.status != 429)) { + throw new HttpError(res); + } else { + if (res.status == 502 || res.status == 429) { + subDebug.warn( + `rate limited detected (${res.status}), delaying next interval request processing for ${delayAfterRequestRateLimitMs}ms before requeuing` + ); + q.delayRequestProcessingByMs(delayAfterRequestRateLimitMs); + } else { + subDebug.warn( + `a server error occurred (${res.status}), delaying next interval request processing for ${delayAfterRequestErrorMs}ms before requeuing` + ); + q.delayRequestProcessingByMs(delayAfterRequestErrorMs); + } + + return q.addRequestToQueue(requestInfo, requestInit, { + ...state, + tries: tries + 1 + }); + } + } + } + } + }); + + queue.beginProcessingRequestQueue(); + + try { + while (interrogateApi) { + interrogateApi = false; + const api = getApi(usedStackExAuthKey); + + try { + if (process.env.TEST_SKIP_REQUESTS) { + debug.warn( + 'saw debug env var TEST_SKIP_REQUESTS. No requests will be made' + ); + debug('queue stats: %O', queue.getStats()); + } else { + await Promise.all([ + (async () => { + for ( + let questionPage = 1; + data.questions.length < desiredApiGeneratedQuestionsCount; + ++questionPage + ) { + const questions = await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.questions({ + page: questionPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'questions' } + ); + + logOrDebug()( + `remaining questions wanted: ${ + desiredApiGeneratedQuestionsCount - data.questions.length + }` + ); + + await Promise.all( + questions.items.map(async (question, questionIndex) => { + const dataQuestionsLength = data!.questions.length; + const questionAlreadyCached = + data!.cache.complete === false && + data!.questions.find((q) => question.title == q.title); + + if (questionAlreadyCached) { + logOrDebug()( + `retrieved question #${ + dataQuestionsLength + questionIndex + 1 + } directly from local cache: ${question.title}` + ); + } else if ( + question.body.length > maxQuestionBodyLen || + question.title.length > maxQuestionTitleLen + ) { + logOrDebug()( + `skipped question #${ + dataQuestionsLength + questionIndex + 1 + } for violating length constraints: ${question.title}` + ); + } else { + const newQuestionId = new ObjectId(); + const [questionUpvotes, questionDownvotes] = + getUpvotesDownvotesFromScore(question.score); + + const answerItems: InternalAnswer[] = []; + const questionCommentItems: InternalComment[] = []; + + logOrDebug()( + `received question #${ + dataQuestionsLength + questionIndex + 1 + } from API: ${question.title}` + ); + + await Promise.all([ + (async () => { + for ( + let answerPage = 1, shouldContinue = true; + shouldContinue; + ++answerPage + ) { + const answers = await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.questions.answers({ + question_id: question.question_id, + page: answerPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'questions.answers' } + ); + + const answerItemsLength = answerItems.length; + + logOrDebug()( + `received ${answers.items.length} of (estimated) ${ + question.answer_count + } answers for question #${ + dataQuestionsLength + questionIndex + 1 + } from API` + ); + + await Promise.all( + answers.items.map(async (answer, answerIndex) => { + if (answer.body.length <= maxAnswerLen) { + const newAnswerId = new ObjectId(); + const [answerUpvotes, answerDownvotes] = + getUpvotesDownvotesFromScore(answer.score); + const answerCommentItems: InternalComment[] = + []; + + for ( + let answerCommentPage = 1, + subShouldContinue = true; + subShouldContinue; + ++answerCommentPage + ) { + const comments = + await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.answers.comments({ + answer_id: answer.answer_id, + page: answerCommentPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'answers.comments' } + ); + + const answerCommentItemsLength = + answerCommentItems.length; + + logOrDebug()( + `received ${ + comments.items.length + } comments for answer #${ + answerItemsLength + answerIndex + 1 + } of question #${ + dataQuestionsLength + questionIndex + 1 + } from API` + ); + + await Promise.all( + comments.items.map(async (comment) => { + if ( + comment.body.length <= maxCommentLen + ) { + const [ + commentUpvotes, + commentDownvotes + ] = getUpvotesDownvotesFromScore( + comment.score + ); + + answerCommentItems.push({ + _id: new ObjectId(), + creator: comment.owner.display_name, + createdAt: + comment.creation_date * + oneSecondInMs, + text: comment.body, + upvotes: commentUpvotes, + upvoterUsernames: [], + downvotes: commentDownvotes, + downvoterUsernames: [] + }); + + await addOrUpdateUser( + comment.owner.display_name, + { points: comment.owner.reputation } + ); + } + }) + ); + + logOrDebug()( + `added ${ + answerCommentItems.length - + answerCommentItemsLength + } of ${ + comments.items.length + } received comments to answer #${ + answerItemsLength + answerIndex + 1 + } of question #${ + dataQuestionsLength + questionIndex + 1 + }` + ); + + logOrDebug()( + `---\nthere are now ${ + answerCommentItems.length + } total comments added to answer #${ + answerItemsLength + answerIndex + 1 + } of question #${ + dataQuestionsLength + questionIndex + 1 + }\n---` + ); + + subShouldContinue = comments.has_more; + } + + answerItems.push({ + _id: newAnswerId, + creator: answer.owner.display_name, + createdAt: + answer.creation_date * oneSecondInMs, + text: answer.body, + upvotes: answerUpvotes, + upvoterUsernames: [], + downvotes: answerDownvotes, + downvoterUsernames: [], + accepted: answer.is_accepted, + commentItems: answerCommentItems + }); + + logOrDebug()( + `added answer ${ + answerItemsLength + answerIndex + 1 + } to question #${ + dataQuestionsLength + questionIndex + 1 + }` + ); + + logOrDebug()( + `---\nthere are now ${ + answerItems.length + } total answers added to question #${ + dataQuestionsLength + questionIndex + 1 + }\n---` + ); + + await addOrUpdateUser( + answer.owner.display_name, + { + points: answer.owner.reputation, + newQuestionId, + newAnswerId + } + ); + } else { + logOrDebug()( + `skipped answer #${ + answerItemsLength + answerIndex + 1 + } of question #${ + dataQuestionsLength + questionIndex + 1 + } for violating length constraints` + ); + } + }) + ); + + shouldContinue = answers.has_more; + } + })(), + (async () => { + for ( + let commentPage = 1, shouldContinue = true; + shouldContinue; + ++commentPage + ) { + const comments = await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.questions.comments({ + question_id: question.question_id, + page: commentPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'questions.comments' } + ); + + const questionCommentItemsLength = + questionCommentItems.length; + + logOrDebug()( + `received ${ + comments.items.length + } comments for question #${ + dataQuestionsLength + questionIndex + 1 + } from API` + ); + + await Promise.all( + comments.items.map(async (comment) => { + if (comment.body.length <= maxCommentLen) { + const [commentUpvotes, commentDownvotes] = + getUpvotesDownvotesFromScore(comment.score); + + questionCommentItems.push({ + _id: new ObjectId(), + creator: comment.owner.display_name, + createdAt: + comment.creation_date * oneSecondInMs, + text: comment.body, + upvotes: commentUpvotes, + upvoterUsernames: [], + downvotes: commentDownvotes, + downvoterUsernames: [] + }); + + await addOrUpdateUser( + comment.owner.display_name, + { points: comment.owner.reputation } + ); + } + }) + ); + + logOrDebug()( + `added ${ + questionCommentItems.length - + questionCommentItemsLength + } of ${ + comments.items.length + } received comments to question #${ + dataQuestionsLength + questionIndex + 1 + }` + ); + + logOrDebug()( + `---\nthere are now ${ + questionCommentItems.length + } total comments added to question #${ + dataQuestionsLength + questionIndex + 1 + }\n---` + ); + + shouldContinue = comments.has_more; + } + })() + ]); + + data!.questions.push({ + _id: newQuestionId, + title: question.title, + 'title-lowercase': question.title.toLowerCase(), + creator: question.owner.display_name, + createdAt: question.creation_date * oneSecondInMs, + status: 'open', + text: question.body, + upvotes: questionUpvotes, + upvoterUsernames: [], + downvotes: questionDownvotes, + downvoterUsernames: [], + hasAcceptedAnswer: answerItems.some( + (answer) => answer.accepted + ), + views: question.view_count, + answers: answerItems.length, + answerItems, + comments: questionCommentItems.length, + commentItems: questionCommentItems, + sorter: { + uvc: + questionUpvotes + + question.view_count + + questionCommentItems.length, + uvac: + questionUpvotes + + question.view_count + + answerItems.length + + questionCommentItems.length + } + }); + + logOrDebug()( + `added question #${ + dataQuestionsLength + questionIndex + 1 + } to cache` + ); + + logOrDebug().extend('cached')( + `\n>>> there are now ${ + data!.questions.length + } total of ${desiredApiGeneratedQuestionsCount} wanted questions the cache <<<\n\n` + ); + + await addOrUpdateUser(question.owner.display_name, { + points: question.owner.reputation, + newQuestionId + }); + + await cacheDataToDisk(data); + } + }) + ); + + if (!questions.has_more) { + logOrDebug().warn( + 'somehow exhausted all questions in the StackExchange API?!' + ); + break; + } + } + })(), + (async () => { + const newQuestionId = new ObjectId(); + let questionCreatedAt = Date.now(); + const randomAnswers: InternalAnswer[] = []; + const randomComments: InternalComment[] = []; + const totalDesiredComments = + collectAllQuestionCommentsCount + + collectAllFirstAnswerCommentsCount; + + const questionAlreadyCached = + data.cache.complete === false && !!data.catchallQuestion; + + if (questionAlreadyCached) { + logOrDebug()( + 'retrieved catch-all question directly from local cache' + ); + } else { + await Promise.all([ + (async () => { + for ( + let answerPage = 1; + randomAnswers.length < collectAllQuestionAnswersCount; + ++answerPage + ) { + const answers = await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.answers({ + page: answerPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'answers' } + ); + + const randomAnswersLength = randomAnswers.length; + + logOrDebug()( + `received ${answers.items.length} random answers for catch-all question from API` + ); + + await Promise.all( + answers.items.map(async (answer) => { + if (answer.body.length <= maxAnswerLen) { + const createdAt = + answer.creation_date * oneSecondInMs; + + questionCreatedAt = + createdAt < questionCreatedAt + ? createdAt + : questionCreatedAt; + + const newAnswerId = new ObjectId(); + const [answerUpvotes, answerDownvotes] = + getUpvotesDownvotesFromScore(answer.score); + + randomAnswers.push({ + _id: newAnswerId, + creator: answer.owner.display_name, + createdAt, + text: answer.body, + upvotes: answerUpvotes, + upvoterUsernames: [], + downvotes: answerDownvotes, + downvoterUsernames: [], + commentItems: [], + accepted: false + }); + + await addOrUpdateUser(answer.owner.display_name, { + points: answer.owner.reputation, + newQuestionId, + newAnswerId + }); + } + }) + ); + + logOrDebug()( + `added ${randomAnswers.length - randomAnswersLength} of ${ + answers.items.length + } received answers to catch-all question` + ); + + logOrDebug()( + `there are now ${randomAnswers.length} total of ${collectAllQuestionAnswersCount} wanted random answers added to catch-all question` + ); + + if (!answers.has_more) { + logOrDebug().warn( + 'somehow exhausted all answers in the StackExchange API?!' + ); + break; + } + } + })(), + (async () => { + for ( + let commentPage = 1; + randomComments.length < totalDesiredComments; + ++commentPage + ) { + const comments = await queue.addRequestToQueue< + StackExchangeApiResponse + >( + api.comments({ + page: commentPage, + pageSize: maxPageSize + }), + undefined, + { apiEndpoint: 'comments' } + ); + + const randomCommentsLength = randomComments.length; + + logOrDebug()( + `received ${comments.items.length} random comments for catch-all question and its first answer from API` + ); + + await Promise.all( + comments.items.map(async (comment) => { + if (comment.body.length <= maxCommentLen) { + const createdAt = + comment.creation_date * oneSecondInMs; + + questionCreatedAt = + createdAt < questionCreatedAt + ? createdAt + : questionCreatedAt; + + const [commentUpvotes, commentDownvotes] = + getUpvotesDownvotesFromScore(comment.score); + + randomComments.push({ + _id: new ObjectId(), + creator: comment.owner.display_name, + createdAt, + text: comment.body, + upvotes: commentUpvotes, + upvoterUsernames: [], + downvotes: commentDownvotes, + downvoterUsernames: [] + }); + + await addOrUpdateUser(comment.owner.display_name, { + points: comment.owner.reputation + }); + } + }) + ); + + logOrDebug()( + `added ${ + randomComments.length - randomCommentsLength + } of ${ + comments.items.length + } received comments to local storage for catch-all question` + ); + + logOrDebug()( + `---\nthere are now ${randomComments.length} total of ${totalDesiredComments} wanted random comments stored\n---` + ); + + if (!comments.has_more) { + logOrDebug().warn( + 'somehow exhausted all comments in the StackExchange API?!' + ); + break; + } + } + })() + ]); + + // ? Ensure answers and comments are in insertion order (oldest first) + randomAnswers.sort((a, b) => a.createdAt - b.createdAt); + randomComments.sort((a, b) => a.createdAt - b.createdAt); + + data.catchallQuestion = { + _id: newQuestionId, + title: + 'What are the best answers and comments you can come up with?', + 'title-lowercase': + 'what are the best answers and comments you can come up with?', + creator: 'Hordak', + createdAt: questionCreatedAt - 10 ** 6, + status: 'protected', + text: '**Hello, world!** What are some of the best random answers, question comments, and answer comments you can come up with? Post below.', + upvotes: 15, + upvoterUsernames: [], + downvotes: 2, + downvoterUsernames: [], + hasAcceptedAnswer: false, + views: 1024, + answers: collectAllQuestionAnswersCount, + answerItems: randomAnswers.slice( + 0, + collectAllQuestionAnswersCount + ), + comments: collectAllQuestionCommentsCount, + commentItems: randomComments.slice( + 0, + collectAllQuestionCommentsCount + ), + sorter: { + uvc: 15 + 1024 + collectAllQuestionCommentsCount, + uvac: + 15 + + 1024 + + collectAllQuestionAnswersCount + + collectAllQuestionCommentsCount + } + }; + + logOrDebug()( + `added ${collectAllQuestionCommentsCount} random comments to catch-all question` + ); + + if (randomAnswers[0]) { + randomAnswers[0].commentItems = randomComments.slice( + collectAllQuestionCommentsCount, + totalDesiredComments + ); + + logOrDebug()( + `added ${ + totalDesiredComments - collectAllQuestionCommentsCount + } random comments to catch-all question's first answer` + ); + } else { + logOrDebug().warn('catch-all question has no answers?!'); + } + + logOrDebug().extend('cached')( + '\n>>> added catch-all question to cache <<<\n\n' + ); + + await addOrUpdateUser('Hordak', { + points: 1234567, + newQuestionId + }); + } + })() + ]); + + if (data.catchallQuestion) { + data.questions.push(data.catchallQuestion); + delete data.catchallQuestion; + } else { + throw new GuruMeditationError('missing catchall question'); + } + + // ? Ensure questions are in insertion order (oldest first) + data.questions.sort((a, b) => a.createdAt - b.createdAt); + + data.cache.complete = true; + } + } catch (e) { + logOrDebug().error(e); + + queue.immediatelyStopProcessingRequestQueue(); + + logOrDebug()('interrupted queue stats: %O', queue.getStats()); + logOrDebug()('incomplete cache stats: %O', getDataStats(data)); + + await getPrompter(process.env.TEST_PROMPTER_ERRORHANDLER) + .prompt<{ action: string; token: string }>([ + { + name: 'action', + message: 'what now?', + type: 'list', + choices: [ + { + name: 'attempt to continue using a custom token', + value: 'hit-custom' + }, + { + name: 'commit incomplete cache data to database', + value: 'commit' + }, + { + name: 'save incomplete cache data to disk and exit', + value: 'exit-save' + }, + { name: 'exit without saving any data', value: 'exit' } + ] + }, + { + name: 'token', + message: 'enter new token', + type: 'input', + when: (answers) => answers.action.includes('custom') + } + ]) + .then(async (answers) => { + if (answers.action.startsWith('exit')) { + if (answers.action == 'exit-save') { + await cacheDataToDisk(data); + } + + logOrDebug()('execution interrupted'); + process.exit(1); + } + + if (answers.action.startsWith('commit')) { + await commitApiDataToDb(data); + + logOrDebug()('execution interrupted'); + process.exit(1); + } + + interrogateApi = answers.action.startsWith('hit'); + usedStackExAuthKey = answers.token ?? usedStackExAuthKey; + queue.beginProcessingRequestQueue(); + }); + } + } + } finally { + if (queue.isProcessingRequestQueue) { + queue.gracefullyStopProcessingRequestQueue(); + } + + logOrDebug()('waiting for request queue to terminate...'); + await queue.waitForQueueProcessingToStop(); + logOrDebug()('request queue terminated'); + } + + logOrDebug()('interrogation complete'); + logOrDebug()('final queue stats: %O', queue.getStats()); + } + + if (process.env.TEST_SKIP_REQUESTS) { + debug.warn( + 'saw debug env var TEST_SKIP_REQUESTS. Post-request tasks will be skipped' + ); + } else { + logOrDebug()('final cache stats: %O', getDataStats(data)); + await cacheDataToDisk(data); + + await getPrompter(process.env.TEST_PROMPTER_FINALIZER) + .prompt<{ action: string; token: string }>([ + { + name: 'action', + message: 'what now?', + type: 'list', + choices: [ + { + name: 'commit results to database', + value: 'commit' + }, + { + name: 'commit results to database (include dummy root data)', + value: 'commit-root' + }, + { name: 'exit', value: 'exit' } + ] + }, + { + name: 'token', + message: 'enter new token', + type: 'input', + when: (answers) => answers.action.includes('custom') + } + ]) + .then(async (answers) => { + if (!data) { + throw new GuruMeditationError('data cannot be null'); + } + + if (answers.action == 'exit-save') { + await cacheDataToDisk(data); + } + + if (answers.action.startsWith('commit')) { + if (answers.action == 'commit-root') { + await commitRootDataToDb(data); + } + + await commitApiDataToDb(data); + } + }); + } + + logOrDebug()('execution complete'); + process.exit(0); + } catch (e) { + throw new AppError(`${e}`); + } +}; + +export type Data = { + questions: InternalQuestion[]; + catchallQuestion?: InternalQuestion; + users: InternalUser[]; + cache: { + complete: boolean; + }; +}; + +export default invoked().catch((e: Error) => { + log.error(e.message); + process.exit(2); +}); diff --git a/external-scripts/log-stats.ts b/external-scripts/log-stats.ts new file mode 100644 index 0000000..8539512 --- /dev/null +++ b/external-scripts/log-stats.ts @@ -0,0 +1,650 @@ +import jsonFile from 'jsonfile'; + +import { + AppError, + GuruMeditationError, + InvalidAppEnvironmentError +} from 'named-app-errors'; + +import { debugNamespace as namespace } from 'universe/constants'; +import { getEnv } from 'universe/backend/env'; + +import { getDb } from 'multiverse/mongo-schema'; +import { debugFactory } from 'multiverse/debug-extended'; + +const debugNamespace = `${namespace}:log-stats`; +const cachePath = `${__dirname}/log-stats-cache.json`; + +const log = debugFactory(debugNamespace); +const debug = debugFactory(`${debugNamespace}:debug`); + +// eslint-disable-next-line no-console +log.log = console.info.bind(console); + +// ? Ensure this next line survives Webpack +if (!globalThis.process.env.DEBUG && getEnv().NODE_ENV != 'test') { + debugFactory.enable( + `${debugNamespace},${debugNamespace}:*,-${debugNamespace}:debug` + ); +} + +/** + * Pores over request-log entries and drops the ban hammer on rule breaking + * clients. + */ +const invoked = async () => { + try { + if (!getEnv().MONGODB_URI) { + throw new InvalidAppEnvironmentError( + 'MONGODB_URI must be a valid mongodb connection string' + ); + } + + log('compiling statistics...'); + + const previousResults: Record = await (async () => { + try { + debug(`reading in results from cache at ${cachePath}`); + return await jsonFile.readFile(cachePath); + } catch { + return {}; + } + })(); + + debug('previous results: %O', previousResults); + + const db = await getDb({ name: 'root' }); + const requestLogDb = db.collection('request-log'); + const limitedLogDb = db.collection('limited-log'); + + const requestLogPipeline = [ + { + $facet: { + // ? Select the latest timestamp per (header) + group_header_x_latest: [ + { + $group: { + _id: '$header', + latestAt: { $max: '$createdAt' } + } + } + ], + // ? Count requests per (header) + group_header: [ + { + $group: { + _id: '$header', + totalRequests: { $sum: 1 }, + preflightRequests: { + $sum: { + $cond: { if: { $eq: ['$method', 'OPTIONS'] }, then: 1, else: 0 } + } + }, + normalRequests: { + $sum: { + $cond: { if: { $eq: ['$method', 'OPTIONS'] }, then: 0, else: 1 } + } + } + } + } + ], + // ? Count requests per (header, ip) + group_header_x_ip: [ + { + $group: { + _id: { header: '$header', ip: '$ip' }, + requests: { $sum: 1 } + } + }, + { + $group: { + _id: '$_id.header', + ips: { + $push: { + ip: '$_id.ip', + requests: '$requests' + } + } + } + } + ], + // ? Count requests per (header, method) + group_header_x_method: [ + { + $group: { + _id: { header: '$header', method: '$method' }, + requests: { $sum: 1 } + } + }, + { + $group: { + _id: '$_id.header', + methods: { + $push: { + method: '$_id.method', + requests: '$requests' + } + } + } + } + ], + // ? Count requests per (header, status) + group_header_x_status: [ + { + $group: { + _id: { header: '$header', status: '$resStatusCode' }, + requests: { $sum: 1 } + } + }, + { + $group: { + _id: '$_id.header', + statuses: { + $push: { + status: '$_id.status', + requests: '$requests' + } + } + } + } + ], + // ? Count requests per (header, endpoint) + group_header_x_endpoints: [ + { + $group: { + _id: { + header: '$header', + endpoint: { + $cond: { + if: { $not: ['$endpoint'] }, + then: '', + else: '$endpoint' + } + } + }, + requests: { $sum: 1 } + } + }, + { + $group: { + _id: '$_id.header', + endpoints: { + $push: { + endpoint: '$_id.endpoint', + requests: '$requests' + } + } + } + } + ] + } + }, + + // ? Merge stats into per-header documents + { + $project: { + headerStats: { + $concatArrays: [ + '$group_header_x_latest', + '$group_header', + '$group_header_x_ip', + '$group_header_x_method', + '$group_header_x_status', + '$group_header_x_endpoints' + ] + } + } + }, + { + $unwind: '$headerStats' + }, + { + $group: { + _id: '$headerStats._id', + stats: { $mergeObjects: '$$ROOT.headerStats' } + } + }, + { + $replaceRoot: { newRoot: '$stats' } + }, + + // ? Sort results by greatest number of requests + { + $sort: { normalRequests: -1 } + }, + + // ? Add relevant fields + { + $addFields: { + header: '$_id', + token: { $arrayElemAt: [{ $split: ['$_id', ' '] }, 1] } + } + }, + + // ? Cross-reference tokens with identity data + { + $lookup: { + from: 'auth', + localField: 'token', + foreignField: 'token.bearer', + as: 'auth' + } + }, + { + $replaceRoot: { + newRoot: { + $mergeObjects: [{ $arrayElemAt: ['$auth', 0] }, '$$ROOT'] + } + } + }, + + // ? Beautify output + { + $addFields: { + owner: { $ifNull: ['$attributes.owner', ''] }, + token: { + $cond: { + if: { $gt: ['$attributes.owner', null] }, + then: { $ifNull: ['$token', ''] }, + else: '' + } + } + } + }, + { + $project: { + _id: false, + attributes: false, + auth: false, + scheme: false + } + } + ]; + + // ? Calculates duration percentiles: fastest, 50%, 90%, 95%, 99%, 99.9%, + // ? and slowest + const requestPercentilePipeline = [ + { $match: { durationMs: { $exists: true } } }, + { $sort: { durationMs: 1 } }, + { + $group: { + _id: null, + durations: { $push: '$durationMs' } + } + }, + { + $project: { + _id: false, + fastest: { $arrayElemAt: ['$durations', 0] }, + percentile_50: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.5, { $size: '$durations' }] } } + ] + }, + percentile_90: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.9, { $size: '$durations' }] } } + ] + }, + percentile_95: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.95, { $size: '$durations' }] } } + ] + }, + percentile_99: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.99, { $size: '$durations' }] } } + ] + }, + percentile_999: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.999, { $size: '$durations' }] } } + ] + }, + percentile_9999: { + $arrayElemAt: [ + '$durations', + { $floor: { $multiply: [0.9999, { $size: '$durations' }] } } + ] + }, + slowest: { $arrayElemAt: ['$durations', -1] } + } + } + ]; + + const limitedLogPipeline = [ + { + $project: { + _id: false, + header: true, + token: { $arrayElemAt: [{ $split: ['$header', ' '] }, 1] }, + ip: true, + until: true + } + }, + { + $lookup: { + from: 'auth', + localField: 'token', + foreignField: 'token.bearer', + as: 'auth' + } + }, + { + $replaceRoot: { + newRoot: { + $mergeObjects: [{ $arrayElemAt: ['$auth', 0] }, '$$ROOT'] + } + } + }, + { + $project: { + owner: { $ifNull: ['$attributes.owner', ''] }, + token: { + $cond: { + if: { $gt: ['$attributes.owner', null] }, + then: { $ifNull: ['$token', ''] }, + else: '' + } + }, + header: true, + ip: true, + until: true, + dummy: true + } + }, + { + $sort: { until: -1, _id: -1 } + }, + { + $project: { + _id: false, + dummy: false + } + } + ]; + + debug('request-log aggregation pipeline: %O', requestLogPipeline); + debug('limited-log aggregation pipeline: %O', limitedLogPipeline); + + const byRequests = (a: { requests: number }, b: { requests: number }) => + b.requests - a.requests; + + const requestLogCursor = requestLogDb.aggregate<{ + owner: string; + token: string; + header: string | null; + normalRequests: number; + preflightRequests: number; + totalRequests: number; + ips: { ip: string; requests: number }[]; + statuses: { status: number; requests: number }[]; + methods: { method: string; requests: number }[]; + endpoints: { endpoint: string; requests: number }[]; + latestAt: number; + }>(requestLogPipeline); + + const percentileCursor = requestLogDb.aggregate<{ + fastest: number; + percentile_50: number; + percentile_90: number; + percentile_95: number; + percentile_99: number; + percentile_999: number; + percentile_9999: number; + slowest: number; + }>(requestPercentilePipeline); + + const limitedLogCursor = limitedLogDb.aggregate<{ + owner?: string; + token?: string; + header?: string | null; + ip?: string; + until: number; + }>(limitedLogPipeline); + + const [requestLogStats, requestPercentiles, limitedLogStats] = await Promise.all([ + requestLogCursor.toArray(), + percentileCursor.next(), + limitedLogCursor.toArray() + ]); + + await Promise.all([ + requestLogCursor.close(), + percentileCursor.close(), + limitedLogCursor.close() + ]); + + const chalk = (await import('chalk')).default; + const outputStrings: string[] = []; + + const addAuthInfo = ( + owner: string, + token: string, + header: string | null, + error = false + ) => { + outputStrings.push( + ` owner: ${ + owner == '' + ? chalk.gray(owner) + : chalk[error ? 'red' : 'green'].bold(owner) + }` + ); + + outputStrings.push( + ` token: ${token == '' ? chalk.gray(token) : token}` + ); + + outputStrings.push(` header: ${!header ? chalk.gray(header) : header}`); + }; + + debug('compiling output'); + debug(`requestLogStats.length=${requestLogStats.length}`); + debug(`limitedLogStats.length=${limitedLogStats.length}`); + debug(`requestPercentiles=${requestPercentiles}`); + + outputStrings.push(`\n::REQUEST LOG::${requestLogStats.length ? '\n' : ''}`); + + if (!requestLogStats.length) { + outputStrings.push(' '); + Object.keys(previousResults).forEach((k) => { + delete previousResults[k]; + }); + } else { + requestLogStats.forEach( + ({ + owner, + token, + header, + normalRequests, + preflightRequests, + totalRequests, + ips, + methods, + endpoints, + statuses, + latestAt + }) => { + addAuthInfo(owner, token, header); + + const headerString = String(header); + const delta = previousResults[headerString] + ? normalRequests - previousResults[headerString] + : null; + + previousResults[headerString] = normalRequests; + + outputStrings.push( + ` total requests: ${ + preflightRequests + ? `${normalRequests} (+${preflightRequests} preflight, ${totalRequests} total)` + : totalRequests + }${ + delta !== null + ? chalk.yellow(` (Δ${delta >= 0 ? `+${delta}` : delta})`) + : '' + }` + ); + + outputStrings.push( + ` most recent request: ${new Date(latestAt).toLocaleString()}` + ); + + outputStrings.push(' requests by ip:'); + + ips.forEach(({ ip, requests: requestsFromIp }) => + outputStrings.push(` ${ip} - ${requestsFromIp} requests`) + ); + + outputStrings.push(' requests by HTTP status code:'); + + statuses + .sort(byRequests) + .forEach(({ status, requests: requestResponseStatus }) => { + const str = ` ${status} - ${requestResponseStatus} requests`; + outputStrings.push(status == 429 ? chalk.red(str) : str); + outputStrings.push(); + }); + + outputStrings.push(' requests by HTTP method:'); + + methods + .sort(byRequests) + .forEach(({ method, requests: requestsOfMethod }) => { + const str = ` ${method} - ${requestsOfMethod} requests`; + outputStrings.push(method == 'OPTIONS' ? chalk.gray(str) : str); + }); + + outputStrings.push(' requests by endpoint:'); + + endpoints + .sort(byRequests) + .forEach(({ endpoint, requests: requestsToEndpoint }) => { + const str = ` ${endpoint} - ${requestsToEndpoint} requests`; + outputStrings.push(endpoint == '' ? chalk.gray(str) : str); + }); + + outputStrings.push(''); + } + ); + + outputStrings.push(' :PERCENTILES:'); + + outputStrings.push( + ` fastest: ${ + requestPercentiles?.fastest !== undefined + ? `${requestPercentiles.fastest}ms` + : '' + }` + ); + + outputStrings.push( + ` 50%<=: ${ + requestPercentiles?.percentile_50 !== undefined + ? `${requestPercentiles.percentile_50}ms` + : '' + }` + ); + + outputStrings.push( + ` 90%<=: ${ + requestPercentiles?.percentile_90 !== undefined + ? `${requestPercentiles.percentile_90}ms` + : '' + }` + ); + + outputStrings.push( + ` 95%<=: ${ + requestPercentiles?.percentile_95 !== undefined + ? `${requestPercentiles.percentile_95}ms` + : '' + }` + ); + + outputStrings.push( + ` 99%<=: ${ + requestPercentiles?.percentile_99 !== undefined + ? `${requestPercentiles.percentile_99}ms` + : '' + }` + ); + + outputStrings.push( + ` 99.9%<=: ${ + requestPercentiles?.percentile_999 !== undefined + ? `${requestPercentiles.percentile_999}ms` + : '' + }` + ); + + outputStrings.push( + ` 99.99%<=: ${ + requestPercentiles?.percentile_9999 !== undefined + ? `${requestPercentiles.percentile_9999}ms` + : '' + }` + ); + + outputStrings.push( + ` slowest: ${ + requestPercentiles?.slowest !== undefined + ? `${requestPercentiles.slowest}ms` + : '' + }` + ); + } + + outputStrings.push(`\n::LIMIT LOG::${limitedLogStats.length ? '\n' : ''}`); + + if (!limitedLogStats.length) { + outputStrings.push(' '); + } else { + limitedLogStats.forEach(({ owner, token, header, ip, until }) => { + const now = Date.now(); + const banned = until > now; + + if (owner && token && header !== undefined) { + addAuthInfo(owner, token, header, banned); + } else if (ip) { + outputStrings.push(` ip: ${ip}`); + } else { + throw new GuruMeditationError('encountered malformed limit log data'); + } + + outputStrings.push( + ` status: ${ + !banned + ? chalk.gray('expired') + : chalk.red.bold( + `banned until ${new Date(until - now).toLocaleString()}` + ) + }` + ); + + outputStrings.push(''); + }); + } + + log(outputStrings.join('\n')); + + debug(`writing out results to cache at ${cachePath}`); + await jsonFile.writeFile(cachePath, previousResults); + + log('execution complete'); + process.exit(0); + } catch (e) { + throw new AppError(`${e}`); + } +}; + +export default invoked().catch((e: Error) => { + log.error(e.message); + process.exit(2); +}); diff --git a/external-scripts/prune-data.ts b/external-scripts/prune-data.ts new file mode 100644 index 0000000..20a5fc7 --- /dev/null +++ b/external-scripts/prune-data.ts @@ -0,0 +1,269 @@ +import { toss } from 'toss-expression'; + +import { + AppError, + GuruMeditationError, + InvalidAppEnvironmentError +} from 'named-app-errors'; + +import { debugNamespace as namespace } from 'universe/constants'; +import { getEnv } from 'universe/backend/env'; +import { deleteUser } from 'universe/backend'; + +import { debugFactory } from 'multiverse/debug-extended'; +import { getDb } from 'multiverse/mongo-schema'; + +import type { Document, ObjectId, WithId } from 'mongodb'; +import type { Promisable } from 'type-fest'; +import type { InternalUser } from 'universe/backend/db'; + +const debugNamespace = `${namespace}:prune-data`; + +const log = debugFactory(debugNamespace); +const debug = debugFactory(`${debugNamespace}:debug`); + +type DataLimit = { + limit: { maxBytes: number } | { maxDocuments: number }; + orderBy?: string; + deleteFn?: (thresholdEntry: WithId) => Promisable; +}; + +// eslint-disable-next-line no-console +log.log = console.info.bind(console); + +// ? Ensure this next line survives Webpack +if (!globalThis.process.env.DEBUG && getEnv().NODE_ENV != 'test') { + debugFactory.enable( + `${debugNamespace},${debugNamespace}:*,-${debugNamespace}:debug` + ); +} + +// * Add new env var configurations here +const getDbCollectionLimits = (env: ReturnType) => { + const limits: Record> = { + root: { + 'request-log': { + limit: { + maxBytes: + env.PRUNE_DATA_MAX_LOGS_BYTES && env.PRUNE_DATA_MAX_LOGS_BYTES > 0 + ? env.PRUNE_DATA_MAX_LOGS_BYTES + : toss( + new InvalidAppEnvironmentError( + 'PRUNE_DATA_MAX_LOGS_BYTES must be greater than zero' + ) + ) + } + }, + 'limited-log': { + limit: { + maxBytes: + env.PRUNE_DATA_MAX_BANNED_BYTES && env.PRUNE_DATA_MAX_BANNED_BYTES > 0 + ? env.PRUNE_DATA_MAX_BANNED_BYTES + : toss( + new InvalidAppEnvironmentError( + 'PRUNE_DATA_MAX_BANNED_BYTES must be greater than zero' + ) + ) + } + } + }, + app: { + mail: { + limit: { + maxBytes: + env.PRUNE_DATA_MAX_MAIL_BYTES && env.PRUNE_DATA_MAX_MAIL_BYTES > 0 + ? env.PRUNE_DATA_MAX_MAIL_BYTES + : toss( + new InvalidAppEnvironmentError( + 'PRUNE_DATA_MAX_MAIL_BYTES must be greater than zero' + ) + ) + } + }, + questions: { + limit: { + maxBytes: + env.PRUNE_DATA_MAX_QUESTIONS_BYTES && + env.PRUNE_DATA_MAX_QUESTIONS_BYTES > 0 + ? env.PRUNE_DATA_MAX_QUESTIONS_BYTES + : toss( + new InvalidAppEnvironmentError( + 'PRUNE_DATA_MAX_QUESTIONS_BYTES must be greater than zero' + ) + ) + } + }, + users: { + limit: { + maxBytes: + env.PRUNE_DATA_MAX_USERS_BYTES && env.PRUNE_DATA_MAX_USERS_BYTES > 0 + ? env.PRUNE_DATA_MAX_USERS_BYTES + : toss( + new InvalidAppEnvironmentError( + 'PRUNE_DATA_MAX_USERS_BYTES must be greater than zero' + ) + ) + }, + async deleteFn(thresholdEntry) { + const users = (await getDb({ name: 'app' })).collection( + 'users' + ); + + const usernames = ( + await users.find({ _id: { $lte: thresholdEntry._id } }).toArray() + ).map((user) => user.username); + + await Promise.all(usernames.map((username) => deleteUser({ username }))); + return usernames.length; + } + } + } + }; + + debug('limits: %O', limits); + return limits; +}; + +/** + * Runs maintenance on the database, ensuring collections do not grow too large. + */ +const invoked = async () => { + try { + const limits = getDbCollectionLimits(getEnv()); + + await Promise.all( + Object.entries(limits).map(async ([dbName, dbLimitsObj]) => { + debug(`using db "${dbName}"`); + const db = await getDb({ name: dbName }); + + await Promise.all( + Object.entries(dbLimitsObj).map(async ([collectionName, colLimitsObj]) => { + const name = `${dbName}.${collectionName}`; + debug(`collection "${name}" is a target for pruning`); + + const subLog = log.extend(name); + const collection = db.collection(collectionName); + const totalCount = await collection.countDocuments(); + + const { + limit: limitSpec, + orderBy = '_id', + deleteFn = undefined + } = colLimitsObj; + + const pruneCollectionAtThreshold = async ( + thresholdEntry: WithId | null, + deleteFn: DataLimit['deleteFn'], + pruneMessage: string, + noPruneMessage: string + ) => { + if (thresholdEntry) { + debug(`determined threshold entry: ${thresholdEntry._id}`); + let deletedCount: number; + + if (deleteFn) { + debug('using custom pruning strategy'); + deletedCount = await deleteFn(thresholdEntry); + } else { + debug('using default pruning strategy'); + deletedCount = ( + await collection.deleteMany({ + [orderBy]: { $lte: thresholdEntry[orderBy] } + }) + ).deletedCount; + } + + subLog(`${deletedCount} pruned (${pruneMessage})`); + } else { + subLog(`0 pruned (${noPruneMessage})`); + } + }; + + if ('maxBytes' in limitSpec) { + debug('limiting metric: document size'); + + const { maxBytes } = limitSpec; + + debug(`sorting ${name} by "${orderBy}"`); + debug( + `iteratively summing document size until limit is reached (${maxBytes} bytes)` + ); + + // ? Use $bsonSize operator to sort by most recent first, then sum + // ? them until either documents are exhausted or total size > + // ? limit, then delete the (old) documents that exist beyond the + // ? limit. + const cursor = collection.aggregate>([ + { $sort: { [orderBy]: -1 } }, + { $project: { _id: true, size: { $bsonSize: '$$ROOT' } } } + ]); + + let totalSizeBytes = 0; + let thresholdId: ObjectId | null = null; + let foundThresholdId = false; + + (await cursor.toArray()).forEach(({ _id, size }) => { + if (!thresholdId) { + thresholdId = _id; + } + + totalSizeBytes += size; + + if (!foundThresholdId) { + if (totalSizeBytes > maxBytes) { + foundThresholdId = true; + } else { + thresholdId = _id; + } + } + }); + + await pruneCollectionAtThreshold( + foundThresholdId && thresholdId ? { _id: thresholdId } : null, + deleteFn, + `${totalCount}, ${totalSizeBytes}b > ${maxBytes}b`, + `${totalCount}, ${totalSizeBytes}b <= ${maxBytes}b` + ).then(() => cursor.close()); + } else { + debug('limiting metric: document count'); + + const { maxDocuments } = limitSpec; + + if (!maxDocuments) { + throw new GuruMeditationError('invalid limit spec'); + } + + debug(`sorting ${name} by "${orderBy}"`); + debug(`skipping ${maxDocuments} entries"`); + + const cursor = collection + .find() + .sort({ [orderBy]: -1 }) + .skip(maxDocuments) + .limit(1); + + const thresholdEntry = await cursor.next(); + + await pruneCollectionAtThreshold( + thresholdEntry, + deleteFn, + `${totalCount} > ${maxDocuments}`, + `${totalCount} <= ${maxDocuments}` + ).then(() => cursor.close()); + } + }) + ); + }) + ); + + log('execution complete'); + process.exit(0); + } catch (e) { + throw new AppError(`${e}`); + } +}; + +export default invoked().catch((e: Error) => { + log.error(e.message); + process.exit(2); +}); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..d324a5a --- /dev/null +++ b/jest.config.js @@ -0,0 +1,35 @@ +'use strict'; + +module.exports = { + restoreMocks: true, + resetMocks: true, + testEnvironment: 'node', + testRunner: 'jest-circus/runner', + // ? 24 hours so MMS and other tools don't choke during debugging + testTimeout: 1000 * 60 * 60 * 24, + verbose: false, + testPathIgnorePatterns: ['/node_modules/'], + // ! If changed, also update these aliases in tsconfig.json, + // ! webpack.config.js, next.config.ts, and .eslintrc.js + moduleNameMapper: { + '^universe/(.*)$': '/src/$1', + '^multiverse/(.*)$': '/lib/$1', + '^testverse/(.*)$': '/test/$1', + '^externals/(.*)$': '/external-scripts/$1', + '^types/(.*)$': '/types/$1', + '^package$': '/package.json', + // ? These are used at various points (including at compile time by + // ? Next.js) to get mongo schema configuration and/or test dummy data. + // ! Must be defined if using @xunnamius/mongo-schema + '^configverse/get-schema-config$': '/src/backend/db.ts', + // ! Must be defined if using @xunnamius/mongo-test + '^configverse/get-dummy-data$': '/test/db.ts' + }, + setupFilesAfterEnv: ['./test/setup.ts'], + collectCoverageFrom: [ + 'src/**/*.ts*', + 'lib/**/*.ts*', + 'external-scripts/**/*.ts*', + '!**/*.test.*' + ] +}; diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..08a7a6c --- /dev/null +++ b/lib/README.md @@ -0,0 +1,5 @@ +# Libraries + +This folder holds code frequently passed around between projects. If passed +around enough, it will be turned into an npm module. Until then, they get +dogfooded here as multiverse libs. diff --git a/lib/debug-extended/index.ts b/lib/debug-extended/index.ts new file mode 100644 index 0000000..119bfff --- /dev/null +++ b/lib/debug-extended/index.ts @@ -0,0 +1,47 @@ +import getDebugger from 'debug'; +import type { Debug, Debugger } from 'debug'; + +export type { Debug, Debugger }; + +/** + * A Debug factory interface that returns `ExtendedDebugger` instances. + */ +export interface ExtendedDebug extends Debug { + (...args: Parameters): ExtendedDebugger; +} + +/** + * A Debugger interface extended with convenience methods. + */ +export interface ExtendedDebugger extends Debugger { + error: Debugger; + warn: Debugger; + extend: (...args: Parameters) => ExtendedDebugger; +} + +/** + * An `ExtendedDebug` instance that returns an `ExtendedDebugger` instance via + * `extendDebugger`. + */ +const debugFactory = ((...args: Parameters) => { + return extendDebugger(getDebugger(...args)); +}) as ExtendedDebug; + +Object.assign(debugFactory, getDebugger); + +export { debugFactory }; + +/** + * Extends a `Debugger` instance with several convenience methods, returning + * what would more accurately be called an `ExtendedDebugger` instance. + */ +export function extendDebugger(instance: Debugger) { + const extend = instance.extend.bind(instance); + const finalInstance = instance as ExtendedDebugger; + + finalInstance.error = finalInstance.extend(''); + finalInstance.warn = finalInstance.extend(''); + finalInstance.extend = (...args) => extendDebugger(extend(...args)); + + return finalInstance; +} diff --git a/lib/debug-extended/package.json b/lib/debug-extended/package.json new file mode 100644 index 0000000..dd44c03 --- /dev/null +++ b/lib/debug-extended/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/debug-extended" +} diff --git a/lib/debug-extended/unit.test.ts b/lib/debug-extended/unit.test.ts new file mode 100644 index 0000000..7101a27 --- /dev/null +++ b/lib/debug-extended/unit.test.ts @@ -0,0 +1,38 @@ +import { debugFactory, extendDebugger } from 'multiverse/debug-extended'; +import { debug as oldDebug } from 'debug'; + +describe('::debugFactory', () => { + it('returns ExtendedDebugger instances', async () => { + expect.hasAssertions(); + + const debug = debugFactory('namespace'); + + expect(debug).toHaveProperty('error'); + expect(debug).toHaveProperty('warn'); + expect(debug).toHaveProperty('extend'); + }); + + it('returns an instance with error, warn, and extend', async () => { + expect.hasAssertions(); + + const debug = debugFactory('namespace'); + const extended = debug.extend('extended'); + + expect(extended).toHaveProperty('error'); + expect(extended).toHaveProperty('warn'); + expect(extended).toHaveProperty('extend'); + }); +}); + +describe('::extendDebugger', () => { + it('returns an instance with error, warn, and extend', async () => { + expect.hasAssertions(); + + const debug = oldDebug('namespace'); + const extended = extendDebugger(debug); + + expect(extended).toHaveProperty('error'); + expect(extended).toHaveProperty('warn'); + expect(extended).toHaveProperty('extend'); + }); +}); diff --git a/lib/find-project-root/index.ts b/lib/find-project-root/index.ts new file mode 100644 index 0000000..65c23e3 --- /dev/null +++ b/lib/find-project-root/index.ts @@ -0,0 +1,33 @@ +import { sync as findUpSync } from 'find-up'; +import { GuruMeditationError } from 'named-app-errors'; +import { toss } from 'toss-expression'; +import { dirname } from 'path'; + +const memory = { rootPath: null } as { rootPath: string | null }; + +/** + * Overwrite the memoized findProjectRoot result with an explicit value. Useful + * in testing environments and complex setups. + */ +export function setProjectRoot(rootPath: string | null) { + memory.rootPath = rootPath; +} + +/** + * Synchronously finds the root of a project by walking up parent + * directories beginning at `process.cwd()` and looking for certain files/dirs. + */ +export function findProjectRoot() { + return (memory.rootPath = + memory.rootPath ?? + dirname( + findUpSync('next.config.js') || + findUpSync('projector.config.js') || + findUpSync('.git') || + toss( + new GuruMeditationError( + 'could not find project root: none of "next.config.js", "projector.config.js", nor ".git" were found in any ancestor directory' + ) + ) + )); +} diff --git a/lib/find-project-root/package.json b/lib/find-project-root/package.json new file mode 100644 index 0000000..2dee606 --- /dev/null +++ b/lib/find-project-root/package.json @@ -0,0 +1,4 @@ +{ + "name": "@xunnamius/find-project-root", + "version": "0.0.0-development" +} diff --git a/lib/find-project-root/unit.test.ts b/lib/find-project-root/unit.test.ts new file mode 100644 index 0000000..816048b --- /dev/null +++ b/lib/find-project-root/unit.test.ts @@ -0,0 +1,59 @@ +import { findProjectRoot, setProjectRoot } from 'multiverse/find-project-root'; +import { sync as findUpSync } from 'find-up'; +import { asMockedFunction } from '@xunnamius/jest-types'; + +jest.mock('find-up'); + +const mockFindUpSync = asMockedFunction(findUpSync); + +describe('::findProjectRoot', () => { + it('find the project root unless all expected paths not encountered', async () => { + expect.hasAssertions(); + + mockFindUpSync.mockImplementationOnce(() => '/some/x/path/next.config.js'); + + expect(findProjectRoot()).toBe('/some/x/path'); + + setProjectRoot(null); + mockFindUpSync.mockImplementationOnce(() => undefined); + mockFindUpSync.mockImplementationOnce(() => '/some/y/path/projector.config.js'); + + expect(findProjectRoot()).toBe('/some/y/path'); + + setProjectRoot(null); + mockFindUpSync.mockImplementationOnce(() => undefined); + mockFindUpSync.mockImplementationOnce(() => undefined); + mockFindUpSync.mockImplementationOnce(() => '/some/z/path/.git'); + + expect(findProjectRoot()).toBe('/some/z/path'); + + setProjectRoot(null); + mockFindUpSync.mockImplementation(() => undefined); + + expect(() => findProjectRoot()).toThrow('could not find project root'); + }); + + it('memoizes the result', async () => { + expect.hasAssertions(); + + mockFindUpSync.mockImplementationOnce(() => '/some/x/path/next.config.js'); + expect(findProjectRoot()).toBe('/some/x/path'); + + mockFindUpSync.mockImplementation(() => undefined); + expect(findProjectRoot()).toBe('/some/x/path'); + }); +}); + +describe('::setProjectRoot', () => { + it('controls output of findProjectRoot', async () => { + expect.hasAssertions(); + + mockFindUpSync.mockImplementationOnce(() => '/some/w/path/next.config.js'); + + setProjectRoot('/some/x/path/next.config.js'); + expect(findProjectRoot()).toBe('/some/x/path/next.config.js'); + expect(findProjectRoot()).toBe('/some/x/path/next.config.js'); + setProjectRoot(null); + expect(findProjectRoot()).toBe('/some/w/path'); + }); +}); diff --git a/lib/is-plain-object/index.ts b/lib/is-plain-object/index.ts new file mode 100644 index 0000000..f01eea0 --- /dev/null +++ b/lib/is-plain-object/index.ts @@ -0,0 +1,5 @@ +import { isPlainObject as originalIsPlainObject } from 'is-plain-object'; + +export function isPlainObject(obj: unknown): obj is Record { + return originalIsPlainObject(obj); +} diff --git a/lib/is-plain-object/package.json b/lib/is-plain-object/package.json new file mode 100644 index 0000000..e91347e --- /dev/null +++ b/lib/is-plain-object/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/is-plain-object" +} diff --git a/lib/jest-mock-date/index.ts b/lib/jest-mock-date/index.ts new file mode 100644 index 0000000..2e1c47d --- /dev/null +++ b/lib/jest-mock-date/index.ts @@ -0,0 +1,21 @@ +/** + * The mock Date.now() value returned after calling `useMockDateNow`. + */ +export const mockDateNowMs = Date.now(); + +/** + * Sets up a Jest spy on the `Date` object's `now` method such that it returns + * `mockNow` or `mockDateNowMs` (default) rather than the actual date. If you + * want to restore the mock, you will have to do so manually (or use Jest + * configuration to do so automatically). + * + * This is useful when testing against/playing with dummy data containing values + * derived from the current time (i.e. unix epoch). + */ +export function useMockDateNow(options?: { mockNow?: number }) { + beforeEach(() => { + jest + .spyOn(Date, 'now') + .mockImplementation(() => options?.mockNow || mockDateNowMs); + }); +} diff --git a/lib/jest-mock-date/package.json b/lib/jest-mock-date/package.json new file mode 100644 index 0000000..0706309 --- /dev/null +++ b/lib/jest-mock-date/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/jest-mock-date" +} diff --git a/lib/json-node-fetch/index.ts b/lib/json-node-fetch/index.ts new file mode 100644 index 0000000..cbb316a --- /dev/null +++ b/lib/json-node-fetch/index.ts @@ -0,0 +1,360 @@ +import fetch, { FetchError, Headers, Response } from 'node-fetch'; +import { makeNamedError } from 'named-app-errors'; +import { isError } from '@xunnamius/types'; + +import type { BodyInit, RequestInit } from 'node-fetch'; +import type { JsonObject, JsonPrimitive } from 'type-fest'; + +export const JsonContentType = 'application/json' as const; + +/** + * Represents a JSON Fetch error. + */ +export class JsonFetchError< + T extends JsonObject | JsonPrimitive | undefined +> extends FetchError { + constructor( + public readonly res: Response | undefined, + public readonly json: T, + message: string + ) { + super(message, 'json-fetch-error'); + } +} +makeNamedError(JsonFetchError, 'JsonFetchError'); + +/** + * Options to configure how jsonFetch executes. + * + * @see https://github.com/node-fetch/node-fetch#options + */ +export type JsonRequestInit = Omit & { + /** + * If `true`, jsonFetch will reject when `response.ok` is not `true`; if + * `false`, `json` will be undefined and `error` will be an empty object. + * + * @default false + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/ok + */ + rejectIfNotOk?: boolean; + /** + * If `true`, jsonFetch will reject when a response is missing the + * `application/json` content-type header; if `false`, `json` will be + * undefined and `error` will be an empty object. + * + * @default false + */ + rejectIfNonJsonContentType?: boolean; + /** + * The request body to send. Automatically stringified (via `JSON.stringify`) + * if request content-type is `application/json`. + * + * Note that this type is loose enough to accept JSON objects, but if you're + * not using the `application/json` content-type when passing a JSON object as + * the body then jsonFetch will reject with an error. + */ + body?: BodyInit | JsonObject | JsonPrimitive; +}; + +/** + * The mutable default options for all `jsonFetch` calls. Keys will be + * overridden by the optional `options` object passed into each call, e.g. + * `jsonFetch(url, options)`. + * + * Note: you must use `credentials: 'include'` to include cookies with your + * requests. This is not the default setting. + * + * **WARN: this setting MUST only be used in "end-developer" source, not in + * libraries or anything that is meant to be imported into higher-order code or + * you run the risk of terrible conflicts!** + * + * @see https://github.com/node-fetch/node-fetch#options + */ +export const globalJsonRequestOptions: JsonRequestInit = { + headers: { 'content-type': JsonContentType }, + rejectIfNotOk: false, + rejectIfNonJsonContentType: false +}; + +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and the response body parsed as JSON under either `error` + * (if the response has a non-2xx status) or `json`. + * + * If the response was not received with an `application/json` content-type + * header or has a non-2xx status _and_ unparseable response body, `json` will + * be undefined and `error` will be an empty object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header or 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { + * method: 'POST', + * headers: { authorization: `Bearer ${apiKey}` }, + * body: requestData + * } + * ); + * + * if (error) { + * console.error(error?.reason ?? (res.ok + * ? 'bad json' + * : res.statusText)); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init?: Omit & { + rejectIfNotOk?: false; + rejectIfNonJsonContentType?: false; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and the response body parsed as JSON under either `error` + * (if the response has a non-2xx status) or `json`. + * + * If the response was received with a non-2xx status _and_ unparseable response + * body, `json` will be undefined and `error` will be an empty object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, or 3) the response was received with a content-type + * header other than `application/json`. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * + * try { + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { rejectIfNonJsonContentType: true } + * ); + * + * if (error) { + * console.error(error?.reason ?? res.statusText); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-json response bodies + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit & { + rejectIfNotOk?: false; + rejectIfNonJsonContentType: true; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and either the response body parsed as JSON under `json` + * or, if the response was received with a content-type header other than + * `application/json`, an empty object under `error`. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, or 3) the response was received with a non-2xx status. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * + * try { + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { rejectIfNotOk: true } + * ); + * + * if (error) { + * console.error(error?.reason ?? 'bad json'); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-2xx responses + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit & { + rejectIfNotOk: true; + rejectIfNonJsonContentType?: false; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and and the response body parsed as JSON under `json`. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, 3) the response was received with a content-type header + * other than `application/json`, or 4) the response was received with a non-2xx + * status. + * + * Hence, when jsonFetch is called in this way, `json` will always be defined + * and `error` will always be undefined. + * + * @example + * ``` + * try { + * const url = 'https://some.resource.com/data.json'; + * const { json } = await jsonFetch(url, { + * rejectIfNotOk: true, + * rejectIfNonJsonContentType: true + * }); + * doSomethingWith(json); + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-2xx/non-json response bodies + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit & { + rejectIfNotOk: true; + rejectIfNonJsonContentType: true; + } +): Promise<{ + res: Response; + json: JsonType; + error: undefined; +}>; +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>(url: string, init?: JsonRequestInit): Promise { + const parsedOptions = { + ...globalJsonRequestOptions, + ...init + }; + + if (parsedOptions.headers) { + parsedOptions.headers = new Headers(parsedOptions.headers); + + if (parsedOptions.headers.get('content-type') == JsonContentType) { + try { + parsedOptions.body = JSON.stringify(parsedOptions.body); + } catch (e) { + throw new JsonFetchError( + undefined, + undefined, + `failed to stringify request body: ${isError(e) ? e.message : e}` + ); + } + } + } + + const res = await fetch(url, parsedOptions as RequestInit); + const responseContentType = res.headers.get('content-type'); + + let parseError = ''; + let json: JsonType | undefined = undefined; + let error: Partial | undefined = undefined; + + try { + json = await res.json(); + } catch (e) { + parseError = `${isError(e) ? e.message : e}`; + } + + if (!res.ok && parsedOptions.rejectIfNotOk) { + throw new JsonFetchError( + res, + json, + `response status code ${res.status} was not in the range 200-299` + ); + } + + if ( + responseContentType != JsonContentType && + parsedOptions.rejectIfNonJsonContentType + ) { + throw new JsonFetchError( + res, + json, + `received response ${ + responseContentType + ? `with unexpected content-type "${responseContentType}"` + : 'without a content-type' + } (expected "application/json")` + ); + } + + if (parseError && responseContentType == JsonContentType) { + throw new JsonFetchError( + res, + json, + `failed to parse response body: ${parseError}` + ); + } + + if (responseContentType != JsonContentType || parseError) { + json = undefined; + error = {}; + } else if (!res.ok) { + error = json as unknown as Partial; + json = undefined; + } + + return { res, json, error }; +} diff --git a/lib/json-node-fetch/package.json b/lib/json-node-fetch/package.json new file mode 100644 index 0000000..32b7403 --- /dev/null +++ b/lib/json-node-fetch/package.json @@ -0,0 +1,3 @@ +{ + "name": "json-node-fetch" +} diff --git a/lib/json-node-fetch/test/integration.test.ts b/lib/json-node-fetch/test/integration.test.ts new file mode 100644 index 0000000..eaa08ca --- /dev/null +++ b/lib/json-node-fetch/test/integration.test.ts @@ -0,0 +1,178 @@ +import { createServer, ServerResponse, IncomingMessage } from 'http'; +import { globalJsonRequestOptions, jsonFetch } from 'multiverse/json-node-fetch'; + +let cleanupFn = () => undefined; + +afterAll(() => cleanupFn()); + +describe('::jsonFetch', () => { + it('works', async () => { + expect.hasAssertions(); + + let status = 200; + let header: Record = { 'content-type': 'application/json' }; + let data: unknown = { hello: 'world!' }; + + const server = createServer((_req: IncomingMessage, res: ServerResponse) => { + res.statusCode = status; + + const headers = Object.entries(header); + headers.length && res.setHeader(...headers[0]); + + let dat; + try { + if (data != '{"broken') { + dat = JSON.stringify(data); + } + } catch { + dat = data; + } + + res.end(dat); + }); + + cleanupFn = () => void server.close(); + + const port = await new Promise((resolve, reject) => { + server.listen(() => { + const addr = server.address(); + !addr || typeof addr == 'string' + ? reject(new Error('assertion failed unexpectedly')) + : resolve(addr.port); + }); + }); + + const localUrl = `http://localhost:${port}/`; + let { res, json, error } = await jsonFetch(localUrl); + + expect(res.url).toBe(localUrl); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + status = 555; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual(data); + + status = 200; + header = {}; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + await expect( + jsonFetch(localUrl, { rejectIfNonJsonContentType: true }) + ).rejects.toThrow('without a content-type'); + + header = { 'content-type': 'text/unknown' }; + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + await expect( + jsonFetch(localUrl, { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toThrow('"text/unknown"'); + + data = { hello: 'world!' }; + + await expect( + jsonFetch(localUrl, { rejectIfNonJsonContentType: true }).catch((e) => e.json) + ).resolves.toStrictEqual(data); + + header = { 'content-type': 'application/json' }; + status = 666; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual(data); + + data = {}; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + globalJsonRequestOptions.rejectIfNotOk = true; + + await expect(jsonFetch(localUrl)).rejects.toThrow('666'); + + data = { hello: 'worlds!' }; + status = 201; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + delete globalJsonRequestOptions.rejectIfNotOk; + + header = { 'CONTENT-TYPE': 'application/json' }; + status = 200; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + await expect( + jsonFetch(localUrl, { + headers: { 'CONTENT-TYPE': 'application/json' }, + method: 'POST', + body: { hi: 'world' } + }) + ).resolves.toBeDefined(); + + header = {}; + status = 500; + data = '{"broken'; + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true + }) + ).rejects.toThrow(/without a content-type/); + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + header = { 'content-type': 'application/json' }; + + await expect(jsonFetch(localUrl)).rejects.toThrow( + /failed to parse response body:/ + ); + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true, + rejectIfNotOk: true + }) + ).rejects.toThrow(/500/); + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true + }) + ).rejects.toThrow(/failed to parse response body:/); + + const badObj = { badObj: {} }; + badObj.badObj = badObj; + + await expect( + jsonFetch(localUrl, { + body: badObj + }) + ).rejects.toThrow(/failed to stringify request body:/); + }); +}); diff --git a/lib/json-node-fetch/test/unit.test.ts b/lib/json-node-fetch/test/unit.test.ts new file mode 100644 index 0000000..6ca26d4 --- /dev/null +++ b/lib/json-node-fetch/test/unit.test.ts @@ -0,0 +1,317 @@ +import { asMockedFunction } from '@xunnamius/jest-types'; +import fetch, { Headers } from 'node-fetch'; +import { globalJsonRequestOptions, jsonFetch } from 'multiverse/json-node-fetch'; +import { JsonObject } from 'type-fest'; +import { toss } from 'toss-expression'; + +import type { Response } from 'node-fetch'; + +jest.mock('node-fetch', () => { + const fetch = jest.fn(); + // ? We need to mock Headers (earlier than when beforeEach runs) + // @ts-expect-error: defining Headers + fetch.Headers = jest.requireActual('node-fetch').Headers; + // ? We also need to mock FetchError + // @ts-expect-error: defining FetchError + fetch.FetchError = jest.requireActual('node-fetch').FetchError; + return fetch; +}); + +const mockFetch = asMockedFunction(fetch); + +const mockedFetchResult = {} as unknown as Response; +let mockedFetchResultJson = {} as JsonObject | Error | string; + +beforeEach(() => { + mockFetch.mockImplementation(async () => mockedFetchResult); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = true; + mockedFetchResult.status = 200; + mockedFetchResult.headers = new Headers(); + + mockedFetchResult.json = jest.fn(async () => { + return typeof mockedFetchResultJson == 'string' || + mockedFetchResultJson instanceof Error + ? toss(mockedFetchResultJson) + : mockedFetchResultJson; + }); +}); + +describe('::jsonFetch', () => { + it('fetches a resource and returns the response itself and the body as json', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + }); + + it('returns empty error if the response has a non-json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + + mockedFetchResult.headers.set('content-type', 'something/else'); + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + }); + + it('rejects if the response has a non-json content-type and rejectIfNonJsonContentType is true', async () => { + expect.hasAssertions(); + + mockedFetchResultJson = { hello: 'world' }; + + await expect( + jsonFetch('some-url', { rejectIfNonJsonContentType: true }) + ).rejects.toThrow( + 'received response without a content-type (expected "application/json")' + ); + + mockedFetchResult.headers.set('content-type', 'something/else'); + + await expect( + jsonFetch('some-url', { rejectIfNonJsonContentType: true }) + ).rejects.toThrow( + 'received response with unexpected content-type "something/else" (expected "application/json")' + ); + }); + + it('rejects if the response has a json content-type but non-json body regardless of status code', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ? in JSON at position ??' + ); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ?? in JSON at position ?' + ); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 413; + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ?? in JSON at position ?' + ); + }); + + it('returns error if the response has a non-2xx status code', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 567; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: mockedFetchResultJson + }); + }); + + it('returns empty error if the response has a non-2xx status code and non-json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/something-or-other'); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = false; + mockedFetchResult.status = 403; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + }); + + it('rejects if the response has a non-2xx status code and rejectIfNotOk is true', async () => { + expect.hasAssertions(); + + globalJsonRequestOptions.rejectIfNotOk = true; + + try { + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 567; + globalJsonRequestOptions.rejectIfNotOk = false; + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + + // ? Should also reject with an HttpError even if JSON is not parsable + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + } finally { + delete globalJsonRequestOptions.rejectIfNotOk; + } + }); + + it('rejects if the response has a non-2xx status code, non-json content-type, or non-json body when both rejectIfNonJsonContentType and rejectIfNotOk are true', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = false; + mockedFetchResult.status = 404; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: mockedFetchResultJson }); + + mockedFetchResult.headers.set('content-type', 'application/something'); + mockedFetchResult.ok = true; + mockedFetchResult.status = 200; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: mockedFetchResultJson }); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: undefined }); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: undefined }); + }); + + it('rejects on failure to stringify request body with json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + + const badObj = { badObj: {} }; + badObj.badObj = badObj; + + await expect( + jsonFetch('some-url', { + headers: { 'content-type': 'application/json' }, + body: 'hello' + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: { hello: 'world' } + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + headers: { 'content-type': 'something/else' }, + body: badObj + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: badObj + }) + ).rejects.toThrow('failed to stringify request body:'); + + const spy = jest + .spyOn(JSON, 'stringify') + .mockImplementation(() => + toss(new SyntaxError('unexpected token ? in JSON at position ??')) + ); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: 'whatever' + }) + ).rejects.toThrow( + 'failed to stringify request body: unexpected token ? in JSON at position ??' + ); + + spy.mockImplementation(() => toss('unexpected token ?? in JSON at position ?')); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: 'whatever' + }) + ).rejects.toThrow( + 'failed to stringify request body: unexpected token ?? in JSON at position ?' + ); + }); + + it('handles empty global options', async () => { + expect.hasAssertions(); + + const oldValue = globalJsonRequestOptions.headers; + delete globalJsonRequestOptions.headers; + + try { + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + } finally { + globalJsonRequestOptions.headers = oldValue; + } + }); +}); diff --git a/lib/json-unfetch/index.ts b/lib/json-unfetch/index.ts new file mode 100644 index 0000000..3268bcd --- /dev/null +++ b/lib/json-unfetch/index.ts @@ -0,0 +1,454 @@ +import unfetch from 'unfetch'; +import { makeNamedError } from 'named-app-errors'; +import { isError } from '@xunnamius/types'; + +import type { JsonObject, JsonPrimitive } from 'type-fest'; + +const JsonContentType = 'application/json'; + +// ? Some types are being taken from TypeScript's global built-in DOM library + +/** + * Represents a JSON (un)Fetch error. + */ +export class JsonUnfetchError< + T extends JsonObject | JsonPrimitive | undefined +> extends Error { + constructor( + public readonly res: Response | undefined, + public readonly json: T, + message: string + ) { + super(message); + } +} +makeNamedError(JsonUnfetchError, 'JsonUnfetchError'); + +export type Response = Awaited>; +export type RequestInit = NonNullable[1]>; +export type BodyInit = RequestInit['body']; + +/** + * Options to configure how jsonFetch executes. + * + * @see https://github.com/developit/unfetch#api + */ +export type JsonRequestInit = Omit & { + /** + * Enables SWR compatibility mode when `true`. Favor importing `swrFetch`, a + * SWR syntactic sugar function, to use SWR compatibility rather than setting + * this manually. + * + * @default false + */ + swr?: boolean; + /** + * If `true`, jsonFetch will reject when `response.ok` is not `true`; if + * `false`, `json` will be undefined and `error` will be an empty object. + * + * @default false + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/ok + */ + rejectIfNotOk?: boolean; + /** + * If `true`, jsonFetch will reject when a response is missing the + * `application/json` content-type header; if `false`, `json` will be + * undefined and `error` will be an empty object. + * + * @default false + */ + rejectIfNonJsonContentType?: boolean; + /** + * The request body to send. Automatically stringified (via `JSON.stringify`) + * if request content-type is `application/json`. + * + * Note that this type is loose enough to accept JSON objects, but if you're + * not using the `application/json` content-type when passing a JSON object as + * the body then jsonFetch will reject with an error. + */ + body?: BodyInit | JsonObject | JsonPrimitive; +}; + +/** + * The mutable default options for all `jsonFetch` calls. Keys will be + * overridden by the optional `options` object passed into each call, e.g. + * `jsonFetch(url, options)`. + * + * Note: you must use `credentials: 'include'` to include cookies with your + * requests. This is not the default setting. + * + * @see https://github.com/developit/unfetch#api + */ +export const globalJsonRequestOptions: JsonRequestInit = { + headers: { 'content-type': JsonContentType }, + rejectIfNotOk: false, + rejectIfNonJsonContentType: false +}; + +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and the response body parsed as JSON under either `error` + * (if the response has a non-2xx status) or `json`. + * + * If the response was not received with an `application/json` content-type + * header or has a non-2xx status _and_ unparseable response body, `json` will + * be undefined and `error` will be an empty object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header or 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { + * method: 'POST', + * headers: { authorization: `Bearer ${apiKey}` }, + * body: requestData + * } + * ); + * + * if (error) { + * console.error(error?.reason ?? (res.ok + * ? 'bad json' + * : res.statusText)); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init?: Omit< + JsonRequestInit, + 'swr' | 'rejectIfNotOk' | 'rejectIfNonJsonContentType' + > & { + swr?: false; + rejectIfNotOk?: false; + rejectIfNonJsonContentType?: false; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and the response body parsed as JSON under either `error` + * (if the response has a non-2xx status) or `json`. + * + * If the response was received with a non-2xx status _and_ unparseable response + * body, `json` will be undefined and `error` will be an empty object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, or 3) the response was received with a content-type + * header other than `application/json`. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * + * try { + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { rejectIfNonJsonContentType: true } + * ); + * + * if (error) { + * console.error(error?.reason ?? res.statusText); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-json response bodies + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit< + JsonRequestInit, + 'swr' | 'rejectIfNotOk' | 'rejectIfNonJsonContentType' + > & { + swr?: false; + rejectIfNotOk?: false; + rejectIfNonJsonContentType: true; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and either the response body parsed as JSON under `json` + * or, if the response was received with a content-type header other than + * `application/json`, an empty object under `error`. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, or 3) the response was received with a non-2xx status. + * + * @example + * ``` + * type ResJson = { myNumber: number }; + * type ResErr = { reason: string }; + * + * try { + * const { res, json, error } = await jsonFetch( + * 'api/endpoint', + * { rejectIfNotOk: true } + * ); + * + * if (error) { + * console.error(error?.reason ?? 'bad json'); + * } else { + * console.log(`number is: ${json?.myNumber}`); + * } + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-2xx responses + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit< + JsonRequestInit, + 'swr' | 'rejectIfNotOk' | 'rejectIfNonJsonContentType' + > & { + swr?: false; + rejectIfNotOk: true; + rejectIfNonJsonContentType?: false; + } +): Promise<{ + res: Response; + json: JsonType | undefined; + error: Partial | undefined; +}>; +/** + * Fetches a resource and returns an object containing two items: the response + * itself under `res` and and the response body parsed as JSON under `json`. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, 3) the response was received with a content-type header + * other than `application/json`, or 4) the response was received with a non-2xx + * status. + * + * Hence, when jsonFetch is called in this way, `json` will always be defined + * and `error` will always be undefined. + * + * @example + * ``` + * try { + * const url = 'https://some.resource.com/data.json'; + * const { json } = await jsonFetch(url, { + * rejectIfNotOk: true, + * rejectIfNonJsonContentType: true + * }); + * doSomethingWith(json); + * } catch(e) { + * if(e instanceof JsonFetchError) { + * // Special handling for non-2xx/non-json response bodies + * specialHandler(e.res.status, e.json); + * } else { + * throw e; + * } + * } + * ``` + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ErrorType extends JsonObject = JsonType +>( + url: string, + init: Omit< + JsonRequestInit, + 'swr' | 'rejectIfNotOk' | 'rejectIfNonJsonContentType' + > & { + swr?: false; + rejectIfNotOk: true; + rejectIfNonJsonContentType: true; + } +): Promise<{ + res: Response; + json: JsonType; + error: undefined; +}>; +/** + * Fetches a resource and returns the response body parsed as a JSON object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, 3) the response was received with a content-type header + * other than `application/json`, or 4) the response was received with a non-2xx + * status. + * + * The object SWR returns will contain the rejection reason under the `error` + * property. Usually, `error` is as an instance of JsonUnfetchError complete + * with `json` and `res` properties. If unfetch itself fails, the `error` + * object returned will not have these properties. + * + * @example + * ``` + * const { data: json, error } = useSwr('api/endpoint', swrFetch); + * // Or: ... = useSwr('api/endpoint', key => jsonFetch(key, { swr: true })); + * + * if(error)
Error: {error.message}
; + * return
Hello, your data is: {json.data}
; + * ``` + * + * @see https://swr.vercel.app + */ +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ErrorType extends JsonObject = JsonType +>(url: string, init: Omit & { swr: true }): Promise; +export async function jsonFetch< + JsonType extends JsonObject = JsonObject, + ErrorType extends JsonObject = JsonType +>(url: string, init?: JsonRequestInit): Promise { + const parsedOptions = { + ...globalJsonRequestOptions, + ...(init?.swr ? { method: 'GET' } : {}), + ...init + }; + + // ? A case-insensitive check since unfetch doesn't use a Headers instance + const hasJsonContentType = !!Object.entries(parsedOptions.headers || {}).find( + ([k, v]) => k.toLowerCase() == 'content-type' && v == JsonContentType + ); + + if (hasJsonContentType) { + try { + parsedOptions.body = JSON.stringify(parsedOptions.body); + } catch (e) { + throw new JsonUnfetchError( + undefined, + undefined, + `failed to stringify request body: ${isError(e) ? e.message : e}` + ); + } + } + + const res = await unfetch(url, parsedOptions as RequestInit); + const responseContentType = res.headers.get('content-type'); + + let parseError = ''; + let json: JsonType | undefined = undefined; + let error: Partial | undefined = undefined; + + try { + json = await res.json(); + } catch (e) { + parseError = `${isError(e) ? e.message : e}`; + } + + if (!res.ok && (parsedOptions.rejectIfNotOk || parsedOptions.swr)) { + throw new JsonUnfetchError( + res, + json, + `response status code ${res.status} was not in the range 200-299` + ); + } + + if ( + responseContentType != JsonContentType && + (parsedOptions.rejectIfNonJsonContentType || parsedOptions.swr) + ) { + throw new JsonUnfetchError( + res, + json, + `received response ${ + responseContentType + ? `with unexpected content-type "${responseContentType}"` + : 'without a content-type' + } (expected "application/json")` + ); + } + + if (parseError && responseContentType == JsonContentType) { + throw new JsonUnfetchError( + res, + json, + `failed to parse response body: ${parseError}` + ); + } + + if (responseContentType != JsonContentType || parseError) { + json = undefined; + error = {}; + } else if (!res.ok) { + error = json as unknown as Partial; + json = undefined; + } + + return parsedOptions.swr ? json : { res, json, error }; +} + +/** + * Fetches a resource and returns the response body parsed as a JSON object. + * + * This function rejects if 1) the request body cannot be parsed as JSON but is + * being sent with an `application/json` content-type header, 2) the response + * body cannot be parsed as JSON but was received with an `application/json` + * content-type header, 3) the response was received with a content-type header + * other than `application/json`, or 4) the response was received with a non-2xx + * status. + * + * The object SWR returns will contain the rejection reason under the `error` + * property. Usually, `error` is as an instance of JsonUnfetchError complete + * with `json` and `res` properties. If unfetch itself fails, the `error` + * object returned will not have these properties. + * + * @example + * ``` + * const { data: json, error } = useSwr('api/endpoint', swrFetch); + * + * if(error)
Error: {error.message}
; + * return
Hello, your data is: {json.data}
; + * ``` + * + * @see https://swr.vercel.app + */ +export function swrFetch( + init?: JsonRequestInit +): (key: string) => Promise { + return (key) => { + return jsonFetch(key, { + ...init, + swr: true + }); + }; +} diff --git a/lib/json-unfetch/package.json b/lib/json-unfetch/package.json new file mode 100644 index 0000000..c47f576 --- /dev/null +++ b/lib/json-unfetch/package.json @@ -0,0 +1,3 @@ +{ + "name": "json-unfetch" +} diff --git a/lib/json-unfetch/test/integration.test.ts b/lib/json-unfetch/test/integration.test.ts new file mode 100644 index 0000000..ae1788a --- /dev/null +++ b/lib/json-unfetch/test/integration.test.ts @@ -0,0 +1,186 @@ +/** + * @jest-environment jsdom + */ +import { createServer, ServerResponse, IncomingMessage } from 'http'; +import { createHttpTerminator } from 'http-terminator'; +import { globalJsonRequestOptions, jsonFetch } from 'multiverse/json-unfetch'; + +let cleanupFn = async () => undefined; + +afterAll(async () => cleanupFn()); + +describe('::jsonFetch', () => { + it('works', async () => { + expect.hasAssertions(); + + let status = 200; + let header: Record = { 'content-type': 'application/json' }; + let data: unknown = { hello: 'world!' }; + + const server = createServer((_req: IncomingMessage, res: ServerResponse) => { + res.statusCode = status; + + res.setHeader('access-control-allow-origin', '*'); + const headers = Object.entries(header); + headers.length && res.setHeader(...headers[0]); + + let dat; + try { + if (data != '{"broken') { + dat = JSON.stringify(data); + } + } catch { + dat = data; + } + + res.end(dat); + }); + + // ? Unlike node-fetch, unfetch keeps connection handles open a while after + // ? they complete, perhaps for pooling/caching reasons... + const httpTerminator = createHttpTerminator({ server }); + cleanupFn = async () => void (await httpTerminator.terminate()); + + const port = await new Promise((resolve, reject) => { + server.listen(() => { + const addr = server.address(); + !addr || typeof addr == 'string' + ? reject(new Error('assertion failed unexpectedly')) + : resolve(addr.port); + }); + }); + + const localUrl = `http://localhost:${port}/`; + let { res, json, error } = await jsonFetch(localUrl); + + expect(res.url).toBe(localUrl); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + status = 555; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual(data); + + status = 200; + header = {}; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + await expect( + jsonFetch(localUrl, { rejectIfNonJsonContentType: true }) + ).rejects.toThrow('without a content-type'); + + header = { 'content-type': 'text/unknown' }; + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + await expect( + jsonFetch(localUrl, { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toThrow('"text/unknown"'); + + data = { hello: 'world!' }; + + await expect( + jsonFetch(localUrl, { rejectIfNonJsonContentType: true }).catch((e) => e.json) + ).resolves.toStrictEqual(data); + + header = { 'content-type': 'application/json' }; + status = 666; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual(data); + + data = {}; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + globalJsonRequestOptions.rejectIfNotOk = true; + + await expect(jsonFetch(localUrl)).rejects.toThrow('666'); + + data = { hello: 'worlds!' }; + status = 201; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + delete globalJsonRequestOptions.rejectIfNotOk; + + header = { 'CONTENT-TYPE': 'application/json' }; + status = 200; + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toStrictEqual(data); + expect(error).toBeUndefined(); + + await expect( + jsonFetch(localUrl, { + headers: { 'CONTENT-TYPE': 'application/json' }, + method: 'POST', + body: { hi: 'world' } + }) + ).resolves.toBeDefined(); + + header = {}; + status = 500; + data = '{"broken'; + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true + }) + ).rejects.toThrow(/without a content-type/); + + ({ res, json, error } = await jsonFetch(localUrl)); + + expect(res.status).toBe(status); + expect(json).toBeUndefined(); + expect(error).toStrictEqual({}); + + header = { 'content-type': 'application/json' }; + + await expect(jsonFetch(localUrl)).rejects.toThrow( + /failed to parse response body:/ + ); + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true, + rejectIfNotOk: true + }) + ).rejects.toThrow(/500/); + + await expect( + jsonFetch(localUrl, { + rejectIfNonJsonContentType: true + }) + ).rejects.toThrow(/failed to parse response body:/); + + const badObj = { badObj: {} }; + badObj.badObj = badObj; + + await expect( + jsonFetch(localUrl, { + body: badObj + }) + ).rejects.toThrow(/failed to stringify request body:/); + }); +}); diff --git a/lib/json-unfetch/test/unit.test.ts b/lib/json-unfetch/test/unit.test.ts new file mode 100644 index 0000000..cad14db --- /dev/null +++ b/lib/json-unfetch/test/unit.test.ts @@ -0,0 +1,382 @@ +import { asMockedFunction } from '@xunnamius/jest-types'; +import unfetch from 'unfetch'; +import { + globalJsonRequestOptions, + jsonFetch, + swrFetch +} from 'multiverse/json-unfetch'; +import { JsonObject } from 'type-fest'; +import { toss } from 'toss-expression'; + +import type { Response } from 'multiverse/json-unfetch'; + +jest.mock('unfetch'); + +const mockFetch = asMockedFunction(unfetch); + +const mockedFetchResult = {} as unknown as Omit & { + headers: Response['headers'] & { + set: (k: string, v: string) => void; + }; +}; + +let mockedFetchResultJson = {} as JsonObject | Error | string; + +beforeEach(() => { + mockFetch.mockImplementation(async () => mockedFetchResult); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = true; + mockedFetchResult.status = 200; + mockedFetchResult.headers = + new Map() as unknown as typeof mockedFetchResult['headers']; + + mockedFetchResult.json = jest.fn(async () => { + return typeof mockedFetchResultJson == 'string' || + mockedFetchResultJson instanceof Error + ? toss(mockedFetchResultJson) + : mockedFetchResultJson; + }); +}); + +describe('::jsonFetch', () => { + it('fetches a resource and returns the response itself and the body as json', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + }); + + it('returns empty error if the response has a non-json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + + mockedFetchResult.headers.set('content-type', 'something/else'); + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + }); + + it('rejects if the response has a non-json content-type and rejectIfNonJsonContentType is true', async () => { + expect.hasAssertions(); + + mockedFetchResultJson = { hello: 'world' }; + + await expect( + jsonFetch('some-url', { rejectIfNonJsonContentType: true }) + ).rejects.toThrow( + 'received response without a content-type (expected "application/json")' + ); + + mockedFetchResult.headers.set('content-type', 'something/else'); + + await expect( + jsonFetch('some-url', { rejectIfNonJsonContentType: true }) + ).rejects.toThrow( + 'received response with unexpected content-type "something/else" (expected "application/json")' + ); + }); + + it('rejects if the response has a json content-type but non-json body regardless of status code', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ? in JSON at position ??' + ); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ?? in JSON at position ?' + ); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 413; + + await expect(jsonFetch('some-url')).rejects.toThrow( + 'failed to parse response body: unexpected token ?? in JSON at position ?' + ); + }); + + it('returns error if the response has a non-2xx status code', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 567; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: mockedFetchResultJson + }); + }); + + it('returns empty error if the response has a non-2xx status code and non-json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/something-or-other'); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = false; + mockedFetchResult.status = 403; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: undefined, + error: {} + }); + }); + + it('rejects if the response has a non-2xx status code and rejectIfNotOk is true', async () => { + expect.hasAssertions(); + + globalJsonRequestOptions.rejectIfNotOk = true; + + try { + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + + mockedFetchResult.ok = false; + mockedFetchResult.status = 567; + globalJsonRequestOptions.rejectIfNotOk = false; + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + + // ? Should also reject with an HttpError even if JSON is not parsable + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect(jsonFetch('some-url', { rejectIfNotOk: true })).rejects.toThrow( + 'response status code 567 was not in the range 200-299' + ); + } finally { + delete globalJsonRequestOptions.rejectIfNotOk; + } + }); + + it('rejects if the response has a non-2xx status code, non-json content-type, or non-json body when both rejectIfNonJsonContentType and rejectIfNotOk are true', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + mockedFetchResult.ok = false; + mockedFetchResult.status = 404; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: mockedFetchResultJson }); + + mockedFetchResult.headers.set('content-type', 'application/something'); + mockedFetchResult.ok = true; + mockedFetchResult.status = 200; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: mockedFetchResultJson }); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = new SyntaxError( + 'unexpected token ? in JSON at position ??' + ); + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: undefined }); + + mockedFetchResultJson = 'unexpected token ?? in JSON at position ?'; + + await expect( + jsonFetch('some-url', { rejectIfNotOk: true, rejectIfNonJsonContentType: true }) + ).rejects.toMatchObject({ res: mockedFetchResult, json: undefined }); + }); + + it('rejects on failure to stringify request body with json content-type', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + + const badObj = { badObj: {} }; + badObj.badObj = badObj; + + await expect( + jsonFetch('some-url', { + headers: { 'content-type': 'application/json' }, + body: 'hello' + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: { hello: 'world' } + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + headers: { 'content-type': 'something/else' }, + body: badObj + }) + ).resolves.toBeDefined(); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: badObj + }) + ).rejects.toThrow('failed to stringify request body:'); + + const spy = jest + .spyOn(JSON, 'stringify') + .mockImplementation(() => + toss(new SyntaxError('unexpected token ? in JSON at position ??')) + ); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: 'whatever' + }) + ).rejects.toThrow( + 'failed to stringify request body: unexpected token ? in JSON at position ??' + ); + + spy.mockImplementation(() => toss('unexpected token ?? in JSON at position ?')); + + await expect( + jsonFetch('some-url', { + // headers: { 'content-type': 'application/json' }, // ? Default + body: 'whatever' + }) + ).rejects.toThrow( + 'failed to stringify request body: unexpected token ?? in JSON at position ?' + ); + }); + + it('handles empty global options', async () => { + expect.hasAssertions(); + + const oldValue = globalJsonRequestOptions.headers; + delete globalJsonRequestOptions.headers; + + try { + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world' }; + + await expect(jsonFetch('some-url')).resolves.toStrictEqual({ + res: mockedFetchResult, + json: mockedFetchResultJson, + error: undefined + }); + } finally { + globalJsonRequestOptions.headers = oldValue; + } + }); +}); + +describe('::swrFetch', () => { + it('returns the json response directly', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(swrFetch()('some-x-url')).resolves.toBe(mockedFetchResultJson); + expect(asMockedFunction(unfetch)).toBeCalledWith( + 'some-x-url', + expect.objectContaining({ swr: true }) + ); + }); + + it('sets the request method to GET even if it is set to POST globally (still locally overridable)', async () => { + expect.hasAssertions(); + + globalJsonRequestOptions.method = 'POST'; + + try { + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResultJson = { hello: 'world!' }; + + await expect(swrFetch()('some-x-url')).resolves.toBe(mockedFetchResultJson); + expect(asMockedFunction(unfetch)).toBeCalledWith( + 'some-x-url', + expect.objectContaining({ method: 'GET' }) + ); + + await expect(swrFetch({ method: 'PUT' })('some-x-url')).resolves.toBe( + mockedFetchResultJson + ); + expect(asMockedFunction(unfetch)).toBeCalledWith( + 'some-x-url', + expect.objectContaining({ method: 'PUT' }) + ); + } finally { + delete globalJsonRequestOptions.method; + } + }); + + it('rejects if the response has a non-2xx status code even if rejectIfNotOk is false', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/json'); + mockedFetchResult.ok = false; + mockedFetchResult.status = 789; + + await expect(swrFetch({ rejectIfNotOk: false })('some-x-url')).rejects.toThrow( + 'response status code 789 was not in the range 200-299' + ); + }); + + it('rejects if the response has a non-json content-type even if rejectIfNonJsonContentType is false', async () => { + expect.hasAssertions(); + + mockedFetchResult.headers.set('content-type', 'application/bad'); + + await expect( + swrFetch({ rejectIfNonJsonContentType: false })('some-x-url') + ).rejects.toThrow('application/bad'); + }); +}); diff --git a/lib/mongo-common/index.ts b/lib/mongo-common/index.ts new file mode 100644 index 0000000..2df3646 --- /dev/null +++ b/lib/mongo-common/index.ts @@ -0,0 +1,149 @@ +import cloneDeep from 'clone-deep'; +import { ObjectId } from 'mongodb'; +import { InvalidAppConfigurationError } from 'named-app-errors'; + +import { mockDateNowMs } from 'multiverse/jest-mock-date'; + +import { + BANNED_BEARER_TOKEN, + DEV_BEARER_TOKEN, + DUMMY_BEARER_TOKEN +} from 'multiverse/next-auth'; + +import type { DbSchema } from 'multiverse/mongo-schema'; +import type { DummyData } from 'multiverse/mongo-test'; +import type { InternalAuthEntry } from 'multiverse/next-auth'; +import type { InternalLimitedLogEntry } from 'multiverse/next-limit'; +import type { InternalRequestLogEntry } from 'multiverse/next-log'; + +export * from 'multiverse/jest-mock-date'; + +/** + * A JSON representation of the backend Mongo database structure. This is used + * for common consistent "well-known" db structure across projects. + * + * Well-known databases and their well-known collections currently include: + * - `root` (collections: `auth`, `request-log`, `limited-log`) + */ +export function getCommonSchemaConfig(additionalSchemaConfig?: DbSchema): DbSchema { + const schema = { + databases: { + root: { + collections: [ + { + name: 'auth', + indices: [ + { spec: 'attributes.owner' }, + // ! When performing equality matches on embedded documents, field + // ! order matters and the embedded documents must match exactly. + // * https://xunn.at/mongo-docs-query-embedded-docs + // ! Additionally, field order determines internal sort order. + { spec: ['scheme', 'token'], options: { unique: true } } + ] + }, + { + name: 'request-log', + indices: [{ spec: 'header' }, { spec: 'ip' }] + }, + { + name: 'limited-log', + indices: [{ spec: 'header' }, { spec: 'ip' }, { spec: { until: -1 } }] + } + ] + }, + ...additionalSchemaConfig?.databases + }, + aliases: { ...additionalSchemaConfig?.aliases } + }; + + const actualDatabaseNames = Object.keys(schema.databases); + + Object.entries(schema.aliases).every(([alias, actual]) => { + if (!actualDatabaseNames.includes(actual)) { + throw new InvalidAppConfigurationError( + `aliased database "${actual}" (referred to by alias "${alias}") does not exist in database schema or is not aliasable. Existing aliasable databases: ${actualDatabaseNames.join( + ', ' + )}` + ); + } + + if (actualDatabaseNames.includes(alias)) { + throw new InvalidAppConfigurationError( + `database alias "${alias}" (referring to actual database "${actual}") is invalid: an actual database with that name already exists in the database schema. You must choose a different alias` + ); + } + }); + + return schema; +} + +/** + * Returns data used to hydrate well-known databases and their well-known + * collections. + * + * Well-known databases and their well-known collections currently include: + * - `root` (collections: `auth`, `request-log`, `limited-log`) + */ +export function getCommonDummyData(additionalDummyData?: DummyData): DummyData { + return cloneDeep({ root: dummyRootData, ...additionalDummyData }); +} + +/** + * The shape of the well-known `root` database's collections and their test + * data. + */ +export type DummyRootData = { + _generatedAt: number; + auth: InternalAuthEntry[]; + 'request-log': InternalRequestLogEntry[]; + 'limited-log': InternalLimitedLogEntry[]; +}; + +/** + * Test data for the well-known `root` database. + */ +export const dummyRootData: DummyRootData = { + _generatedAt: mockDateNowMs, + auth: [ + // ! Must maintain order or various unit tests across projects will fail ! + { + _id: new ObjectId(), + attributes: { owner: 'local developer', isGlobalAdmin: true }, + scheme: 'bearer', + token: { bearer: DEV_BEARER_TOKEN } + }, + { + _id: new ObjectId(), + attributes: { owner: 'dummy owner' }, + scheme: 'bearer', + token: { bearer: DUMMY_BEARER_TOKEN } + }, + { + _id: new ObjectId(), + attributes: { owner: 'banned dummy owner' }, + scheme: 'bearer', + token: { bearer: BANNED_BEARER_TOKEN } + } + ], + 'request-log': [...Array(22)].map((_, ndx) => ({ + _id: new ObjectId(), + ip: '1.2.3.4', + header: ndx % 2 ? null : `bearer ${BANNED_BEARER_TOKEN}`, + method: ndx % 3 ? 'GET' : 'POST', + route: 'fake/route', + endpoint: '/fake/:route', + createdAt: mockDateNowMs + 10 ** 6, + resStatusCode: 200, + durationMs: 1234 + })), + 'limited-log': [ + // ! Must maintain order or various unit tests will fail + { _id: new ObjectId(), ip: '1.2.3.4', until: mockDateNowMs + 1000 * 60 * 15 }, + { _id: new ObjectId(), ip: '5.6.7.8', until: mockDateNowMs + 1000 * 60 * 15 }, + { + _id: new ObjectId(), + header: `bearer ${BANNED_BEARER_TOKEN}`, + until: mockDateNowMs + 1000 * 60 * 60 + } + ] +}; diff --git a/lib/mongo-common/package.json b/lib/mongo-common/package.json new file mode 100644 index 0000000..b1a8a42 --- /dev/null +++ b/lib/mongo-common/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/mongo-common" +} diff --git a/lib/mongo-common/unit.test.ts b/lib/mongo-common/unit.test.ts new file mode 100644 index 0000000..5abbc9e --- /dev/null +++ b/lib/mongo-common/unit.test.ts @@ -0,0 +1,59 @@ +import { getCommonSchemaConfig, getCommonDummyData } from 'multiverse/mongo-common'; + +describe('::getCommonSchemaConfig', () => { + it('returns an object with dummy root schema and additional dummy schema', async () => { + expect.hasAssertions(); + + expect( + getCommonSchemaConfig({ + databases: { someDb: { collections: [] } }, + aliases: {} + }) + ).toStrictEqual({ + databases: { + root: expect.toBeObject(), + someDb: expect.toBeObject() + }, + aliases: {} + }); + }); + + it('throws if an alias references a non-existent database name', async () => { + expect.hasAssertions(); + + expect(() => + getCommonSchemaConfig({ + databases: { someDb: { collections: [] } }, + aliases: { app: 'badDb' } + }) + ).toThrow( + /aliased database "badDb" \(referred to by alias "app"\) does not exist/ + ); + }); + + it('throws if an alias itself conflicts with a database name', async () => { + expect.hasAssertions(); + + expect(() => + getCommonSchemaConfig({ + databases: { someDb: { collections: [] } }, + aliases: { someDb: 'root' } + }) + ).toThrow( + /database alias "someDb" \(referring to actual database "root"\) is invalid/ + ); + }); +}); + +describe('::getCommonDummyData', () => { + it('returns an object with dummy root data and additional dummy data', async () => { + expect.hasAssertions(); + + const customDummyData = { someDb: { _generatedAt: 123 } }; + + expect(getCommonDummyData(customDummyData)).toStrictEqual({ + root: expect.toBeObject(), + someDb: expect.toBeObject() + }); + }); +}); diff --git a/lib/mongo-item/index.ts b/lib/mongo-item/index.ts new file mode 100644 index 0000000..b7d50ff --- /dev/null +++ b/lib/mongo-item/index.ts @@ -0,0 +1,193 @@ +import { ObjectId } from 'mongodb'; +import { isError } from '@xunnamius/types'; +import { toss } from 'toss-expression'; +import { GuruMeditationError, ValidationError } from 'named-app-errors'; + +import type { Collection, WithId } from 'mongodb'; + +/** + * Represents the value of the `_id` property of a MongoDB collection entry. + * Optionally, a key other than `_id` can be specified using the `{ key: ..., + * id: ... }` syntax. + */ +export type ItemExistsIdParam = + | string + | ObjectId + | { key: string; id: string | ObjectId }; + +/** + * Available options for the `itemExists` function. + */ +export type ItemExistsOptions = { + /** + * Items matching excludeId will be completely ignored by this function. + * + * @default undefined + */ + excludeId?: ItemExistsIdParam; + /** + * If `true`, ids will be matched in a case-insensitive manner (via locale). + * + * @default false + */ + caseInsensitive?: boolean; + /** + * When looking for an item matching `{ _id: id }`, where the descriptor key + * is the string `"_id"`, `id` will be optimistically wrapped in a `new + * ObjectId(id)` call. Set this to `false` to prevent this. + * + * @default true + */ + optimisticCoercion?: boolean; +}; + +/** + * Checks if an item matching `{ _id: id }` exists within `collection`. + */ +export async function itemExists( + collection: Collection, + id: string | ObjectId, + options?: ItemExistsOptions +): Promise; +/** + * Checks if an item matching `{ [descriptor.key]: descriptor.id }` exists + * within `collection`. + */ +export async function itemExists( + collection: Collection, + descriptor: { key: string; id: string | ObjectId }, + options?: ItemExistsOptions +): Promise; +export async function itemExists( + collection: Collection, + id: ItemExistsIdParam, + options?: ItemExistsOptions +): Promise { + let excludeIdProperty: string | null = null; + let excludeId: string | ObjectId | null = null; + const idProperty = typeof id == 'string' || id instanceof ObjectId ? '_id' : id.key; + id = typeof id == 'string' || id instanceof ObjectId ? id : id.id; + + if (options?.excludeId) { + excludeIdProperty = + typeof options.excludeId == 'string' || options.excludeId instanceof ObjectId + ? '_id' + : options.excludeId.key; + + excludeId = + typeof options.excludeId == 'string' || options.excludeId instanceof ObjectId + ? options.excludeId + : options.excludeId.id; + } + + if (idProperty == excludeIdProperty) { + throw new GuruMeditationError( + `cannot lookup an item by property "${idProperty}" while also filtering results by that same property` + ); + } + + if ( + options?.optimisticCoercion !== false && + typeof id == 'string' && + idProperty == '_id' + ) { + id = new ObjectId(id); + } + + return ( + (await collection.countDocuments( + { + [idProperty]: id, + ...(excludeIdProperty ? { [excludeIdProperty]: { $ne: excludeId } } : {}) + } as unknown as Parameters[0], + { + ...(options?.caseInsensitive + ? { collation: { locale: 'en', strength: 2 } } + : {}) + } + )) != 0 + ); +} + +/** + * The shape of an object that can be translated into an `ObjectId` (or `T`) + * instance or is `null`/`undefined`. + */ +export type IdItem = + | WithId + | string + | T + | null + | undefined; + +/** + * The shape of an array of objects that can be translated into an array of + * `ObjectId` (or `T`) instances or are `null`/`undefined`. + */ +export type IdItemArray = IdItem[]; + +/** + * Reduces an `item` down to its `ObjectId` instance. + */ +export function itemToObjectId(item: IdItem): T; +/** + * Reduces an array of `items` down to their respective `ObjectId` instances. + * + * An attempt is made to eliminate duplicates via `new Set(...)`, but the + * absence of duplicates is not guaranteed when `items` contains `WithId<...>` + * objects. + */ +export function itemToObjectId(items: IdItemArray): T[]; +export function itemToObjectId( + item: IdItem | IdItemArray +): T | T[] { + let _id: unknown = ''; + try { + return item instanceof ObjectId + ? item + : Array.isArray(item) + ? Array.from(new Set(item)).map((i) => { + _id = i; + return ( + i instanceof ObjectId + ? i + : typeof i == 'string' + ? new ObjectId(i) + : i?._id instanceof ObjectId + ? i._id + : toss( + new GuruMeditationError(`encountered irreducible sub-item: ${i}`) + ) + ) as T; + }) + : typeof item == 'string' + ? ((_id = item), new ObjectId(item) as T) + : item?._id instanceof ObjectId + ? (item._id as T) + : toss(new GuruMeditationError(`encountered irreducible item: ${item}`)); + } catch (e) { + if (isError(e) && e.name == 'BSONTypeError') { + throw new ValidationError(`invalid id "${_id}"`); + } + + throw e; + } +} + +/** + * Reduces an `item` down to the string representation of its `ObjectId` + * instance. + */ +export function itemToStringId(item: IdItem): string; +/** + * Reduces an array of `items` down to the string representations of their + * respective `ObjectId` instances. + */ +export function itemToStringId(items: IdItemArray): string[]; +export function itemToStringId( + item: IdItem | IdItemArray +): string | string[] { + return Array.isArray(item) + ? itemToObjectId(item).map(String) + : itemToObjectId(item).toString(); +} diff --git a/lib/mongo-item/package.json b/lib/mongo-item/package.json new file mode 100644 index 0000000..c8883c3 --- /dev/null +++ b/lib/mongo-item/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/mongo-item" +} diff --git a/lib/mongo-item/unit.test.ts b/lib/mongo-item/unit.test.ts new file mode 100644 index 0000000..6202804 --- /dev/null +++ b/lib/mongo-item/unit.test.ts @@ -0,0 +1,234 @@ +import { ObjectId } from 'mongodb'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { itemExists, itemToObjectId, itemToStringId } from 'multiverse/mongo-item'; +import { getDb } from 'multiverse/mongo-schema'; +import { toss } from 'toss-expression'; +import { TrialError } from 'named-app-errors'; +import { DUMMY_BEARER_TOKEN, NULL_BEARER_TOKEN } from 'multiverse/next-auth'; + +import type { InternalAuthBearerEntry } from 'multiverse/next-auth'; + +setupMemoryServerOverride(); + +describe('::itemExists', () => { + it('returns true if an item exists in a collection where [key] == id', async () => { + expect.hasAssertions(); + + const col = (await getDb({ name: 'root' })).collection('auth'); + const item = + (await col.findOne()) || + toss(new TrialError('assert failed')); + + await expect(itemExists(col, item._id)).resolves.toBeTrue(); + await expect(itemExists(col, new ObjectId())).resolves.toBeFalse(); + + await expect( + itemExists(col, { key: 'token.bearer', id: DUMMY_BEARER_TOKEN }) + ).resolves.toBeTrue(); + + await expect( + itemExists(col, { key: 'token.bearer', id: NULL_BEARER_TOKEN }) + ).resolves.toBeFalse(); + }); + + it('optimistically coerces strings to ObjectIds unless optimisticCoercion is false', async () => { + expect.hasAssertions(); + + const col = (await getDb({ name: 'root' })).collection('auth'); + const item = + (await col.findOne()) || + toss(new TrialError('assert failed')); + + await expect(itemExists(col, item._id.toString())).resolves.toBeTrue(); + await expect( + itemExists(col, item._id.toString(), { optimisticCoercion: false }) + ).resolves.toBeFalse(); + }); + + it('respects excludeId option', async () => { + expect.hasAssertions(); + + const col = (await getDb({ name: 'root' })).collection('auth'); + const item = + (await col.findOne()) || + toss(new TrialError('assert failed')); + + await expect(itemExists(col, item._id)).resolves.toBeTrue(); + await expect( + itemExists(col, item._id, { + excludeId: { key: 'token.bearer', id: item.token.bearer } + }) + ).resolves.toBeFalse(); + + await expect( + itemExists( + col, + { key: 'token.bearer', id: item.token.bearer }, + { + excludeId: item._id + } + ) + ).resolves.toBeFalse(); + }); + + it('rejects if attempting to exclude using same property as id', async () => { + expect.hasAssertions(); + + const col = (await getDb({ name: 'root' })).collection('auth'); + const item = + (await col.findOne()) || + toss(new TrialError('assert failed')); + + await expect(itemExists(col, item._id, { excludeId: item._id })).rejects.toThrow( + 'cannot lookup an item by property "_id"' + ); + + await expect( + itemExists( + col, + { key: 'token.bearer', id: item.token.bearer }, + { excludeId: { key: 'token.bearer', id: item.token.bearer } } + ) + ).rejects.toThrow('cannot lookup an item by property "token.bearer"'); + }); + + it('respects caseInsensitive option', async () => { + expect.hasAssertions(); + + const col = (await getDb({ name: 'root' })).collection('auth'); + + await expect( + itemExists( + col, + { key: 'token.bearer', id: DUMMY_BEARER_TOKEN.toUpperCase() }, + { caseInsensitive: true } + ) + ).resolves.toBeTrue(); + + await expect( + itemExists(col, { key: 'token.bearer', id: DUMMY_BEARER_TOKEN.toUpperCase() }) + ).resolves.toBeFalse(); + + const item = + (await col.findOne()) || + toss(new TrialError('assert failed')); + + await expect(itemExists(col, item._id)).resolves.toBeTrue(); + + await expect( + itemExists(col, item._id, { + excludeId: { key: 'token.bearer', id: item.token.bearer.toUpperCase() } + }) + ).resolves.toBeTrue(); + + await expect( + itemExists(col, item._id, { + excludeId: { key: 'token.bearer', id: item.token.bearer.toUpperCase() }, + caseInsensitive: true + }) + ).resolves.toBeFalse(); + }); +}); + +describe('::itemToObjectId', () => { + it('reduces an item down to its ObjectId instance', async () => { + expect.hasAssertions(); + + const id = new ObjectId(); + + expect(itemToObjectId({ _id: id })).toBe(id); + expect(itemToObjectId(id.toString())).toStrictEqual(id); + expect(itemToObjectId(id)).toBe(id); + }); + + it('reduces an array of items down to their respective ObjectId instances', async () => { + expect.hasAssertions(); + + const ids = [new ObjectId(), new ObjectId(), new ObjectId()]; + + expect(itemToObjectId(ids)).toStrictEqual(ids); + + expect( + itemToObjectId([{ _id: ids[0] }, { _id: ids[1] }, { _id: ids[2] }]) + ).toStrictEqual(ids); + + expect( + itemToObjectId([ids[0].toString(), ids[1].toString(), ids[2].toString()]) + ).toStrictEqual(ids); + }); + + it('duplicate ObjectIds are eliminated during simple array reduction', async () => { + expect.hasAssertions(); + + const id = new ObjectId(); + const ids = [id]; + + expect(itemToObjectId(ids)).toStrictEqual(ids); + + expect( + itemToObjectId([id.toString(), id.toString(), id.toString()]) + ).toStrictEqual(ids); + }); + + it('throws if an item is irreducible or invalid', async () => { + expect.hasAssertions(); + + expect(() => itemToObjectId(null)).toThrow('irreducible'); + expect(() => itemToObjectId(undefined)).toThrow('irreducible'); + expect(() => itemToObjectId([null])).toThrow('irreducible'); + expect(() => itemToObjectId([undefined])).toThrow('irreducible'); + // @ts-expect-error: bad param + expect(() => itemToObjectId({})).toThrow('irreducible'); + // @ts-expect-error: bad param + expect(() => itemToObjectId([{}])).toThrow('irreducible'); + expect(() => itemToObjectId('bad')).toThrow('invalid id "bad"'); + expect(() => itemToObjectId(['bad'])).toThrow('invalid id "bad"'); + expect(() => itemToObjectId([new ObjectId(), 'bad'])).toThrow('invalid id "bad"'); + }); +}); + +describe('::itemToStringId', () => { + it('reduces an item down to its string representation', async () => { + expect.hasAssertions(); + + const id = new ObjectId(); + const idString = id.toString(); + + expect(itemToStringId({ _id: id })).toBe(idString); + expect(itemToStringId(idString)).toBe(idString); + expect(itemToStringId(id)).toBe(idString); + }); + + it('reduces an array of items down to string representations', async () => { + expect.hasAssertions(); + + const ids = [new ObjectId(), new ObjectId(), new ObjectId()]; + const idStrings = ids.map(String); + + expect(itemToStringId(ids)).toStrictEqual(idStrings); + + expect( + itemToStringId([{ _id: ids[0] }, { _id: ids[1] }, { _id: ids[2] }]) + ).toStrictEqual(idStrings); + + expect( + itemToStringId([ids[0].toString(), ids[1].toString(), ids[2].toString()]) + ).toStrictEqual(idStrings); + }); + + it('throws if item is irreducible', async () => { + expect.hasAssertions(); + + expect(() => itemToStringId(null)).toThrow('irreducible'); + expect(() => itemToStringId(undefined)).toThrow('irreducible'); + expect(() => itemToStringId([null])).toThrow('irreducible'); + expect(() => itemToStringId([undefined])).toThrow('irreducible'); + // @ts-expect-error: bad param + expect(() => itemToStringId({})).toThrow('irreducible'); + // @ts-expect-error: bad param + expect(() => itemToStringId([{}])).toThrow('irreducible'); + expect(() => itemToStringId('bad')).toThrow('invalid id "bad"'); + expect(() => itemToStringId(['bad'])).toThrow('invalid id "bad"'); + expect(() => itemToStringId([new ObjectId(), 'bad'])).toThrow('invalid id "bad"'); + }); +}); diff --git a/lib/mongo-schema/index.ts b/lib/mongo-schema/index.ts new file mode 100644 index 0000000..4d0149d --- /dev/null +++ b/lib/mongo-schema/index.ts @@ -0,0 +1,317 @@ +import { MongoClient } from 'mongodb'; +import { InvalidAppConfigurationError } from 'named-app-errors'; +import { getEnv } from 'multiverse/next-env'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { Db } from 'mongodb'; + +// TODO: this package must be published transpiled to cjs by babel but NOT +// TODO: webpacked! + +const debug = debugFactory('mongo-schema:db'); +let memory: InternalMemory = getInitialInternalMemoryState(); + +type createIndexParams = Parameters; + +/** + * An internal cache of connection, server schema, and database state. + */ +export type InternalMemory = { + /** + * Memoized resolved database schemas and aliases. + */ + schema: DbSchema | null; + /** + * Memoized MongoDB driver client connection. + */ + client: MongoClient | null; + /** + * Memoized MongoDB driver Database instances. + */ + databases: Record; +}; + +/** + * A configuration object representing a MongoDB collection. + */ +export type CollectionSchema = { + /** + * The valid MongoDB name of the collection. + */ + name: string; + /** + * An object passed directly to the MongoDB `createCollection` function via + * the `createOptions` parameter. + */ + createOptions?: Parameters[1]; + /** + * An object representing indices to be created on the MongoDB collection via + * `createIndex`. + */ + indices?: { + spec: createIndexParams[1]; + options?: createIndexParams[2]; + }[]; +}; + +/** + * A configuration object representing one or more MongoDB databases and their + * aliases. + */ +export type DbSchema = { + /** + * All databases known to this system. These can be accessed via `getDb`. + */ + databases: Record< + string, + { + /** + * An array of MongoDB collections. + */ + collections: (string | CollectionSchema)[]; + } + >; + + /** + * These are alternative names to use with `getDb` that map to the names of + * databases known to this system. Aliases are specified as `alias: + * real-name`. + */ + aliases: Record; +}; + +/** + * Returns a copy of the initial state of internal memory. Useful when + * overwriting internal memory. + */ +export function getInitialInternalMemoryState(): InternalMemory { + return { + schema: null, + client: null, + databases: {} + }; +} + +/** + * Imports `getSchemaConfig` from "configverse/get-schema-config", calls it, and + * memoizes the result. + */ +export async function getSchemaConfig(): Promise { + if (memory.schema) { + debug('returning schema configuration from memory'); + return memory.schema; + } else { + try { + debug('importing `getSchemaConfig` from "configverse/get-schema-config"'); + return (memory.schema = await ( + await import('configverse/get-schema-config') + ).getSchemaConfig()); + } catch (e) { + debug.warn( + `failed to import getSchemaConfig from "configverse/get-schema-config": ${e}` + ); + + throw new InvalidAppConfigurationError( + 'could not resolve mongodb schema configuration: failed to import getSchemaConfig from "configverse/get-schema-config". Did you forget to register "configverse/get-schema-config" as an import alias/path?' + ); + } + } +} + +/** + * Mutates internal memory. Used for testing purposes. + */ +export function overwriteMemory(newMemory: InternalMemory) { + memory = newMemory; + debug('internal memory overwritten'); +} + +/** + * Lazily connects to the server on-demand, memoizing the result. + */ +export async function getClient() { + if (!memory.client) { + const uri = getEnv().MONGODB_URI; + debug(`connecting to mongo server at ${uri}`); + memory.client = await MongoClient.connect(uri); + } else { + debug('connected (from memory) to mongo server'); + } + + return memory.client; +} + +/** + * Kills the MongoClient instance and any related database connections and + * clears internal memory. + */ +export async function closeClient() { + /* istanbul ignore else */ + if (memory?.client) { + debug('closing server connection'); + await memory.client.close(true); + } + + memory = getInitialInternalMemoryState(); +} + +/** + * Accepts a database alias (or real name) and returns its real name. If the + * actual database is not listed in the schema, an error is thrown. + */ +export async function getNameFromAlias(alias: string) { + const schema = await getSchemaConfig(); + const nameActual = schema.aliases[alias] || alias; + + if (alias != nameActual) { + debug(`mapped alias "${alias}" to database name "${nameActual}"`); + } + + if (!schema.databases[nameActual]?.collections) { + throw new InvalidAppConfigurationError( + `database "${nameActual}" is not defined in schema` + ); + } + + return nameActual; +} + +/** + * Accepts a database name (or an alias) and returns one or more aliases. If the + * named database has no aliases listed in the schema, said database name is + * returned as a single-element array. If said database name is not listed in + * the schema, an error is thrown. + */ +export async function getAliasFromName(nameActual: string) { + const schema = await getSchemaConfig(); + + if (!schema.databases[nameActual]?.collections) { + throw new InvalidAppConfigurationError( + `database "${nameActual}" is not defined in schema` + ); + } + + const aliases = Object.entries(schema.aliases) + .filter(([, name]) => name == nameActual) + .map(([alias]) => alias); + + if (aliases.length) { + debug( + `reverse-mapped database name "${nameActual}" to alias${ + aliases.length == 1 ? ` "${aliases.toString()}"` : `es: ${aliases.join(', ')}` + }` + ); + + return aliases; + } else { + return [nameActual]; + } +} + +/** + * Lazily connects to a database on-demand, memoizing the result. If the + * database does not yet exist, it is both created and initialized by this + * function. The latter can be prevented by setting `initialize` to `false`. + */ +export async function getDb({ + name, + initialize +}: { + /** + * The name or alias of the database to retrieve. + */ + name: string; + /** + * Set to `false` to prevent `getDb` from calling `initializeDb` if the + * database does not exist prior to acquiring it. + * + * @default true + */ + initialize?: boolean; +}) { + const nameActual = await getNameFromAlias(name); + + if (!memory.databases[nameActual]) { + debug(`acquiring mongo database "${nameActual}"`); + + const client = await getClient(); + const existingDatabases = ( + await client.db('admin').admin().listDatabases() + ).databases.map(({ name }) => name); + + memory.databases[nameActual] = client.db(nameActual); + + if (initialize !== false && !existingDatabases.includes(nameActual)) { + debug(`calling initializeDb since "${nameActual}" was just created`); + await initializeDb({ name: nameActual }); + } + } else { + debug(`acquired (from memory) mongo database "${nameActual}"`); + } + + return memory.databases[nameActual]; +} + +/** + * Drops a database, destroying its collections. If the database does not exist + * before calling this function, it will be created first then dropped. + * + * Note that this function does not clear the destroyed database's Db instance + * from internal memory for performance reasons. + */ +export async function destroyDb({ + name +}: { + /** + * The name or alias of the database to destroy. + */ + name: string; +}) { + const nameActual = await getNameFromAlias(name); + debug(`destroying database "${nameActual}" and its collections`); + return !memory.databases[nameActual] || (await getDb({ name })).dropDatabase(); +} + +/** + * Creates a database and initializes its collections. If the database does not + * exist before calling this function, it will be created first. This function + * should only be called on empty or brand new databases **and not on databases + * with pre-existing collections.** + */ +export async function initializeDb({ + name +}: { + /** + * The name or alias of the database to initialize. + */ + name: string; +}) { + const db = await getDb({ name, initialize: false }); + const nameActual = await getNameFromAlias(name); + + debug(`initializing database "${nameActual}"`); + + await Promise.all( + ( + await getSchemaConfig() + ).databases[nameActual].collections.map((colNameOrSchema) => { + const colSchema: CollectionSchema = + typeof colNameOrSchema == 'string' + ? { + name: colNameOrSchema + } + : colNameOrSchema; + + debug(`initializing collection "${nameActual}.${colSchema.name}"`); + return db + .createCollection(colSchema.name, colSchema.createOptions) + .then((col) => { + return Promise.all( + colSchema.indices?.map((indexSchema) => + col.createIndex(indexSchema.spec, indexSchema.options || {}) + ) || [] + ); + }); + }) + ); +} diff --git a/lib/mongo-schema/package.json b/lib/mongo-schema/package.json new file mode 100644 index 0000000..a62764a --- /dev/null +++ b/lib/mongo-schema/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/mongo-schema" +} diff --git a/lib/mongo-schema/unit.test.ts b/lib/mongo-schema/unit.test.ts new file mode 100644 index 0000000..f77fd8a --- /dev/null +++ b/lib/mongo-schema/unit.test.ts @@ -0,0 +1,343 @@ +import { isolatedImportFactory, mockEnvFactory } from 'testverse/setup'; +import { Db, MongoClient } from 'mongodb'; +import { asMockedClass } from '@xunnamius/jest-types'; + +import type { TestCustomizations } from 'multiverse/mongo-test'; +import { getInitialInternalMemoryState } from 'multiverse/mongo-schema'; + +jest.mock('mongodb'); +jest.mock('configverse/get-schema-config', () => mockedMongoCustomizations); + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); + +const mockMongoClient = asMockedClass(MongoClient); +let mockedMongoCustomizations: TestCustomizations; + +const importDbLib = isolatedImportFactory({ + path: 'multiverse/mongo-schema' +}); + +beforeEach(() => { + mockedMongoCustomizations = mockedMongoCustomizations || {}; + + mockedMongoCustomizations.getSchemaConfig = async () => { + return { + databases: { + 'fake-db-1': { + collections: ['col'] + }, + + 'fake-db-2': { + collections: [ + 'col-1', + { name: 'col-2', createOptions: { capped: true } }, + { name: 'col-3', indices: [{ spec: 'some-key' }] }, + { + name: 'col-4', + indices: [{ spec: ['some-key', -1], options: { comment: '' } }] + } + ] + }, + + 'fake-db-3': { + collections: ['col'] + } + }, + aliases: { + 'fake-alias-1': 'fake-db-1', + 'fake-alias-2': 'fake-db-2', + 'fake-alias-3': 'fake-db-2' + } + }; + }; + + mockMongoClient.connect = jest.fn((url: string) => + Promise.resolve( + new (class { + url = url; + + db(name: string) { + return new (class { + parentUrl = url; + databaseName = name; + dropDatabase; + createCollection; + createIndex; + collection; + admin; + + constructor() { + this.dropDatabase = jest.fn(); + this.createIndex = jest.fn(); + // ? Reuse this.createIndex method for easy access to mock + this.collection = jest.fn(() => ({ insertMany: this.createIndex })); + this.createCollection = jest.fn(() => + Promise.resolve({ createIndex: this.createIndex }) + ); + this.admin = jest.fn(() => ({ + listDatabases: jest.fn(() => ({ + databases: [ + { name: 'auth' }, + { name: 'request-log' }, + { name: 'limited-log' } + ] + })) + })); + } + })(); + } + + close() { + return url; + } + })() as unknown as MongoClient + ) + ); +}); + +describe('::getSchemaConfig', () => { + it('dynamically imports customizations', async () => { + expect.hasAssertions(); + + await expect(importDbLib().getSchemaConfig()).resolves.toStrictEqual( + await mockedMongoCustomizations.getSchemaConfig() + ); + }); + + it('rejects if customizations are unavailable', async () => { + expect.hasAssertions(); + + // @ts-expect-error: don't care that we're deleting a non-optional prop + delete mockedMongoCustomizations.getSchemaConfig; + await expect(importDbLib().getSchemaConfig()).rejects.toThrow( + 'configverse/get-schema-config' + ); + }); +}); + +describe('::getClient', () => { + it("creates client if it doesn't already exist", async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const client = await lib.getClient(); + await expect(lib.getClient()).resolves.toBe(client); + expect(mockMongoClient.connect).toBeCalledTimes(1); + expect(client.close()).toBe('abc'); + }, + { MONGODB_URI: 'abc' } + ); + }); +}); + +describe('::getDb', () => { + it("creates db and connection if it doesn't already exist", async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + expect(mockMongoClient.connect).toBeCalledTimes(0); + const db = await lib.getDb({ name: 'fake-db-1' }); + await expect(lib.getDb({ name: 'fake-db-1' })).resolves.toBe(db); + expect(mockMongoClient.connect).toBeCalledTimes(1); + await expect(lib.getDb({ name: 'fake-db-2' })).resolves.not.toBe(db); + expect(mockMongoClient.connect).toBeCalledTimes(1); + expect(db.databaseName).toBe('fake-db-1'); + }, + { MONGODB_URI: 'abc' } + ); + }); + + it('automatically initializes newly created databases unless initialize is false', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const db = await lib.getDb({ name: 'fake-db-1', initialize: false }); + expect(db.createCollection).not.toBeCalled(); + await lib.getDb({ name: 'fake-db-1' }); + expect(db.createCollection).not.toBeCalled(); + const db2 = await lib.getDb({ name: 'fake-db-2' }); + expect(db2.createCollection).toBeCalled(); + await lib.getDb({ name: 'fake-db-2' }); + expect(db2.createCollection).toBeCalled(); + }, + { MONGODB_URI: 'abc' } + ); + }); + + it('returns db using alias', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const db1 = await lib.getDb({ name: 'fake-db-1' }); + await expect(lib.getDb({ name: 'fake-alias-1' })).resolves.toBe(db1); + + const db2 = await lib.getDb({ name: 'fake-alias-2' }); + await expect(lib.getDb({ name: 'fake-db-2' })).resolves.toBe(db2); + }, + { MONGODB_URI: 'abc' } + ); + }); +}); + +describe('::overwriteMemory', () => { + it('replaces memory when called', async () => { + expect.hasAssertions(); + + const client = new (class {})() as MongoClient; + const databases = { 'fake-db-1': new (class {})() as Db }; + const lib = importDbLib(); + + lib.overwriteMemory({ ...getInitialInternalMemoryState(), client, databases }); + + await expect(lib.getClient()).resolves.toBe(client); + await expect(lib.getDb({ name: 'fake-db-1' })).resolves.toBe( + databases['fake-db-1'] + ); + }); +}); + +describe('::closeClient', () => { + it('closes client and deletes memory', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const client = await lib.getClient(); + await expect(lib.getClient()).resolves.toBe(client); + await lib.closeClient(); + await expect(lib.getClient()).resolves.not.toBe(client); + }, + { MONGODB_URI: 'abc' } + ); + }); +}); + +describe('::destroyDb', () => { + it('drops database', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const db = await lib.getDb({ name: 'fake-db-1' }); + expect(db.dropDatabase).toBeCalledTimes(0); + await lib.destroyDb({ name: 'fake-db-2' }); + expect(db.dropDatabase).toBeCalledTimes(0); + await lib.destroyDb({ name: 'fake-db-1' }); + expect(db.dropDatabase).toBeCalledTimes(1); + }, + { MONGODB_URI: 'abc' } + ); + }); +}); + +describe('::getNameFromAlias', () => { + it('returns an actual database name', async () => { + expect.hasAssertions(); + await expect(importDbLib().getNameFromAlias('fake-alias-2')).resolves.toBe( + 'fake-db-2' + ); + }); + + it('passes through actual database name if given', async () => { + expect.hasAssertions(); + + await expect(importDbLib().getNameFromAlias('fake-db-3')).resolves.toBe( + 'fake-db-3' + ); + }); + + it('throws if database is not in schema', async () => { + expect.hasAssertions(); + await expect(importDbLib().getNameFromAlias('fake-alias-x')).rejects.toThrow( + 'database "fake-alias-x" is not defined' + ); + }); +}); + +describe('::getAliasFromName', () => { + it('returns one or more aliases', async () => { + expect.hasAssertions(); + + await expect(importDbLib().getAliasFromName('fake-db-1')).resolves.toStrictEqual([ + 'fake-alias-1' + ]); + + await expect(importDbLib().getAliasFromName('fake-db-2')).resolves.toStrictEqual([ + 'fake-alias-2', + 'fake-alias-3' + ]); + }); + + it('passes through actual database name if given', async () => { + expect.hasAssertions(); + + await expect(importDbLib().getAliasFromName('fake-db-3')).resolves.toStrictEqual([ + 'fake-db-3' + ]); + }); + + it('throws if database is not in schema', async () => { + expect.hasAssertions(); + await expect(importDbLib().getAliasFromName('fake-alias-3')).rejects.toThrow( + 'database "fake-alias-3" is not defined' + ); + }); +}); + +describe('::initializeDb', () => { + it("initializes a database's collections according to schema", async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + + await withMockedEnv( + async () => { + const schema = await lib.getSchemaConfig(); + const db1 = await lib.getDb({ name: 'fake-db-1' }); + const db2 = await lib.getDb({ name: 'fake-db-2' }); + + await lib.initializeDb({ name: 'fake-db-1' }); + await lib.initializeDb({ name: 'fake-db-2' }); + + schema.databases['fake-db-1'].collections.forEach((col) => { + expect(db1.createCollection).toBeCalledWith( + ...(typeof col == 'string' + ? [col, undefined] + : [col.name, col.createOptions]) + ); + }); + + schema.databases['fake-db-2'].collections.forEach((col) => { + if (typeof col == 'string') { + expect(db2.createCollection).toBeCalledWith(col, undefined); + } else { + expect(db2.createCollection).toBeCalledWith(col.name, col.createOptions); + + if (col.indices) { + col.indices.forEach((spec) => + expect(db2.createIndex).toBeCalledWith(spec.spec, spec.options || {}) + ); + } + } + }); + }, + { MONGODB_URI: 'abc' } + ); + }); +}); diff --git a/lib/mongo-test/index.ts b/lib/mongo-test/index.ts new file mode 100644 index 0000000..2866a2a --- /dev/null +++ b/lib/mongo-test/index.ts @@ -0,0 +1,255 @@ +import { MongoClient } from 'mongodb'; +import { getEnv } from 'multiverse/next-env'; +import { MongoMemoryServer } from 'mongodb-memory-server'; +import { InvalidAppConfigurationError, TrialError } from 'named-app-errors'; +import { debugFactory } from 'multiverse/debug-extended'; +import inspector from 'inspector'; + +import { + getSchemaConfig, + overwriteMemory, + getDb, + getNameFromAlias, + initializeDb, + destroyDb, + closeClient, + getInitialInternalMemoryState, + getAliasFromName +} from 'multiverse/mongo-schema'; + +import type { Document } from 'mongodb'; +import type { DbSchema } from 'multiverse/mongo-schema'; + +// TODO: this package must be published transpiled to cjs by babel but NOT +// TODO: webpacked! + +const debug = debugFactory('mongo-test:test-db'); + +/** + * Generic dummy data used to hydrate databases and their collections. + */ +export type DummyData = { + /** + * The data inserted into each collection in the named database. + * `databaseName` can also be an alias. + */ + [databaseName: string]: { + /** + * Timestamp of when this dummy data was generated (in ms since unix epoch). + */ + _generatedAt: number; + + /** + * The objects (if array) or object (if non-array) inserted into the + * named collection. + */ + [collectionName: string]: unknown; + }; +}; + +/** + * For use when mocking the contents of files containing `getDummyData` and/or + * `getSchemaConfig`. + */ +export type TestCustomizations = { + getDummyData: () => Promise; + getSchemaConfig: () => Promise; +}; + +/** + * Imports `getDummyData` from "configverse/get-dummy-data" and calls it. + */ +export async function getDummyData(): Promise { + try { + debug('importing `getDummyData` from "configverse/get-dummy-data"'); + return await (await import('configverse/get-dummy-data')).getDummyData(); + } catch (e) { + debug.warn( + `failed to import getDummyData from "configverse/get-dummy-data": ${e}` + ); + + throw new InvalidAppConfigurationError( + 'could not resolve mongodb dummy data: failed to import getDummyData from "configverse/get-dummy-data". Did you forget to register "configverse/get-dummy-data" as an import alias/path?' + ); + } +} + +/** + * Fill an initialized database with data. You should call {@link initializeDb} + * before calling this function. + */ +export async function hydrateDb({ + name +}: { + /** + * The name or alias of the database to hydrate. + */ + name: string; +}) { + const db = await getDb({ name }); + const nameActual = await getNameFromAlias(name); + const aliases = await getAliasFromName(nameActual); + + debug(`hydrating database ${nameActual}`); + + const rawDummyData = await getDummyData(); + let dummyData = rawDummyData[nameActual]; + + if (aliases[0] != nameActual) { + const foundAliases = aliases.filter((alias) => !!rawDummyData[alias]); + + if (foundAliases.length > 1) { + throw new InvalidAppConfigurationError( + `the following aliases have duplicate dummy data specifications (only one may exist): ${foundAliases.join( + ', ' + )}` + ); + } + + const alias = foundAliases[0]; + + if (alias) { + if (dummyData) { + throw new InvalidAppConfigurationError( + `duplicate dummy data specifications for database "${nameActual}" and alias "${alias}"` + ); + } + + debug(`(using alias "${alias}")`); + dummyData = rawDummyData[alias]; + } + } + + if (!dummyData) { + throw new InvalidAppConfigurationError( + `dummy data for database "${nameActual}" does not exist` + ); + } + + const collectionNames = (await getSchemaConfig()).databases[ + nameActual + ].collections.map((col) => (typeof col == 'string' ? col : col.name)); + + await Promise.all( + Object.entries(dummyData).map(([colName, colSchema]) => { + if (colName != '_generatedAt') { + if (!collectionNames.includes(colName)) { + throw new InvalidAppConfigurationError( + `collection "${nameActual}.${colName}" referenced in dummy data is not defined in source db schema` + ); + } + + return db.collection(colName).insertMany([colSchema].flat() as Document[]); + } + }) + ); +} + +/** + * Setup per-test versions of the mongodb client and database connections using + * jest lifecycle hooks. + */ +export function setupMemoryServerOverride(params?: { + /** + * If `true`, `beforeEach` and `afterEach` lifecycle hooks are skipped and the + * database is initialized and hydrated once before all tests are run. **In + * this mode, all tests will share the same database state!** + * + * @default false + */ + defer?: boolean; +}) { + // ? If an error (like a bad schema config or misconfigured dummy dataset) + // ? occurs at any point (e.g. in one of the hooks), the other hooks should + // ? become noops. Without this, test database state may leak outside the test + // ? environment. If an .env file is defined, test state could leak into a + // ? real mongodb instance (super bad!!!) + let errored = false; + + const port: number | undefined = + // * https://stackoverflow.com/a/67445850/1367414 + ((getEnv().DEBUG_INSPECTING || inspector.url() !== undefined) && + getEnv().MONGODB_MS_PORT) || + undefined; + + debug(`using ${port ? `port ${port}` : 'random port'} for mongo memory server`); + + // * The in-memory server is not started until it's needed later on + const server = new MongoMemoryServer({ + instance: { + port + // ? MongoDB errors WITHOUT this line as of version 4.x + // ? However, MongoDB errors WITH this line as of version 5.x 🙃 + // args: ['--enableMajorityReadConcern=0'] + } + }); + + /** + * Reset the dummy MongoDb server databases back to their initial states. + */ + const reinitializeServer = async () => { + try { + if (errored) { + debug.warn( + 'reinitialization was skipped due to a previous jest lifecycle errors' + ); + } else { + const databases = Object.keys((await getSchemaConfig()).databases); + debug('reinitializing mongo databases'); + await Promise.all( + databases.map((name) => + destroyDb({ name }) + .then(() => initializeDb({ name })) + .then(() => hydrateDb({ name })) + ) + ); + } + } catch (e) { + errored = true; + debug.error('an error occurred during reinitialization'); + throw e; + } + }; + + beforeAll(async () => { + try { + if (errored) { + debug.warn( + '"beforeAll" jest lifecycle hook was skipped due to previous errors' + ); + } else { + await server.ensureInstance(); + const uri = server.getUri(); + debug(`connecting to in-memory dummy mongo server at ${uri}`); + + if (port && !(uri.endsWith(`:${port}/`) || uri.endsWith(`:${port}`))) { + throw new TrialError( + `unable to start mongodb memory server: port ${port} seems to be in use` + ); + } + + overwriteMemory({ + ...getInitialInternalMemoryState(), + client: await MongoClient.connect(uri) + }); + + if (params?.defer) await reinitializeServer(); + } + } catch (e) { + errored = true; + debug.error('an error occurred within "beforeAll" lifecycle hook'); + throw e; + } + }); + + if (!params?.defer) { + beforeEach(reinitializeServer); + } + + afterAll(async () => { + await closeClient(); + await server.stop({ force: true }); + }); + + return { reinitializeServer }; +} diff --git a/lib/mongo-test/package.json b/lib/mongo-test/package.json new file mode 100644 index 0000000..0dfe940 --- /dev/null +++ b/lib/mongo-test/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/mongo-test" +} diff --git a/lib/mongo-test/test/integration.test.ts b/lib/mongo-test/test/integration.test.ts new file mode 100644 index 0000000..6b5fc9e --- /dev/null +++ b/lib/mongo-test/test/integration.test.ts @@ -0,0 +1,106 @@ +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { getClient } from 'multiverse/mongo-schema'; + +import type { TestCustomizations } from 'multiverse/mongo-test'; + +jest.mock('configverse/get-schema-config', () => mockedMongoCustomizations); +jest.mock('configverse/get-dummy-data', () => mockedMongoCustomizations); + +const now = Date.now(); + +let mockedMongoCustomizations: TestCustomizations; + +beforeEach(() => { + mockedMongoCustomizations = mockedMongoCustomizations || {}; + + mockedMongoCustomizations.getSchemaConfig = async () => { + return { + databases: { + 'db-1': { + collections: ['col'] + }, + + 'db-2': { + collections: [ + 'col-1', + { name: 'col-2', createOptions: { capped: true, size: 256 } }, + { + name: 'col-3', + indices: [ + { spec: ['key', -1], options: { unique: true } }, + { spec: 'item' } + ] + } + ] + } + }, + aliases: { + 'alias-1': 'db-1', + 'alias-2': 'db-2' + } + }; + }; + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'db-1': { + _generatedAt: now, + col: [{ item: 1 }, { item: 2 }, { item: 3 }] + }, + 'db-2': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }], + 'col-2': [{ item: 'c' }], + 'col-3': [{ key: 1, item: 'd' }] + } + }; + }; +}); + +describe('[run using non-deferred setupMemoryServerOverride]', () => { + setupMemoryServerOverride(); + + it('setupMemoryServerOverride works', async () => { + expect.hasAssertions(); + + const client = await getClient(); + const db1 = client.db('db-1'); + const db2 = client.db('db-2'); + + await expect(db1.listCollections().next()).resolves.toStrictEqual( + expect.objectContaining({ name: 'col', options: {} }) + ); + + await expect(db2.listCollections().toArray()).resolves.toIncludeAllPartialMembers( + [ + { name: 'col-1', options: {} }, + { name: 'col-2', options: { capped: true, size: 256 } }, + { name: 'col-3', options: {} } + ] + ); + + await expect( + db2.collection('col-3').listIndexes().toArray() + ).resolves.toIncludeAllPartialMembers([ + { key: { _id: 1 } }, + { key: { key: 1 }, unique: true }, + { key: { item: 1 } } + ]); + + await expect( + db1.collection('col').find().toArray() + ).resolves.toIncludeAllPartialMembers([{ item: 1 }, { item: 2 }, { item: 3 }]); + + await expect( + db2.collection('col-1').find().toArray() + ).resolves.toIncludeAllPartialMembers([{ item: 'a' }, { item: 'b' }]); + + await expect( + db2.collection('col-2').find().toArray() + ).resolves.toIncludeAllPartialMembers([{ item: 'c' }]); + + await expect( + db2.collection('col-3').find().toArray() + ).resolves.toIncludeAllPartialMembers([{ key: 1, item: 'd' }]); + }); +}); diff --git a/lib/mongo-test/test/unit.test.ts b/lib/mongo-test/test/unit.test.ts new file mode 100644 index 0000000..cc7892e --- /dev/null +++ b/lib/mongo-test/test/unit.test.ts @@ -0,0 +1,702 @@ +import { asMockedFunction, asMockedClass } from '@xunnamius/jest-types'; +import { isolatedImportFactory, mockEnvFactory } from 'testverse/setup'; +import { MongoClient } from 'mongodb'; +import { MongoMemoryServer } from 'mongodb-memory-server'; + +import type { TestCustomizations } from 'multiverse/mongo-test'; +import { DummyError } from 'named-app-errors'; +import { toss } from 'toss-expression'; + +jest.mock('mongodb'); +jest.mock('mongodb-memory-server'); + +jest.mock('multiverse/mongo-schema', () => { + if (mockedMongoSchema) { + return mockedMongoSchema; + } else { + return jest.requireActual('multiverse/mongo-schema'); + } +}); + +jest.mock('configverse/get-schema-config', () => mockedMongoCustomizations); +jest.mock('configverse/get-dummy-data', () => mockedMongoCustomizations); + +const now = Date.now(); +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); + +type MongoSchemaPackage = typeof import('multiverse/mongo-schema'); + +let mockedMongoSchema: MongoSchemaPackage | undefined; +let mockedMongoCustomizations: TestCustomizations; + +const mockMongoClient = asMockedClass(MongoClient); +const mockMongoMemoryServer = asMockedClass(MongoMemoryServer); + +const mockedMongoMemoryServer = { + ensureInstance: jest.fn(), + getUri: jest.fn(), + stop: jest.fn() +} as unknown as MongoMemoryServer; + +const importDbLib = isolatedImportFactory({ + path: 'multiverse/mongo-schema' +}); + +const importTestDbLib = isolatedImportFactory( + { + path: 'multiverse/mongo-test' + } +); + +beforeEach(() => { + mockedMongoSchema = undefined; + mockedMongoCustomizations = mockedMongoCustomizations || {}; + + mockedMongoCustomizations.getSchemaConfig = async () => { + return { + databases: { + 'fake-db-1': { + collections: ['col'] + }, + 'fake-db-2': { + collections: [ + 'col-1', + { name: 'col-2', createOptions: { capped: true } }, + { name: 'col-3', indices: [{ spec: 'some-key' }] }, + { + name: 'col-4', + indices: [{ spec: ['some-key', -1], options: { comment: '' } }] + } + ] + } + }, + aliases: { + 'fake-alias-1': 'fake-db-1', + 'fake-alias-2': 'fake-db-2', + 'fake-alias-3': 'fake-db-2' + } + }; + }; + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: now, + col: [{ item: 1 }, { item: 2 }, { item: 3 }] + }, + 'fake-db-2': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }], + 'col-does-not-exist': [{ fake: true }] + } + }; + }; + + mockMongoClient.connect = jest.fn(async (url: string) => { + return new (class { + url = url; + + db(name: string) { + return new (class { + parentUrl = url; + databaseName = name; + dropDatabase; + createCollection; + createIndex; + collection; + admin; + + constructor() { + this.dropDatabase = jest.fn(); + this.createIndex = jest.fn(); + // ? Reuse this.createIndex method for easy access to mock + this.collection = jest.fn(() => ({ insertMany: this.createIndex })); + this.createCollection = jest.fn(() => + Promise.resolve({ createIndex: this.createIndex }) + ); + this.admin = jest.fn(() => ({ + listDatabases: jest.fn(() => ({ + databases: [ + { name: 'auth' }, + { name: 'request-log' }, + { name: 'limited-log' } + ] + })) + })); + } + })(); + } + + close() { + return url; + } + })() as unknown as MongoClient; + }); + + mockMongoMemoryServer.mockImplementation(() => mockedMongoMemoryServer); +}); + +describe('::getDummyData', () => { + it('dynamically imports customizations', async () => { + expect.hasAssertions(); + + await expect(importTestDbLib().getDummyData()).resolves.toStrictEqual( + await mockedMongoCustomizations.getDummyData() + ); + }); + + it('rejects if customizations are unavailable', async () => { + expect.hasAssertions(); + + // @ts-expect-error: don't care that we're deleting a non-optional prop + delete mockedMongoCustomizations.getDummyData; + await expect(importTestDbLib().getDummyData()).rejects.toThrow( + 'configverse/get-dummy-data' + ); + }); +}); + +describe('::hydrateDb', () => { + it('fills a database with dummy data (multi-item collections)', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + const db = await lib.getDb({ name: 'fake-db-1' }); + + await expect(testLib.hydrateDb({ name: 'fake-db-1' })).resolves.toBeUndefined(); + + Object.entries((await testLib.getDummyData())['fake-db-1']).forEach( + ([colName, colData]) => { + if (colName != '_generatedAt') { + expect(db.collection).toBeCalledWith(colName); + // ? The createIndex method is reused for easy access to the insertMany mock + expect(db.createIndex).toBeCalledWith(colData); + } + } + ); + }); + + it('handles non-aliased databases with single-item collections', async () => { + expect.hasAssertions(); + + mockedMongoCustomizations.getSchemaConfig = async () => { + return { + databases: { + 'fake-db-1': { + collections: ['col'] + } + }, + aliases: {} + }; + }; + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: 0, + col: { item: 'single', name: 'just-the-one' } + } + }; + }; + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + const db = await lib.getDb({ name: 'fake-db-1' }); + + await expect(testLib.hydrateDb({ name: 'fake-db-1' })).resolves.toBeUndefined(); + + expect(db.collection).toBeCalledWith('col'); + // ? The createIndex method is reused for easy access to the insertMany mock + expect(db.createIndex).toBeCalledWith([ + (await testLib.getDummyData())['fake-db-1'].col + ]); + }); + + it('accepts an alias as a name', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + const db = await lib.getDb({ name: 'fake-alias-1' }); + + await expect( + testLib.hydrateDb({ name: 'fake-alias-1' }) + ).resolves.toBeUndefined(); + + Object.entries((await testLib.getDummyData())['fake-db-1']).forEach( + ([colName, colData]) => { + if (colName != '_generatedAt') { + expect(db.collection).toBeCalledWith(colName); + // ? The createIndex method is reused for easy access to the insertMany mock + expect(db.createIndex).toBeCalledWith(colData); + } + } + ); + }); + + it('reverse-maps database name to alias when necessary', async () => { + expect.hasAssertions(); + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: now, + col: [{ item: 1 }, { item: 2 }, { item: 3 }] + }, + 'fake-alias-3': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }] + } + }; + }; + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + const db = await lib.getDb({ name: 'fake-db-2' }); + + await expect(testLib.hydrateDb({ name: 'fake-db-2' })).resolves.toBeUndefined(); + + Object.entries((await testLib.getDummyData())['fake-alias-3']).forEach( + ([colName, colData]) => { + if (colName != '_generatedAt') { + expect(db.collection).toBeCalledWith(colName); + // ? The createIndex method is reused for easy access to the insertMany mock + expect(db.createIndex).toBeCalledWith(colData); + } + } + ); + }); + + it('throws if both actual database name and alias represent the same dummy data', async () => { + expect.hasAssertions(); + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: now, + col: [{ item: 1 }, { item: 2 }, { item: 3 }] + }, + 'fake-db-2': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }] + }, + 'fake-alias-3': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }] + } + }; + }; + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + + await expect(testLib.hydrateDb({ name: 'fake-alias-3' })).rejects.toThrow( + /duplicate dummy data specifications for database "fake-db-2" and alias "fake-alias-3"/ + ); + + await expect(testLib.hydrateDb({ name: 'fake-db-2' })).rejects.toThrow( + /duplicate dummy data specifications for database "fake-db-2" and alias "fake-alias-3"/ + ); + }); + + it('throws if two aliases represent the same dummy data', async () => { + expect.hasAssertions(); + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: now, + col: [{ item: 1 }, { item: 2 }, { item: 3 }] + }, + 'fake-alias-2': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }] + }, + 'fake-alias-3': { + _generatedAt: now, + 'col-1': [{ item: 'a' }, { item: 'b' }] + } + }; + }; + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + + await expect(testLib.hydrateDb({ name: 'fake-alias-2' })).rejects.toThrow( + /the following aliases have duplicate dummy data specifications \(only one may exist\): fake-alias-2, fake-alias-3/ + ); + + await expect(testLib.hydrateDb({ name: 'fake-alias-3' })).rejects.toThrow( + /the following aliases have duplicate dummy data specifications \(only one may exist\): fake-alias-2, fake-alias-3/ + ); + }); + + it('throws if database in schema has no corresponding dummy data', async () => { + expect.hasAssertions(); + + mockedMongoCustomizations.getSchemaConfig = async () => { + return { + databases: { + 'fake-db-2': { + collections: ['col'] + } + }, + aliases: {} + }; + }; + + mockedMongoCustomizations.getDummyData = async () => { + return { + 'fake-db-1': { + _generatedAt: 0, + col: { item: 'single', name: 'just-the-one' } + } + }; + }; + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + + await expect(testLib.hydrateDb({ name: 'fake-db-2' })).rejects.toThrow( + /dummy data for database "fake-db-2" does not exist/ + ); + }); + + it('throws if collection referenced in dummy data is not in schema', async () => { + expect.hasAssertions(); + + const lib = importDbLib(); + mockedMongoSchema = lib; + const testLib = importTestDbLib(); + + await expect(testLib.hydrateDb({ name: 'fake-db-2' })).rejects.toThrow( + /collection "fake-db-2.col-does-not-exist" referenced in dummy data is not defined in source db schema/ + ); + }); +}); + +describe('::setupMemoryServerOverride', () => { + it('registers jest hooks with respect to defer', async () => { + expect.hasAssertions(); + + const oldBeforeAll = beforeAll; + const oldBeforeEach = beforeEach; + const oldAfterAll = afterAll; + + try { + const testLib = importTestDbLib(); + + // eslint-disable-next-line no-global-assign + beforeAll = jest.fn(); + // eslint-disable-next-line no-global-assign + beforeEach = jest.fn(); + // eslint-disable-next-line no-global-assign + afterAll = jest.fn(); + + testLib.setupMemoryServerOverride(); + + expect(beforeAll).toBeCalledTimes(1); + expect(beforeEach).toBeCalledTimes(1); + expect(afterAll).toBeCalledTimes(1); + + testLib.setupMemoryServerOverride({ defer: true }); + + expect(beforeAll).toBeCalledTimes(2); + expect(beforeEach).toBeCalledTimes(1); + expect(afterAll).toBeCalledTimes(2); + } finally { + // eslint-disable-next-line no-global-assign + beforeAll = oldBeforeAll; + // eslint-disable-next-line no-global-assign + beforeEach = oldBeforeEach; + // eslint-disable-next-line no-global-assign + afterAll = oldAfterAll; + } + }); + + it('non-deferred hooks run', async () => { + expect.hasAssertions(); + + const oldBeforeAll = beforeAll; + const oldBeforeEach = beforeEach; + const oldAfterAll = afterAll; + + try { + await withMockedEnv(async () => { + const lib = importDbLib(); + + mockedMongoSchema = lib; + + const testLib = importTestDbLib(); + + const destroySpy = jest + .spyOn(lib, 'destroyDb') + .mockImplementation(async () => true); + + const initializeDbSpy = jest + .spyOn(lib, 'initializeDb') + .mockImplementation(async () => undefined); + + const hydrateDbSpy = jest + .spyOn(testLib, 'hydrateDb') + .mockImplementation(async () => undefined); + + const closeClientSpy = jest + .spyOn(lib, 'closeClient') + .mockImplementation(async () => undefined); + + // eslint-disable-next-line no-global-assign + beforeAll = jest.fn(); + // eslint-disable-next-line no-global-assign + beforeEach = jest.fn(); + // eslint-disable-next-line no-global-assign + afterAll = jest.fn(); + + testLib.setupMemoryServerOverride(); + + expect(beforeAll).toBeCalledTimes(1); + expect(beforeEach).toBeCalledTimes(1); + expect(afterAll).toBeCalledTimes(1); + + await asMockedFunction(beforeAll).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ); + + expect(destroySpy).not.toBeCalled(); + expect(initializeDbSpy).not.toBeCalled(); + expect(hydrateDbSpy).not.toBeCalled(); + expect(closeClientSpy).not.toBeCalled(); + // eslint-disable-next-line jest/unbound-method + expect(asMockedFunction(mockedMongoMemoryServer.stop)).not.toBeCalled(); + + await asMockedFunction(afterAll).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ); + + expect(closeClientSpy).toBeCalled(); + // eslint-disable-next-line jest/unbound-method + expect(asMockedFunction(mockedMongoMemoryServer.stop)).toBeCalled(); + + testLib.setupMemoryServerOverride({ defer: true }); + + expect(beforeAll).toBeCalledTimes(2); + expect(beforeEach).toBeCalledTimes(1); + expect(afterAll).toBeCalledTimes(2); + + await asMockedFunction(beforeAll).mock.calls[1][0]( + undefined as unknown as jest.DoneCallback + ); + + Object.keys( + (await mockedMongoCustomizations.getSchemaConfig()).databases + ).map((name) => { + expect(destroySpy).toBeCalledWith({ name }); + expect(initializeDbSpy).toBeCalledWith({ name }); + expect(hydrateDbSpy).toBeCalledWith({ name }); + }); + + await asMockedFunction(afterAll).mock.calls[1][0]( + undefined as unknown as jest.DoneCallback + ); + + expect(closeClientSpy).toBeCalledTimes(2); + // eslint-disable-next-line jest/unbound-method + expect(asMockedFunction(mockedMongoMemoryServer.stop)).toBeCalledTimes(2); + }); + } finally { + // eslint-disable-next-line no-global-assign + beforeAll = oldBeforeAll; + // eslint-disable-next-line no-global-assign + beforeEach = oldBeforeEach; + // eslint-disable-next-line no-global-assign + afterAll = oldAfterAll; + } + }); + + it('uses the debug port when inspecting', async () => { + expect.hasAssertions(); + + const oldBeforeAll = beforeAll; + const oldBeforeEach = beforeEach; + const oldAfterAll = afterAll; + + try { + const testLib = importTestDbLib(); + + // eslint-disable-next-line no-global-assign + beforeAll = jest.fn(); + // eslint-disable-next-line no-global-assign + beforeEach = jest.fn(); + // eslint-disable-next-line no-global-assign + afterAll = jest.fn(); + + await withMockedEnv( + async () => { + testLib.setupMemoryServerOverride(); + expect(mockMongoMemoryServer).toBeCalledWith({ + instance: expect.objectContaining({ port: 5678 }) + }); + }, + { + VSCODE_INSPECTOR_OPTIONS: 'exists', + MONGODB_MS_PORT: '5678' + } + ); + } finally { + // eslint-disable-next-line no-global-assign + beforeAll = oldBeforeAll; + // eslint-disable-next-line no-global-assign + beforeEach = oldBeforeEach; + // eslint-disable-next-line no-global-assign + afterAll = oldAfterAll; + } + }); + + it('hook rejects if port does not match uri (EADDRINUSE)', async () => { + expect.hasAssertions(); + + const oldBeforeAll = beforeAll; + const oldBeforeEach = beforeEach; + const oldAfterAll = afterAll; + + try { + const testLib = importTestDbLib(); + + // eslint-disable-next-line no-global-assign + beforeAll = jest.fn(); + // eslint-disable-next-line no-global-assign + beforeEach = jest.fn(); + // eslint-disable-next-line no-global-assign + afterAll = jest.fn(); + + // eslint-disable-next-line jest/unbound-method + asMockedFunction(mockedMongoMemoryServer.getUri).mockImplementationOnce( + () => 'uri-not-ending-in-colon-5678' + ); + + await withMockedEnv( + async () => { + testLib.setupMemoryServerOverride(); + + await expect( + asMockedFunction(beforeAll).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).rejects.toThrow('port 5678 seems to be in use'); + }, + { + VSCODE_INSPECTOR_OPTIONS: 'exists', + MONGODB_MS_PORT: '5678' + } + ); + } finally { + // eslint-disable-next-line no-global-assign + beforeAll = oldBeforeAll; + // eslint-disable-next-line no-global-assign + beforeEach = oldBeforeEach; + // eslint-disable-next-line no-global-assign + afterAll = oldAfterAll; + } + }); + + it('any rejection turns lifecycle hooks into noops', async () => { + expect.hasAssertions(); + + const oldBeforeAll = beforeAll; + const oldBeforeEach = beforeEach; + const oldAfterAll = afterAll; + + try { + const lib = importDbLib(); + + mockedMongoSchema = lib; + + const testLib = importTestDbLib(); + + const destroySpy = jest + .spyOn(lib, 'destroyDb') + .mockImplementation(async () => true); + + // eslint-disable-next-line no-global-assign + beforeAll = jest.fn(); + // eslint-disable-next-line no-global-assign + beforeEach = jest.fn(); + // eslint-disable-next-line no-global-assign + afterAll = jest.fn(); + + // eslint-disable-next-line jest/unbound-method + asMockedFunction(mockedMongoMemoryServer.getUri).mockImplementationOnce( + () => 'uri-not-ending-in-colon-5678' + ); + + await withMockedEnv( + async () => { + testLib.setupMemoryServerOverride(); + + await expect( + asMockedFunction(beforeAll).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).rejects.toThrow('port 5678 seems to be in use'); + + // ? Calling it a second time turns it into a noop + await expect( + asMockedFunction(beforeAll).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).resolves.toBeUndefined(); + + // ? Other hooks are also noops + await expect( + asMockedFunction(beforeEach).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).resolves.toBeUndefined(); + + expect(destroySpy).not.toBeCalled(); + }, + { + VSCODE_INSPECTOR_OPTIONS: 'exists', + MONGODB_MS_PORT: '5678' + } + ); + + asMockedFunction(beforeAll).mockReset(); + asMockedFunction(beforeEach).mockReset(); + asMockedFunction(afterAll).mockReset(); + jest + .spyOn(lib, 'getSchemaConfig') + .mockImplementation(() => toss(new DummyError())); + + testLib.setupMemoryServerOverride(); + + await expect( + asMockedFunction(beforeEach).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).rejects.toThrowError(DummyError); + + // ? Calling it a second time turns it into a noop + await expect( + asMockedFunction(beforeEach).mock.calls[0][0]( + undefined as unknown as jest.DoneCallback + ) + ).resolves.toBeUndefined(); + } finally { + // eslint-disable-next-line no-global-assign + beforeAll = oldBeforeAll; + // eslint-disable-next-line no-global-assign + beforeEach = oldBeforeEach; + // eslint-disable-next-line no-global-assign + afterAll = oldAfterAll; + } + }); +}); diff --git a/lib/next-adhesive/add-raw-body.ts b/lib/next-adhesive/add-raw-body.ts new file mode 100644 index 0000000..f47f9f3 --- /dev/null +++ b/lib/next-adhesive/add-raw-body.ts @@ -0,0 +1,167 @@ +import { sendHttpTooLarge } from 'multiverse/next-api-respond'; +import { + ClientValidationError, + InvalidAppConfigurationError +} from 'named-app-errors'; +import { debugFactory } from 'multiverse/debug-extended'; +import { parse } from 'content-type'; +import getRawBody, { RawBodyError } from 'raw-body'; +import querystring from 'node:querystring'; +import { isError } from '@xunnamius/types'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:add-raw-body'); + +// * https://xunn.at/source-nextjs-defaultbodylimit +const defaultRequestBodySizeLimit = '1mb'; + +const isRawBodyError = (e: unknown): e is RawBodyError => { + return isError(e) && typeof (e as RawBodyError).type == 'string'; +}; + +/** + * The shape of an object (typically a NextApiRequest object) that has a rawBody + * property. + */ +export type WithRawBody = T & { + /** + * The raw request body exactly as it was received (as a string parsed by + * `raw-body`). + */ + rawBody: string; +}; + +export type Options = { + /** + * The byte limit of the request body. This is the number of bytes or any + * string format supported by bytes, for example `1000`, `'500kb'` or `'3mb'`. + * + * @default defaultRequestBodySizeLimit (see source) + * + * @see https://nextjs.org/docs/api-routes/api-middlewares#custom-config + */ + requestBodySizeLimit?: number | string | null; +}; + +/** + * Type predicate function that returns `true` if the request object can + * satisfy the `WithRawBody` type constraint. + */ +export function isNextApiRequestWithRawBody( + req: NextApiRequest +): req is WithRawBody { + return (req as WithRawBody).rawBody !== undefined; +} + +/** + * Type guard function similar to the `isNextApiRequestWithRawBody` type + * predicate except an error is thrown if the request object cannot satisfy the + * `WithRawBody` type constraint. + */ +export function ensureNextApiRequestHasRawBody( + req: NextApiRequest +): req is WithRawBody { + if ((req as WithRawBody).rawBody !== undefined) { + return true; + } else { + throw new InvalidAppConfigurationError( + 'encountered a NextApiRequest object without a rawBody property' + ); + } +} + +/** + * Adds a `rawBody` property onto the NextApiRequest object, which contains the + * raw unparsed content of the request body if it exists or `undefined` if it + * does not. The body is still parsed (using `bodyParser`) like normal using a + * custom implementation of Next.js's `parseBody` function. + * + * To use this middleware, `bodyParser` must be disabled via Next.js API route + * configuration like so: + * + * ```TypeScript + * export const config = { + * api: { + * bodyParser: false + * }, + * } + * ``` + * + * Note that this middleware cannot be used with other middleware or routes that + * also directly consume the request body in a special way, such as when using + * streams. + * + * @see https://nextjs.org/docs/api-routes/api-middlewares#custom-config + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + if (req.body !== undefined) { + throw new InvalidAppConfigurationError( + "Next.js's body parser must be disabled when using add-raw-body middleware" + ); + } else if (isNextApiRequestWithRawBody(req)) { + throw new InvalidAppConfigurationError( + 'NextApiRequest object already has a defined "rawBody" property (is the add-raw-body middleware obsolete?)' + ); + } else { + debug('adding "rawBody" property to request object via custom body parsing'); + + // * The below code was adapted from https://xunn.at/source-nextjs-parsebody + + let contentType; + + try { + contentType = parse(req.headers['content-type'] || 'text/plain'); + } catch { + contentType = parse('text/plain'); + } + + const { type, parameters } = contentType; + const encoding = parameters.charset || 'utf-8'; + const limit = context.options.requestBodySizeLimit || defaultRequestBodySizeLimit; + + let buffer; + + try { + buffer = (await getRawBody(req, { encoding, limit })).toString(); + } catch (e) { + if (isRawBodyError(e) && e.type == 'entity.too.large') { + sendHttpTooLarge(res, { error: `body exceeded ${limit} size limit` }); + } else { + throw new ClientValidationError('invalid body'); + } + } + + if (buffer !== undefined) { + const finalReq = req as WithRawBody; + finalReq.rawBody = buffer; + + if (type === 'application/json' || type === 'application/ld+json') { + debug('secondary parsing of body as JSON data'); + if (finalReq.rawBody.length === 0) { + // special-case empty json body, as it's a common client-side mistake + finalReq.body = {}; + } else { + try { + finalReq.body = JSON.parse(finalReq.rawBody); + } catch { + throw new ClientValidationError('invalid JSON body'); + } + } + } else if (type === 'application/x-www-form-urlencoded') { + debug('secondary parsing of body as urlencoded form data'); + finalReq.body = querystring.decode(finalReq.rawBody); + } else { + debug('no secondary parsing of body (passthrough)'); + finalReq.body = finalReq.rawBody; + } + } + } +} diff --git a/lib/next-adhesive/auth-request.ts b/lib/next-adhesive/auth-request.ts new file mode 100644 index 0000000..67ec0c8 --- /dev/null +++ b/lib/next-adhesive/auth-request.ts @@ -0,0 +1,115 @@ +import { debugFactory } from 'multiverse/debug-extended'; +import { + authenticateHeader, + authorizeHeader, + AuthScheme, + AuthConstraint +} from 'multiverse/next-auth'; + +import { + sendHttpUnauthenticated, + sendHttpUnauthorized +} from 'multiverse/next-api-respond'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; +import { InvalidAppConfigurationError } from 'named-app-errors'; + +const debug = debugFactory('next-adhesive:auth-request'); + +export type Options = { + /** + * If not `false` or falsy, accessing this endpoint requires a valid (yet + * unfortunately named) Authorization header. + * + * If one or more schemes are provided, the request will be authenticated + * using one of said schemes. If no schemes are provided, the request will be + * authenticated using any available scheme. + * + * Additionally, if one or more constraints are provided, the request will be + * authorized conditioned upon said constraints. If no constraints are + * provided, all requests will be vacuously authorized. + */ + requiresAuth?: + | boolean + | { + allowedSchemes?: AuthScheme | AuthScheme[]; + constraints?: AuthConstraint | AuthConstraint[]; + }; +}; + +/** + * Rejects unauthenticatable and unauthorizable requests (via Authorization + * header). + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + const { authorization: header } = req.headers; + + if ( + typeof context.options.requiresAuth != 'boolean' && + (!context.options.requiresAuth || typeof context.options.requiresAuth != 'object') + ) { + throw new InvalidAppConfigurationError( + 'a valid "requiresAuth" option is missing from middleware configuration' + ); + } + + if (context.options.requiresAuth) { + const allowedSchemes = + context.options.requiresAuth !== true + ? context.options.requiresAuth?.allowedSchemes + : undefined; + + const { authenticated, error: authenticationError } = await authenticateHeader({ + header, + allowedSchemes + }); + + if (!authenticated || authenticationError) { + debug( + `authentication check failed: ${ + authenticationError || 'bad Authorization header' + }` + ); + sendHttpUnauthenticated(res); + } else { + debug('authentication check succeeded: client is authenticated'); + + const constraints = + context.options.requiresAuth !== true + ? context.options.requiresAuth?.constraints + : undefined; + + if (constraints) { + debug(`authorization check required: ${constraints}`); + + const { authorized, error: authorizationError } = await authorizeHeader({ + header, + constraints + }); + + if (!authorized || authorizationError) { + debug( + `authorization check failed: ${ + authorizationError || 'bad Authorization header' + }` + ); + + sendHttpUnauthorized(res); + } + + debug('authorization check succeeded: client is authorized'); + } else { + debug('skipped authorization check'); + } + } + } else { + debug('skipped authentication and authorization checks'); + } +} diff --git a/lib/next-adhesive/check-content-type.ts b/lib/next-adhesive/check-content-type.ts new file mode 100644 index 0000000..3b0ed1e --- /dev/null +++ b/lib/next-adhesive/check-content-type.ts @@ -0,0 +1,179 @@ +import { debugFactory } from 'multiverse/debug-extended'; +import { ValidHttpMethod } from '@xunnamius/types'; +import { InvalidAppConfigurationError } from 'named-app-errors'; +import { toss } from 'toss-expression'; + +import { + sendHttpBadContentType, + sendHttpBadRequest +} from 'multiverse/next-api-respond'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:check-content-type'); + +/** + * The shape of a simple configuration object. + */ +export type AllowedContentTypesConfig = string[] | 'any' | 'none'; + +/** + * The shape of a complex configuration object. + */ +export type AllowedContentTypesPerMethodConfig = { + [method in ValidHttpMethod]?: AllowedContentTypesConfig; +}; + +export type Options = { + /** + * A string, a mapping, or an array of media types this endpoint is + * allowed to receive. + * + * If the string `"any"` is provided, any Content-Type header will be allowed, + * including requests without a Content-Type header. + * + * If the string `"none"` is provided, only requests without a Content-Type + * header will be allowed. Similarly, `"none"` can also be included in the + * array form to indicate that requests without a Content-Type header are + * allowed in addition to those with a listed media type. + * + * If a plain object is provided, it is assumed to be a mapping of HTTP method + * keys and media type values where each value is one of the string `"any"` or + * `"none"` or an array of media types / `"none"`s. In this form, these + * constraints are applied per request method. + * + * By default, _all_ requests using `POST`, `PUT`, and `PATCH` methods, or any + * request _with_ a Content-Type header, _will always be rejected_ unless + * configured otherwise. Requests _without_ a Content-Type header that are + * using methods other than `POST`, `PUT`, and `PATCH` _will always be + * allowed_ unless explicitly configured via mapping. + * + * @see https://www.iana.org/assignments/media-types/media-types.xhtml + */ + allowedContentTypes?: + | AllowedContentTypesConfig + | AllowedContentTypesPerMethodConfig; +}; + +/** + * Rejects requests that are not using an allowed content type. This middleware + * should usually come _after_ check-method. + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + const { allowedContentTypes: rawAllowedContentTypes } = context.options; + const contentType = req.headers['content-type']?.toLowerCase(); + const method = req.method?.toUpperCase(); + + const configToLowercase = ( + c: AllowedContentTypesConfig + ): AllowedContentTypesConfig => { + return typeof c == 'string' + ? (c.toLowerCase() as typeof c) + : Array.isArray(c) + ? c.map((s) => s.toLowerCase()) + : toss( + new InvalidAppConfigurationError( + 'allowedContentTypes must adhere to type constraints' + ) + ); + }; + + // ? Ensure everything is lowercased before we begin + const allowed = (() => { + if (rawAllowedContentTypes) { + if ( + Array.isArray(rawAllowedContentTypes) || + typeof rawAllowedContentTypes == 'string' + ) { + return configToLowercase(rawAllowedContentTypes); + } else { + for (const [subMethod, config] of Object.entries(rawAllowedContentTypes)) { + if (config) { + rawAllowedContentTypes[subMethod as ValidHttpMethod] = + configToLowercase(config); + } + } + + return rawAllowedContentTypes; + } + } + })(); + + const sendError = () => { + const error = `unrecognized or disallowed Content-Type header for method ${method}: ${ + contentType ? `"${contentType}"` : '(none)' + }`; + + debug(`content-type check failed: ${error}`); + sendHttpBadContentType(res, { error }); + }; + + if (!method) { + debug('content-type check failed: method is undefined'); + sendHttpBadRequest(res, { error: 'undefined method' }); + } else { + const isPayloadMethod = ['PUT', 'POST', 'PATCH'].includes(method); + + if (!allowed) { + if (isPayloadMethod || contentType) { + debug( + 'content-type check failed: this request cannot be handled with the current configuration' + ); + sendHttpBadContentType(res, { + error: 'the server is not configured to handle this type of request' + }); + } + } else { + if (allowed == 'none') { + if (contentType) { + return sendError(); + } + } else if (allowed != 'any') { + if (Array.isArray(allowed)) { + if (isPayloadMethod || contentType) { + const allowsNone = allowed.includes('none'); + if (!contentType) { + if (!allowsNone) { + return sendError(); + } + } else if (contentType == 'none' || !allowed.includes(contentType)) { + return sendError(); + } + } + } else { + if (Object.keys(allowed).includes(method)) { + const allowedSubset = allowed[method as ValidHttpMethod]; + + if (allowedSubset == 'none') { + if (contentType) { + return sendError(); + } + } else if (allowedSubset && allowedSubset != 'any') { + const allowsNone = allowedSubset.includes('none'); + if (!contentType) { + if (!allowsNone) { + return sendError(); + } + } else if ( + contentType == 'none' || + !allowedSubset.includes(contentType) + ) { + return sendError(); + } + } + } else if (isPayloadMethod || contentType) { + return sendError(); + } + } + } + + debug(`content-type check succeeded: type "${contentType}" is allowed`); + } + } +} diff --git a/lib/next-adhesive/check-method.ts b/lib/next-adhesive/check-method.ts new file mode 100644 index 0000000..8abc2e2 --- /dev/null +++ b/lib/next-adhesive/check-method.ts @@ -0,0 +1,49 @@ +import { getEnv } from 'multiverse/next-env'; +import { sendHttpBadMethod } from 'multiverse/next-api-respond'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { ValidHttpMethod } from '@xunnamius/types'; +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:check-method'); + +export type Options = { + /** + * An array of HTTP methods this endpoint is allowed to serve. + */ + allowedMethods?: ValidHttpMethod[]; +}; + +/** + * Rejects requests that are either using a disallowed method or not using an + * allowed method. + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + debug(`original method: ${req.method}`); + + const method = req.method?.toUpperCase(); + const allowedMethods = context.options.allowedMethods?.map((m) => m.toUpperCase()); + + if ( + !method || + // ? Already guaranteed uppercase thanks to next-env + getEnv().DISALLOWED_METHODS.includes(method) || + !allowedMethods || + !allowedMethods.includes(method as ValidHttpMethod) + ) { + debug( + `method check failed: unrecognized or disallowed method "${method || '(none)'}"` + ); + + res.setHeader('Allow', allowedMethods?.join(',') || ''); + sendHttpBadMethod(res); + } else { + debug(`method check succeeded: method "${method}" is allowed`); + } +} diff --git a/lib/next-adhesive/check-version.ts b/lib/next-adhesive/check-version.ts new file mode 100644 index 0000000..ee4a738 --- /dev/null +++ b/lib/next-adhesive/check-version.ts @@ -0,0 +1,37 @@ +import { getEnv } from 'multiverse/next-env'; +import { sendHttpNotFound } from 'multiverse/next-api-respond'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:check-version'); + +export type Options = { + /** + * The version of the api this endpoint serves. + */ + apiVersion?: string; +}; + +/** + * Rejects requests to disabled versions of the API. + */ +export default async function ( + _req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + if (context.options.apiVersion !== undefined) { + if (getEnv().DISABLED_API_VERSIONS.includes(context.options.apiVersion)) { + debug('version check failed: endpoint is disabled'); + sendHttpNotFound(res); + } else { + debug('version check succeeded: endpoint is available'); + } + } else { + debug('skipped version check'); + } +} diff --git a/lib/next-adhesive/contrive-error.ts b/lib/next-adhesive/contrive-error.ts new file mode 100644 index 0000000..bec2e03 --- /dev/null +++ b/lib/next-adhesive/contrive-error.ts @@ -0,0 +1,39 @@ +import { isDueForContrivedError } from 'multiverse/next-contrived'; +import { sendHttpContrivedError } from 'multiverse/next-api-respond'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:contrive-error'); + +export type Options = { + /** + * If `true`, every Nth request will fail with a contrived error. + * + * @default false + */ + enableContrivedErrors?: boolean; +}; + +/** + * Rejects every Nth request with a dummy error (see .env.example). + */ +export default async function ( + _req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + if (context.options.enableContrivedErrors) { + if (await isDueForContrivedError()) { + debug('contrived error check determined client IS due for contrived error'); + sendHttpContrivedError(res); + } else { + debug('contrived error check determined client IS NOT due for contrived error'); + } + } else { + debug('skipped contrived error check'); + } +} diff --git a/lib/next-adhesive/handle-error.ts b/lib/next-adhesive/handle-error.ts new file mode 100644 index 0000000..939911c --- /dev/null +++ b/lib/next-adhesive/handle-error.ts @@ -0,0 +1,126 @@ +import { debugFactory } from 'multiverse/debug-extended'; + +import { + NotImplementedError, + GuruMeditationError, + ValidationError, + NotFoundError, + AuthError, + AppError, + AppValidationError +} from 'named-app-errors'; + +import { + sendHttpError, + sendHttpNotFound, + sendHttpUnauthorized, + sendHttpBadRequest, + sendNotImplemented +} from 'multiverse/next-api-respond'; + +import type { JsonError } from '@xunnamius/types'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { Promisable } from 'type-fest'; + +const debug = debugFactory('next-adhesive:handle-error'); + +/** + * Special middleware used to handle custom errors. + */ +export type ErrorHandler = ( + res: NextApiResponse, + errorJson: Partial +) => Promisable; + +/** + * A Map of Error class constructors to the special middleware that handles + * them. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ErrorHandlerMap = Map Error, ErrorHandler>; + +export type Options = { + /** + * A mapping of Error classes and the functions that handle them. + */ + errorHandlers?: ErrorHandlerMap; +}; + +/** + * Generic error handling middleware. **This should be among the final + * middleware to run on the error handling middleware chain.** + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + const { + runtime: { error }, + options: { errorHandlers } + } = context; + + if (res.writableEnded) { + // ? We're past the point where we're able to change the response. + debug('cannot handle error: response is no longer writable'); + debug('throwing unhandleable error'); + throw error; + } + + const errorJson: Partial = (error as { message: string }).message + ? { error: (error as { message: string }).message } + : {}; + + debug('handling error: %O', errorJson.error || '(no message)'); + + if (errorHandlers) { + for (const [errorType, errorHandler] of errorHandlers) { + if (error instanceof errorType) { + debug(`using custom error handler for type "${error.name}"`); + // eslint-disable-next-line no-await-in-loop + await errorHandler(res, errorJson); + return; + } + } + } + + debug( + `using default error handler${ + error instanceof Error ? ` for type "${error.name}"` : '' + }` + ); + + if (error instanceof GuruMeditationError) { + // eslint-disable-next-line no-console + console.error(`error - sanity check failed on request: ${req.url}\n`, error); + sendHttpError(res, { + error: 'sanity check failed: please report exactly what you did just now!' + }); + } else if (error instanceof AppValidationError) { + // eslint-disable-next-line no-console + console.error( + `error - server-side validation exception on request: ${req.url}\n`, + error + ); + sendHttpError(res, errorJson); + } else if (error instanceof ValidationError) { + sendHttpBadRequest(res, errorJson); + } else if (error instanceof AuthError) { + sendHttpUnauthorized(res, errorJson); + } else if (error instanceof NotFoundError) { + sendHttpNotFound(res, errorJson); + } else if (error instanceof NotImplementedError) { + sendNotImplemented(res); + } else if (error instanceof AppError) { + // eslint-disable-next-line no-console + console.error(`error - named exception on request: ${req.url}\n`, error); + sendHttpError(res, errorJson); + } else { + // eslint-disable-next-line no-console + console.error(`error - unnamed exception on request: ${req.url}\n`, error); + sendHttpError(res); + } +} diff --git a/lib/next-adhesive/limit-request.ts b/lib/next-adhesive/limit-request.ts new file mode 100644 index 0000000..2318357 --- /dev/null +++ b/lib/next-adhesive/limit-request.ts @@ -0,0 +1,42 @@ +import { getEnv } from 'multiverse/next-env'; +import { clientIsRateLimited } from 'multiverse/next-limit'; +import { debugFactory } from 'multiverse/debug-extended'; + +import { + sendHttpRateLimited, + sendHttpUnauthorized +} from 'multiverse/next-api-respond'; + +import type { NextApiRequest, NextApiResponse } from 'next'; + +const debug = debugFactory('next-adhesive:limit-request'); + +export type Options = { + // No options +}; + +/** + * Rejects requests from clients that have sent too many previous requests. + */ +export default async function (req: NextApiRequest, res: NextApiResponse) { + debug('entered middleware runtime'); + + if (getEnv().LOCKOUT_ALL_CLIENTS) { + debug('rate-limit check failed: all clients locked out'); + sendHttpUnauthorized(res, { + error: 'backend has temporarily locked out all clients' + }); + } else if (getEnv().IGNORE_RATE_LIMITS) { + debug('skipped rate-limit check'); + } else { + const { isLimited, retryAfter } = await clientIsRateLimited(req); + + if (isLimited) { + debug('rate-limit check failed: client is rate-limited'); + res.setHeader('Retry-After', Math.ceil(retryAfter / 1000)); + sendHttpRateLimited(res, { retryAfter }); + } else { + debug('rate-limit check succeeded: client not rate-limited'); + } + } +} diff --git a/lib/next-adhesive/log-request.ts b/lib/next-adhesive/log-request.ts new file mode 100644 index 0000000..f68e745 --- /dev/null +++ b/lib/next-adhesive/log-request.ts @@ -0,0 +1,46 @@ +import { randomUUID } from 'node:crypto'; +import { performance as perf } from 'node:perf_hooks'; + +import { addToRequestLog } from 'multiverse/next-log'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { MiddlewareContext } from 'multiverse/next-api-glue'; + +const debug = debugFactory('next-adhesive:log-request'); + +export type Options = { + // No options +}; + +/** + * Logs the response to each request after it is sent (i.e. `res.end()`). + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + const perfUUID = randomUUID(); + perf.mark(perfUUID); + + const send = res.end; + res.end = ((...args: Parameters) => { + const sent = res.writableEnded; + send(...args); + + if (!sent) { + debug('logging request after initial call to res.end'); + // ! Note that this async function is NOT awaited!!! + void addToRequestLog({ + req, + res, + endpoint: context.runtime.endpoint.descriptor, + // @ts-expect-error: @types/node is broken... *sigh* + durationMs: Math.floor(perf.measure(randomUUID(), perfUUID).duration) + }); + } + }) as typeof res.end; +} diff --git a/lib/next-adhesive/package.json b/lib/next-adhesive/package.json new file mode 100644 index 0000000..d8c4037 --- /dev/null +++ b/lib/next-adhesive/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-adhesive" +} diff --git a/lib/next-adhesive/test/unit-add-raw-body.test.ts b/lib/next-adhesive/test/unit-add-raw-body.test.ts new file mode 100644 index 0000000..43af155 --- /dev/null +++ b/lib/next-adhesive/test/unit-add-raw-body.test.ts @@ -0,0 +1,305 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { withMockedOutput, noopHandler, wrapHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; + +import addRawBody, { + ensureNextApiRequestHasRawBody, + isNextApiRequestWithRawBody +} from 'multiverse/next-adhesive/add-raw-body'; + +import type { NextApiRequest } from 'next'; +import type { WithRawBody, Options } from 'multiverse/next-adhesive/add-raw-body'; + +describe('::', () => { + it('throws if bodyParser is not disabled', async () => { + expect.hasAssertions(); + + const handler = wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody] + }), + {} + ); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler, + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('body parser must be disabled') + }); + }); + + handler.config = { api: { bodyParser: false } }; + + await testApiHandler({ + handler, + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + }); + + it('throws if rawBody property already defined on request object', async () => { + expect.hasAssertions(); + + const normalHandler = wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody] + }) + ); + + normalHandler.config = { api: { bodyParser: false } }; + + await testApiHandler({ + handler: normalHandler, + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + + const obsoleterHandler = wrapHandler(async (req, res) => { + (req as WithRawBody).rawBody = 'fake raw body'; + return withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody] + })(req, res); + }); + + obsoleterHandler.config = { api: { bodyParser: false } }; + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: obsoleterHandler, + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('already has a defined "rawBody" property') + }); + }); + }); + + it('throws on bad JSON body', async () => { + expect.hasAssertions(); + + const handler = wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody] + }) + ); + + handler.config = { api: { bodyParser: false } }; + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler, + test: async ({ fetch }) => + void (await fetch({ + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: '' + })) + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('invalid JSON body') + }); + }); + }); + + it('throws on invalid body (raw-body chokes)', async () => { + expect.hasAssertions(); + + const handler = wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody] + }) + ); + + handler.config = { api: { bodyParser: false } }; + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler, + requestPatcher(req) { + req.destroy(); + }, + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('invalid body') + }); + }); + }); + + it('adds rawBody to request object while still providing parsed body', async () => { + expect.hasAssertions(); + + const handler = wrapHandler( + withMiddleware( + (req, res) => { + if (ensureNextApiRequestHasRawBody(req)) { + res.status(200).send({ body: req.body, rawBody: req.rawBody }); + } + }, + { + descriptor: '/fake', + use: [addRawBody] + } + ) + ); + + handler.config = { api: { bodyParser: false } }; + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + let res, json, rawBody, jsonBody; + + // ? Works with empty body (which otherwise evaluates falsy) + res = await fetch(); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: '', rawBody: '' }); + + // ? Works with empty body as JSON + res = await fetch({ headers: { 'content-type': 'application/json' } }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: {}, rawBody: '' }); + + jsonBody = { a: 1, b: 'c', d: true }; + rawBody = JSON.stringify(jsonBody); + res = await fetch({ + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: rawBody + }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: jsonBody, rawBody }); + + jsonBody = { a: 2, b: 'z', d: false }; + rawBody = JSON.stringify(jsonBody); + res = await fetch({ + method: 'PUT', + headers: { 'content-type': 'application/ld+json' }, + body: rawBody + }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: jsonBody, rawBody }); + + jsonBody = { a: '3', b: 'd', e: 'true' }; + rawBody = 'a=3&b=d&e=true'; + res = await fetch({ + method: 'POST', + headers: { 'content-type': 'application/x-www-form-urlencoded' }, + body: rawBody + }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: jsonBody, rawBody }); + + rawBody = 'hello, world!'; + res = await fetch({ + method: 'POST', + headers: { 'content-type': 'text/plain' }, + body: rawBody + }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: rawBody, rawBody }); + + // ? Works with really bad content types + rawBody = 'hello, world!'; + res = await fetch({ + method: 'POST', + headers: { 'content-type': '/' }, + body: rawBody + }); + json = await res.json(); + + expect(res.status).toBe(200); + expect(json).toStrictEqual({ body: rawBody, rawBody }); + } + }); + }); + + it('respects requestBodySizeLimit option', async () => { + expect.hasAssertions(); + + const handler = wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [addRawBody], + options: { requestBodySizeLimit: 1 } + }) + ); + + handler.config = { api: { bodyParser: false } }; + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + expect((await fetch({ method: 'POST', body: 'x' })).status).toBe(200); + expect((await fetch({ method: 'POST', body: 'xx' })).status).toBe(413); + } + }); + }); +}); + +describe('::isNextApiRequestWithRawBody', () => { + it('functions properly as type predicate', async () => { + expect.hasAssertions(); + + const req = { rawBody: '' } as WithRawBody; + + if (isNextApiRequestWithRawBody(req)) { + // ? This test will "fail" during type checking if there is an error here + expect(req.rawBody).toBe(''); + } else { + // @ts-expect-error: test will "fail" during type checking if no error + expect(req.rawBody).toBe(''); + } + }); +}); + +describe('::ensureNextApiRequestHasRawBody', () => { + it('functions properly as type guard', async () => { + expect.hasAssertions(); + + const req = { rawBody: '' } as WithRawBody; + + if (ensureNextApiRequestHasRawBody(req)) { + // ? This test will "fail" during type checking if there is an error here + expect(req.rawBody).toBe(''); + } + }); + + it('throws if NextApiRequest object does not have raw body', async () => { + expect.hasAssertions(); + + expect(() => ensureNextApiRequestHasRawBody({} as NextApiRequest)).toThrow( + 'encountered a NextApiRequest object without a rawBody property' + ); + + expect(() => + ensureNextApiRequestHasRawBody({ rawBody: '' } as WithRawBody) + ).not.toThrow(); + }); +}); diff --git a/lib/next-adhesive/test/unit-auth-request.test.ts b/lib/next-adhesive/test/unit-auth-request.test.ts new file mode 100644 index 0000000..e16004b --- /dev/null +++ b/lib/next-adhesive/test/unit-auth-request.test.ts @@ -0,0 +1,228 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { authenticateHeader, authorizeHeader } from 'multiverse/next-auth'; +import { noopHandler, wrapHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import authRequest, { Options } from 'multiverse/next-adhesive/auth-request'; + +jest.mock('multiverse/next-auth'); + +const mockAuthenticateHeader = asMockedFunction(authenticateHeader); +const mockAuthorizeHeader = asMockedFunction(authorizeHeader); + +beforeEach(() => { + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: false })); + mockAuthorizeHeader.mockReturnValue(Promise.resolve({ authorized: false })); +}); + +it('throws if missing requiresAuth option', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + useOnError: [ + (_, res, ctx) => { + expect(ctx.runtime.error).toMatchObject({ + message: expect.stringContaining( + 'a valid "requiresAuth" option is missing from middleware configuration' + ) + }); + res.send(200); + } + ] + }) + ), + test: async ({ fetch }) => void (await fetch()) + }); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + useOnError: [ + (_, res, ctx) => { + expect(ctx.runtime.error).toMatchObject({ + message: expect.stringContaining( + 'a valid "requiresAuth" option is missing from middleware configuration' + ) + }); + res.send(200); + } + ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: { requiresAuth: 'isGlobalAdmin' as any } + }) + ), + test: async ({ fetch }) => void (await fetch()) + }); +}); + +it('passes allowedSchemes to authenticateHeader', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: true })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: { allowedSchemes: 'bearer' } } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ headers: { authorization: 'token' } })).status).toBe(200); + expect(mockAuthenticateHeader).toBeCalledWith({ + header: 'token', + allowedSchemes: 'bearer' + }); + } + }); +}); + +it('passes constraints to authorizeHeader', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: true })); + mockAuthorizeHeader.mockReturnValue(Promise.resolve({ authorized: true })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: { constraints: ['isGlobalAdmin'] } } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ headers: { authorization: 'token' } })).status).toBe(200); + expect(mockAuthorizeHeader).toBeCalledWith({ + header: 'token', + constraints: ['isGlobalAdmin'] + }); + } + }); +}); + +it('does not send 401 if requires auth and authenticateHeader returns ok', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: true })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: true } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ headers: { authorization: 'token' } })).status).toBe(200); + } + }); +}); + +it('sends 401 if requires auth and authenticateHeader returns not-ok or error', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: false })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: true } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(401) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: false } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + + mockAuthenticateHeader.mockReturnValue( + Promise.resolve({ authenticated: true, error: 'some error' }) + ); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: true } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(401) + }); +}); + +it('does not send 403 if requires auth and authorizeHeader returns ok', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: true })); + mockAuthorizeHeader.mockReturnValue(Promise.resolve({ authorized: true })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: { constraints: ['isGlobalAdmin'] } } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ headers: { authorization: 'token' } })).status).toBe(200); + expect(mockAuthenticateHeader).toBeCalledTimes(1); + expect(mockAuthorizeHeader).toBeCalledTimes(1); + } + }); +}); + +it('sends 403 if requires auth and authorizeHeader returns ok or error', async () => { + expect.hasAssertions(); + + mockAuthenticateHeader.mockReturnValue(Promise.resolve({ authenticated: true })); + mockAuthorizeHeader.mockReturnValue(Promise.resolve({ authorized: false })); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: { constraints: 'isGlobalAdmin' } } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(403) + }); + + mockAuthorizeHeader.mockReturnValue( + Promise.resolve({ authorized: true, error: 'an error' }) + ); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [authRequest], + options: { requiresAuth: { constraints: 'isGlobalAdmin' } } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(403) + }); +}); diff --git a/lib/next-adhesive/test/unit-check-content-type.test.ts b/lib/next-adhesive/test/unit-check-content-type.test.ts new file mode 100644 index 0000000..2bb0a04 --- /dev/null +++ b/lib/next-adhesive/test/unit-check-content-type.test.ts @@ -0,0 +1,719 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { noopHandler, withMockedOutput, wrapHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import randomizeCase from 'random-case'; + +import checkContentType, { + Options +} from 'multiverse/next-adhesive/check-content-type'; + +it('sends 415 by default for POST, PUT, and PATCH requests with or without a Content-Type header', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType] + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'PUT', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'PATCH', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + } + }); +}); + +it('sends 200 by default for requests not using POST, PUT, or PATCH methods if they do not have a Content-Type header', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType] + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + } + }); +}); + +it('sends 415 by default for requests not using POST, PUT, or PATCH methods if they have a Content-Type header', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType] + }) + ), + test: async ({ fetch }) => { + expect( + (await fetch({ method: 'GET', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'HEAD', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'DELETE', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + + // expect( + // (await fetch({ method: 'CONNECT', headers: { 'content-type': 'a/j' } })).status + // ).toBe(415); + + expect( + (await fetch({ method: 'OPTIONS', headers: { 'content-type': 'a/j' } })) + .status + ).toBe(415); + + expect( + (await fetch({ method: 'TRACE', headers: { 'content-type': 'a/j' } })).status + ).toBe(415); + } + }); +}); + +it('sends 200 for POST, PUT, and PATCH requests with allowed Content-Type headers and 415 otherwise', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['a1', 'a2'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a1' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'PUT', headers: { 'content-type': 'a2' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'PATCH', headers: { 'content-type': 'a1' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a3' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'PUT', headers: { 'content-type': 'a3' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'PATCH', headers: { 'content-type': 'a3' } })).status + ).toBe(415); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { + allowedContentTypes: { POST: ['a1'], PUT: ['a2', 'a3'] } + } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a1' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'PUT', headers: { 'content-type': 'a2' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'PATCH', headers: { 'content-type': 'a1' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a3' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'PUT', headers: { 'content-type': 'a3' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'PATCH', headers: { 'content-type': 'a3' } })).status + ).toBe(415); + } + }); +}); + +it(`ignores requests without a Content-Type header that aren't POST, PUT, or PATCH unless explicitly configured`, async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['a1', 'a2'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { + allowedContentTypes: { GET: ['a1'], POST: ['a1'], PUT: ['a2', 'a3'] } + } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(415); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + + expect( + (await fetch({ method: 'GET', headers: { 'content-type': 'a1' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'DELETE', headers: { 'content-type': 'a1' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a1' } })).status + ).toBe(200); + + expect( + (await fetch({ method: 'GET', headers: { 'content-type': 'a2' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'DELETE', headers: { 'content-type': 'a2' } })).status + ).toBe(415); + + expect( + (await fetch({ method: 'POST', headers: { 'content-type': 'a2' } })).status + ).toBe(415); + } + }); +}); + +it(`does not ignore requests that include a Content-Type header`, async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['a1', 'a2'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + + let headers = { 'content-type': 'a1' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + + headers = { 'content-type': 'bad' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + } + }); +}); + +it('respects explicit configuration for all request methods regardless of header presence', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { + allowedContentTypes: { + GET: ['a1'], + HEAD: ['a1'], + POST: ['a1'], + PUT: ['a1'], + DELETE: ['a1'], + CONNECT: ['a1'], + OPTIONS: ['a1'], + TRACE: ['a1'], + PATCH: ['a1'] + } + } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(415); + expect((await fetch({ method: 'HEAD' })).status).toBe(415); + expect((await fetch({ method: 'DELETE' })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(415); + expect((await fetch({ method: 'TRACE' })).status).toBe(415); + expect((await fetch({ method: 'POST' })).status).toBe(415); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + + let headers = { 'content-type': 'a1' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(200); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(200); + + headers = { 'content-type': 'bad' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(415); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + } + }); +}); + +it('ignores Content-Type header case for all requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['application/json'] } + }) + ), + test: async ({ fetch }) => { + const headers = { + get 'content-type'() { + return randomizeCase('application/json'); + } + }; + + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(200); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(200); + } + }); +}); + +it('allows all (even missing) Content-Type header if set to "any"', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: 'any' } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(200); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(200); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(200); + expect((await fetch({ method: 'PATCH' })).status).toBe(200); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: { GET: 'any', POST: 'any' } } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + } + }); +}); + +it('requires all requests to be sent without a Content-Type header if set to "none"', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: 'none' } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(415); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(200); + expect((await fetch({ method: 'PATCH' })).status).toBe(200); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: { POST: 'none' } } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(415); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + } + }); +}); + +it('allows requests without a Content-Type header in addition to other constraints if array (as a mapped value or top-level) includes "none" value', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['none', 'application/json'] } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(200); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(200); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(200); + expect((await fetch({ method: 'PATCH' })).status).toBe(200); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: { POST: ['none', 'application/json'] } } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'application/json' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'HEAD' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT' })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS' })).status).toBe(200); + expect((await fetch({ method: 'TRACE' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(415); + expect((await fetch({ method: 'PATCH' })).status).toBe(415); + } + }); +}); + +it('sends 415 if Content-Type is literally the string "none"', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: ['none', 'application/json'] } + }) + ), + test: async ({ fetch }) => { + const headers = { 'content-type': 'none' }; + expect((await fetch({ method: 'GET', headers })).status).toBe(415); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(415); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(415); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(415); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(415); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(415); + expect((await fetch({ method: 'POST', headers })).status).toBe(415); + expect((await fetch({ method: 'PUT', headers })).status).toBe(415); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(415); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: { POST: ['none', 'application/json'] } } + }) + ), + test: async ({ fetch }) => { + // ? Works even if strange case is used + const headers = { 'content-type': 'NoNe' }; + expect((await fetch({ method: 'POST', headers })).status).toBe(415); + } + }); +}); + +it('sends 400 is method is undefined', async () => { + expect.hasAssertions(); + + await testApiHandler({ + requestPatcher(req) { + req.method = undefined; + }, + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: [randomizeCase('application/json')] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(400); + } + }); +}); + +it('works even if allowedContentTypes mapped value is strange or undefined', async () => { + expect.hasAssertions(); + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: { GET: undefined } } + }) + ), + async test({ fetch }) { + expect((await fetch({ method: 'GET' })).status).toBe(200); + } + }); + + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: { allowedContentTypes: { GET: new BigInt64Array() as any } } + }) + ), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ + message: expect.stringContaining( + 'allowedContentTypes must adhere to type constraints' + ) + }); + }); +}); + +it('works even if allowedContentTypes not specified in lowercase', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkContentType], + options: { allowedContentTypes: [randomizeCase('application/json')] } + }) + ), + test: async ({ fetch }) => { + const headers = { + get 'content-type'() { + return randomizeCase('application/json'); + } + }; + + expect((await fetch({ method: 'GET', headers })).status).toBe(200); + expect((await fetch({ method: 'HEAD', headers })).status).toBe(200); + expect((await fetch({ method: 'DELETE', headers })).status).toBe(200); + // expect((await fetch({ method: 'CONNECT', headers })).status).toBe(200); + expect((await fetch({ method: 'OPTIONS', headers })).status).toBe(200); + expect((await fetch({ method: 'TRACE', headers })).status).toBe(200); + expect((await fetch({ method: 'POST', headers })).status).toBe(200); + expect((await fetch({ method: 'PUT', headers })).status).toBe(200); + expect((await fetch({ method: 'PATCH', headers })).status).toBe(200); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-check-method.test.ts b/lib/next-adhesive/test/unit-check-method.test.ts new file mode 100644 index 0000000..9ba8965 --- /dev/null +++ b/lib/next-adhesive/test/unit-check-method.test.ts @@ -0,0 +1,173 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { noopHandler, wrapHandler, mockEnvFactory } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import checkMethod, { Options } from 'multiverse/next-adhesive/check-method'; + +import type { ValidHttpMethod } from '@xunnamius/types'; + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); + +it('sends 200 for allowed methods', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { allowedMethods: ['GET', 'DELETE', 'POST', 'PUT'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + } + }); +}); + +it('is restrictive by default', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod] + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(405); + expect((await fetch({ method: 'POST' })).status).toBe(405); + expect((await fetch({ method: 'PUT' })).status).toBe(405); + expect((await fetch({ method: 'DELETE' })).status).toBe(405); + } + }); +}); + +it('sends 405 when request.method is undefined', async () => { + expect.hasAssertions(); + + await testApiHandler({ + requestPatcher: (req) => (req.method = undefined), + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod] + }) + ), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(405); + } + }); +}); + +it('sends 405 when encountering unlisted methods', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { allowedMethods: ['POST', 'PUT'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(405); + expect((await fetch({ method: 'POST' })).status).toBe(200); + expect((await fetch({ method: 'PUT' })).status).toBe(200); + expect((await fetch({ method: 'DELETE' })).status).toBe(405); + } + }); +}); + +it('sends 405 when encountering globally disallowed methods', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(200); + expect((await fetch({ method: 'POST' })).status).toBe(405); + expect((await fetch({ method: 'PUT' })).status).toBe(405); + expect((await fetch({ method: 'DELETE' })).status).toBe(405); + } + }); + }, + { DISALLOWED_METHODS: 'POST,PUT,DELETE' } + ); +}); + +it('ignores spacing when parsing DISALLOWED_METHODS', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'] } + }) + ), + test: async ({ fetch }) => { + expect((await fetch({ method: 'GET' })).status).toBe(405); + expect((await fetch({ method: 'POST' })).status).toBe(405); + expect((await fetch({ method: 'PUT' })).status).toBe(405); + expect((await fetch({ method: 'DELETE' })).status).toBe(200); + } + }); + }, + { DISALLOWED_METHODS: ' POST , PUT, GET ' } + ); +}); + +it('sends an Allow header in 405 responses', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { allowedMethods: ['GET', 'POST', 'HEAD'] } + }) + ), + test: async ({ fetch }) => { + const res = await fetch({ method: 'PUT' }); + expect(res.status).toBe(405); + expect(res.headers.get('allow')).toBe('GET,POST,HEAD'); + } + }); +}); + +it('works even if allowedMethods specified in lowercase', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkMethod], + options: { + allowedMethods: ['get'] as unknown as ValidHttpMethod[] + } + }) + ), + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-check-version.test.ts b/lib/next-adhesive/test/unit-check-version.test.ts new file mode 100644 index 0000000..609078d --- /dev/null +++ b/lib/next-adhesive/test/unit-check-version.test.ts @@ -0,0 +1,174 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { mockEnvFactory, noopHandler, wrapHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import checkVersion, { Options } from 'multiverse/next-adhesive/check-version'; + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); + +it('is a noop by default', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion] + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: 'one' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); +}); + +it('sends 404 if its corresponding version is disabled', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '1' } + }) + ), + test: async ({ fetch }) => { + await withMockedEnv( + async () => { + expect((await fetch()).status).toBe(404); + }, + { DISABLED_API_VERSIONS: '1' } + ); + + await withMockedEnv( + async () => { + expect((await fetch()).status).toBe(200); + }, + { DISABLED_API_VERSIONS: '2' } + ); + + await withMockedEnv( + async () => { + expect((await fetch()).status).toBe(404); + }, + { DISABLED_API_VERSIONS: '2,1' } + ); + + await withMockedEnv( + async () => { + expect((await fetch()).status).toBe(200); + }, + { DISABLED_API_VERSIONS: '3,2' } + ); + } + }); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '1' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '2' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(404) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: 'three' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(404) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '4' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(404) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(async () => undefined, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '4' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(404) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion] + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + }, + { DISABLED_API_VERSIONS: 'three,4,2,five' } + ); +}); + +it('is a noop if DISABLED_API_VERSIONS is an empty string', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion], + options: { apiVersion: '4' } + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [checkVersion] + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(200) + }); + }, + { DISABLED_API_VERSIONS: '' } + ); +}); diff --git a/lib/next-adhesive/test/unit-contrive-error.test.ts b/lib/next-adhesive/test/unit-contrive-error.test.ts new file mode 100644 index 0000000..e1eea40 --- /dev/null +++ b/lib/next-adhesive/test/unit-contrive-error.test.ts @@ -0,0 +1,53 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { isDueForContrivedError } from 'multiverse/next-contrived'; +import { wrapHandler, noopHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import contriveError, { Options } from 'multiverse/next-adhesive/contrive-error'; + +jest.mock('multiverse/next-contrived'); + +const mockIsDueForContrivedError = asMockedFunction(isDueForContrivedError); + +beforeEach(() => { + mockIsDueForContrivedError.mockReturnValue(Promise.resolve(false)); +}); + +it('does not inject contrived errors by default', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [contriveError] + }) + ), + test: async ({ fetch }) => { + mockIsDueForContrivedError.mockReturnValue(Promise.resolve(true)); + await expect(fetch().then((r) => r.status)).resolves.toBe(200); + } + }); +}); + +it('injects contrived errors when due if enabled', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [contriveError], + options: { enableContrivedErrors: true } + }) + ), + test: async ({ fetch }) => { + mockIsDueForContrivedError.mockReturnValue(Promise.resolve(false)); + await expect(fetch().then((r) => r.status)).resolves.toBe(200); + mockIsDueForContrivedError.mockReturnValue(Promise.resolve(true)); + await expect(fetch().then((r) => r.status)).resolves.toBe(555); + mockIsDueForContrivedError.mockReturnValue(Promise.resolve(false)); + await expect(fetch().then((r) => r.status)).resolves.toBe(200); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-handle-error.test.ts b/lib/next-adhesive/test/unit-handle-error.test.ts new file mode 100644 index 0000000..4d5c6f7 --- /dev/null +++ b/lib/next-adhesive/test/unit-handle-error.test.ts @@ -0,0 +1,183 @@ +import { withMiddleware } from 'multiverse/next-api-glue'; +import { testApiHandler } from 'next-test-api-route-handler'; +import { + itemFactory, + noopHandler, + withMockedOutput, + wrapHandler +} from 'testverse/setup'; +import { toss } from 'toss-expression'; +import handleError, { Options } from 'multiverse/next-adhesive/handle-error'; + +import { + ValidationError, + AppValidationError, + InvalidAppConfigurationError, + InvalidAppEnvironmentError, + ClientValidationError, + InvalidClientConfigurationError, + InvalidItemError, + InvalidSecretError, + AuthError, + NotAuthenticatedError, + NotAuthorizedError, + NotFoundError, + ItemNotFoundError, + ItemsNotFoundError, + HttpError, + TrialError, + DummyError, + AppError, + GuruMeditationError, + NotImplementedError +} from 'universe/error'; + +it('sends correct HTTP error codes when certain errors occur', async () => { + expect.hasAssertions(); + + const factory = itemFactory<[AppError | string, number]>([ + [new ValidationError(), 400], + [new ValidationError(''), 400], // ! Edge case for code coverage + [new AppValidationError(), 500], + [new InvalidAppConfigurationError(), 500], + [new InvalidAppEnvironmentError(), 500], + [new ClientValidationError(), 400], + [new InvalidClientConfigurationError(), 400], + [new InvalidItemError(), 400], + [new InvalidSecretError(), 400], + [new AuthError(), 403], + [new NotAuthenticatedError(), 403], + [new NotAuthorizedError(), 403], + [new NotFoundError(), 404], + [new ItemNotFoundError(), 404], + [new ItemsNotFoundError(), 404], + [new HttpError(), 500], + [new TrialError(), 500], + [new DummyError(), 500], + [new AppError(), 500], + [new GuruMeditationError(), 500], + [new NotImplementedError(), 501], + [new Error(), 500], // ? Every other error type should return 500 + ['strange error', 500] // ? This too + ]); + + await withMockedOutput(async () => { + await Promise.all( + factory.items.map(async (item) => { + const [expectedError, expectedStatus] = item; + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(async () => toss(expectedError), { + descriptor: '/fake', + use: [], + useOnError: [handleError] + }) + ), + test: async ({ fetch }) => + fetch().then((res) => expect(res.status).toStrictEqual(expectedStatus)) + }); + }) + ); + }); +}); + +it('throws without calling res.end if response is no longer writable', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: async (rq, rs) => { + await expect( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [ + (_req, res) => { + // eslint-disable-next-line jest/unbound-method + const send = res.end; + res.end = ((...args: Parameters) => { + send(...args); + throw new Error('bad bad not good'); + }) as unknown as typeof res.end; + } + ], + useOnError: [handleError] + })(rq, rs) + ).rejects.toMatchObject({ message: 'bad bad not good' }); + }, + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + } + }); +}); + +it('supports pluggable error handlers', async () => { + expect.hasAssertions(); + + const MyError = class extends DummyError {}; + const MyUnusedError = class extends Error {}; + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + () => { + throw new MyError('bad bad not good'); + } + ], + useOnError: [handleError], + options: { + errorHandlers: new Map([ + [ + MyUnusedError, + (res) => { + res.status(555).end(); + } + ], + [ + MyError, + (res, errorJson) => { + res.status(200).send(errorJson); + } + ] + ]) + } + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + await expect((await fetch()).json()).resolves.toStrictEqual({ + error: 'bad bad not good' + }); + } + }); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + () => { + throw new MyError('bad good not good'); + } + ], + useOnError: [handleError], + options: { + errorHandlers: new Map([ + [ + // ? Should catch every error + Error, + (res, errorJson) => { + res.status(201).send(errorJson); + } + ] + ]) + } + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(201); + await expect((await fetch()).json()).resolves.toStrictEqual({ + error: 'bad good not good' + }); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-limit-request.test.ts b/lib/next-adhesive/test/unit-limit-request.test.ts new file mode 100644 index 0000000..6e261a7 --- /dev/null +++ b/lib/next-adhesive/test/unit-limit-request.test.ts @@ -0,0 +1,158 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { clientIsRateLimited } from 'multiverse/next-limit'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import { mockEnvFactory, wrapHandler, noopHandler } from 'testverse/setup'; +import limitRequest from 'multiverse/next-adhesive/limit-request'; + +jest.mock('multiverse/next-limit'); + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); +const mockClientIsRateLimited = asMockedFunction(clientIsRateLimited); + +beforeEach(() => { + mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: false, retryAfter: 0 }) + ); +}); + +it('rate limits requests according to backend determination', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [limitRequest] + }) + ), + test: async ({ fetch }) => { + await withMockedEnv( + async () => { + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: false, retryAfter: 0 }) + ); + + await expect( + fetch().then(async (r) => [r.status, await r.json()]) + ).resolves.toStrictEqual([200, {}]); + + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: true, retryAfter: 100 }) + ); + + await expect( + fetch().then(async (r) => [r.status, await r.json()]) + ).resolves.toStrictEqual([ + 429, + expect.objectContaining({ + retryAfter: 100 + }) + ]); + }, + { IGNORE_RATE_LIMITS: 'false' } + ); + } + }); +}); + +it('does not rate limit requests when ignoring rate limits', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [limitRequest] + }) + ), + test: async ({ fetch }) => { + await withMockedEnv( + async () => { + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: false, retryAfter: 0 }) + ); + + await expect( + fetch().then(async (r) => [r.status, await r.json()]) + ).resolves.toStrictEqual([200, {}]); + + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: true, retryAfter: 100 }) + ); + + await expect( + fetch().then(async (r) => [r.status, await r.json()]) + ).resolves.toStrictEqual([200, {}]); + }, + { IGNORE_RATE_LIMITS: 'true' } + ); + } + }); +}); + +it('treats otherwise valid requests as unauthenticatable only when locking out all clients', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [limitRequest] + }) + ), + test: async ({ fetch }) => { + await withMockedEnv( + async () => { + const res = await fetch(); + expect(res.status).toBe(403); + }, + { + LOCKOUT_ALL_CLIENTS: 'true' + } + ); + + await withMockedEnv(async () => expect((await fetch()).status).toBe(200), { + LOCKOUT_ALL_CLIENTS: 'false' + }); + } + }); +}); + +it('includes retry-after value in header (s) and in response JSON (ms)', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [limitRequest] + }) + ), + test: async ({ fetch }) => { + await withMockedEnv( + async () => { + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: false, retryAfter: 0 }) + ); + + await expect( + fetch().then(async (r) => [r.headers.get('retry-after'), await r.json()]) + ).resolves.toStrictEqual([null, {}]); + + void mockClientIsRateLimited.mockReturnValue( + Promise.resolve({ isLimited: true, retryAfter: 12344 }) + ); + + await expect( + fetch().then(async (r) => [r.headers.get('retry-after'), await r.json()]) + ).resolves.toStrictEqual([ + '13', + expect.objectContaining({ retryAfter: 12344 }) + ]); + }, + { IGNORE_RATE_LIMITS: 'false' } + ); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-log-request.test.ts b/lib/next-adhesive/test/unit-log-request.test.ts new file mode 100644 index 0000000..ebf66c5 --- /dev/null +++ b/lib/next-adhesive/test/unit-log-request.test.ts @@ -0,0 +1,104 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { addToRequestLog } from 'multiverse/next-log'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import { wrapHandler, noopHandler } from 'testverse/setup'; +import { toss } from 'toss-expression'; +import logRequest from 'multiverse/next-adhesive/log-request'; + +jest.mock('multiverse/next-log'); + +const mockAddToRequestLog = asMockedFunction(addToRequestLog); + +beforeEach(() => { + mockAddToRequestLog.mockReturnValue(Promise.resolve()); +}); + +it('logs requests on call to res.send', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + wrapHandler( + withMiddleware(async (_req, res) => res.status(404).send({}), { + descriptor: '/fake', + use: [logRequest] + }) + ) + ), + test: async ({ fetch }) => { + await Promise.all([fetch(), fetch(), fetch()]); + expect(mockAddToRequestLog).toBeCalledTimes(3); + } + }); +}); + +it('logs requests on call to res.end', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + wrapHandler( + withMiddleware(async (_req, res) => void res.status(404).end(), { + descriptor: '/fake', + use: [logRequest] + }) + ) + ), + test: async ({ fetch }) => { + await Promise.all([fetch(), fetch(), fetch()]); + expect(mockAddToRequestLog).toBeCalledTimes(3); + } + }); +}); + +it('logs requests once on multiple calls to res.end', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + wrapHandler( + withMiddleware( + async (_req, res) => { + res.status(404).end(); + res.end(); + }, + { + descriptor: '/fake', + use: [logRequest] + } + ) + ) + ), + test: async ({ fetch }) => { + await Promise.all([fetch(), fetch(), fetch()]); + expect(mockAddToRequestLog).toBeCalledTimes(3); + } + }); +}); + +it('handles request log errors after res.end as gracefully as possible', async () => { + expect.hasAssertions(); + + mockAddToRequestLog.mockImplementation(() => toss(new Error('fake error'))); + let called = false; + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [logRequest], + useOnError: [ + (_req, _res, ctx) => { + expect(ctx.runtime.error).toMatchObject({ message: 'fake error' }); + called = true; + } + ] + }) + ), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + expect(called).toBeTrue(); + } + }); +}); diff --git a/lib/next-adhesive/test/unit-use-cors.test.ts b/lib/next-adhesive/test/unit-use-cors.test.ts new file mode 100644 index 0000000..83a8f12 --- /dev/null +++ b/lib/next-adhesive/test/unit-use-cors.test.ts @@ -0,0 +1,77 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { isolatedImport, wrapHandler, noopHandler } from 'testverse/setup'; +import { withMiddleware } from 'multiverse/next-api-glue'; +import useCors, { Options } from 'multiverse/next-adhesive/use-cors'; + +afterEach(() => { + jest.dontMock('cors'); +}); + +it('works', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [] + }) + ), + test: async ({ fetch }) => { + const res = await fetch({ method: 'OPTIONS' }); + expect(res.status).toBe(200); + expect(res.headers.get('Access-Control-Allow-Origin')).toBeNull(); + expect(res.headers.get('Access-Control-Allow-Methods')).toBeNull(); + } + }); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [useCors], + options: { allowedMethods: ['GET', 'POST', 'HEAD'] } + }) + ), + test: async ({ fetch }) => { + let res = await fetch({ method: 'OPTIONS' }); + expect(res.status).toBe(204); + expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*'); + expect(res.headers.get('Access-Control-Allow-Methods')).toBe('GET,POST,HEAD'); + + res = await fetch({ method: 'GET' }); + expect(res.status).toBe(200); + } + }); +}); + +it('handles cors package errors gracefully', async () => { + expect.hasAssertions(); + + jest.doMock( + 'cors', + () => () => (_req: unknown, _res: unknown, cb: (e: Error) => void) => { + return cb(new Error('fake error')); + } + ); + + await testApiHandler({ + handler: wrapHandler( + withMiddleware(noopHandler, { + descriptor: '/fake', + use: [ + isolatedImport({ + path: 'multiverse/next-adhesive/use-cors' + }) + ], + useOnError: [ + (_req, res, ctx) => { + expect(ctx.runtime.error).toMatchObject({ message: 'fake error' }); + res.status(555).end(); + } + ] + }) + ), + test: async ({ fetch }) => expect((await fetch()).status).toBe(555) + }); +}); diff --git a/lib/next-adhesive/use-cors.ts b/lib/next-adhesive/use-cors.ts new file mode 100644 index 0000000..40e9d98 --- /dev/null +++ b/lib/next-adhesive/use-cors.ts @@ -0,0 +1,36 @@ +import Cors from 'cors'; +import { debugFactory } from 'multiverse/debug-extended'; +import { Options as CheckMethodOptions } from 'multiverse/next-adhesive/check-method'; + +import type { MiddlewareContext } from 'multiverse/next-api-glue'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +const debug = debugFactory('next-adhesive:use-cors'); + +export type Options = { + allowedMethods?: CheckMethodOptions['allowedMethods']; +}; + +/** + * Allows _cross-origin_ requests for the most popular request types. **Note + * that this can be dangerous (huge security hole) and should only be used for + * public APIs**. + * + * When present, this should be among the very first middleware in the chain and + * certainly before _check-method_. + * + * By default, allowed CORS methods are: `GET`, `HEAD`, `PUT`, `PATCH`, `POST`, + * and `DELETE`. + */ +export default async function ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) { + debug('entered middleware runtime'); + + const cors = Cors({ methods: context.options.allowedMethods }); + await new Promise((resolve, reject) => + cors(req, res, (err) => (err ? reject(err) : resolve(undefined))) + ); +} diff --git a/lib/next-api-glue/index.ts b/lib/next-api-glue/index.ts new file mode 100644 index 0000000..3cc7d17 --- /dev/null +++ b/lib/next-api-glue/index.ts @@ -0,0 +1,385 @@ +import { toss } from 'toss-expression'; +import { sendNotImplemented } from 'multiverse/next-api-respond'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { Debugger } from 'multiverse/debug-extended'; +import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next'; +import type { NoInfer } from '@xunnamius/types'; + +const debug = debugFactory('next-api-glue:runtime'); + +/** + * The shape of a custom middleware function. + */ +export type Middleware< + Options extends Record = Record +> = ( + req: NextApiRequest, + res: NextApiResponse, + context: MiddlewareContext +) => unknown; + +/** + * The shape of a middleware context object, potentially customized with + * additional middleware-specific options. + * + * Note that type checking cannot enforce that certain options are passed in the + * case that an options argument is omitted when calling `withMiddleware`. So, + * to be safe, all custom middleware context options should be declared as + * optional (i.e. `{ myOpt?: aType }` instead of `{ myOpt: aType })`. + * + * Middleware should default to the most restrictive configuration possible if + * its respective options are missing. + */ +export type MiddlewareContext< + Options extends Record = Record +> = { + /** + * Contains middleware use chain control functions and various metadata. + */ + runtime: { + /** + * Metadata describing the current endpoint. + */ + endpoint: { + /** + * A parameterized path string in the form of a URI path corresponding to + * the current endpoint. For example: `/my-endpoint/:some_id`. + */ + descriptor?: string; + }; + /** + * Call the next middleware function in the use chain. If not called + * explicitly before a middleware function resolves, and `done()` was also + * not called, `next()` will be called automatically. This means calling + * `next()` in a middleware function is entirely optional. + */ + readonly next: () => Promise; + /** + * Stop calling middleware functions, effectively aborting execution of the + * use chain. If `response.end` hasn't been called before calling this + * function, it will be called automatically. On abort, the handler will + * also be skipped. + */ + readonly done: () => void; + /** + * For middleware run via `useOnError`, the `error` property will contain + * the thrown error object. + */ + readonly error: unknown; + }; + /** + * Options expected by middleware functions at runtime. + */ + options: Options & { + /** + * If `true`, `context.runtime.done` is called whenever `response.end` is + * called before the middleware chain completes execution. If `false`, the + * entire primary middleware chain will always run to completion, even if + * the response has already been sent before it completes. + * + * @default true + */ + callDoneOnEnd: boolean; + }; +}; + +/** + * Generic middleware runner. Decorates a request handler. + * + * Passing `undefined` as `handler` or not calling `res.end()` (and not sending + * headers) in your handler or use chain will trigger an `HTTP 501 Not + * Implemented` response. This can be used to to stub out endpoints and their + * middleware for later implementation. + */ +export function withMiddleware< + Options extends Record = Record +>( + handler: NextApiHandler | undefined, + { + descriptor, + use, + useOnError, + options + }: { + descriptor: MiddlewareContext['runtime']['endpoint']['descriptor']; + use: Middleware>[]; + useOnError?: Middleware>[]; + options?: Partial>['options']> & + NoInfer; + } +) { + if (!Array.isArray(use)) { + throw new Error('withMiddleware `use` parameter must be an array'); + } + + if (useOnError && !Array.isArray(useOnError)) { + throw new Error('withMiddleware `useOnError` parameter must be an array'); + } + + return async (req: NextApiRequest, res: NextApiResponse) => { + /* istanbul ignore next */ + const middlewareContext: MiddlewareContext> = { + runtime: { + endpoint: { + descriptor + }, + next: () => toss(new Error('runtime.next was called unexpectedly')), + done: () => toss(new Error('runtime.done was called unexpectedly')), + error: undefined + }, + options: { callDoneOnEnd: true, ...options } as MiddlewareContext< + NoInfer + >['options'] + }; + + /** + * Async middleware chain iteration. Returns `true` if execution was aborted + * or `false` otherwise. + */ + const startPullingChain = async ( + chain: IterableIterator>>, + localDebug: Debugger + ) => { + let executionWasAborted = false; + let executionCompleted = false; + let ranAtLeastOneMiddleware = false; + + try { + if (middlewareContext.options.callDoneOnEnd) { + localDebug( + 'chain will automatically call runtime.done after first call to res.end' + ); + + const send = res.end; + res.end = ((...args: Parameters) => { + const sent = res.writableEnded || res.headersSent; + send(...args); + + if (!sent) { + if (!executionWasAborted && !executionCompleted) { + localDebug('calling runtime.done after first call to res.end'); + middlewareContext.runtime.done(); + } else { + localDebug( + 'NOTICE: skipped calling runtime.done since chain already finished executing' + ); + } + } + }) as typeof res.end; + } else { + localDebug('chain will NOT automatically call runtime.done'); + } + + const pullChain = async () => { + let chainWasPulled = false; + const { value: currentMiddleware, done } = chain.next(); + + // @ts-expect-error: next is readonly to everyone but us + middlewareContext.runtime.next = async () => { + if (!executionCompleted) { + if (executionWasAborted) { + debug.warn( + 'runtime.next: chain was aborted; calling runtime.next() at this point is a noop' + ); + } else { + chainWasPulled = true; + localDebug( + 'runtime.next: manually selecting next middleware in chain' + ); + await pullChain(); + } + } else { + debug.warn( + 'runtime.next: chain already finished executing; calling runtime.next() at this point is a noop' + ); + } + }; + + // @ts-expect-error: done is readonly to everyone but us + middlewareContext.runtime.done = () => { + if (!executionCompleted) { + if (!executionWasAborted) { + localDebug('runtime.done: aborting middleware execution chain'); + executionWasAborted = true; + } else { + debug.warn( + 'runtime.done: chain already aborted; calling runtime.done() at this point is a noop' + ); + } + } else { + debug.warn( + 'runtime.done: chain already finished executing; calling runtime.done() at this point is a noop' + ); + } + }; + + if (!done) { + if (typeof currentMiddleware == 'function') { + localDebug('executing middleware'); + await currentMiddleware(req, res, middlewareContext); + ranAtLeastOneMiddleware = true; + } else { + debug.warn('skipping execution of non-function item in chain'); + } + + if (executionWasAborted) { + localDebug('execution chain aborted manually'); + } else if (!chainWasPulled) { + localDebug('selecting next middleware in chain'); + await pullChain(); + } + } else { + localDebug('no more middleware to execute'); + !executionCompleted && + localDebug('deactivated runtime control functions'); + executionCompleted = true; + } + }; + + await pullChain(); + localDebug('stopped middleware execution chain'); + localDebug( + `at least one middleware executed: ${ + ranAtLeastOneMiddleware ? 'yes' : 'no' + }` + ); + + return executionWasAborted; + } catch (e) { + executionWasAborted = true; + debug.warn('execution chain aborted due to error'); + throw e; + } + }; + + debug('-- begin --'); + + try { + let primaryChainWasAborted = false; + + try { + debug('selecting first middleware in primary middleware chain'); + primaryChainWasAborted = await startPullingChain( + use[Symbol.iterator](), + debug + ); + } catch (e) { + debug('error in primary middleware chain'); + throw e; + } + + if (typeof handler == 'function') { + if (primaryChainWasAborted) { + debug('not executing handler since primary chain execution was aborted'); + } else { + debug('executing handler'); + await handler(req, res); + debug('finished executing handler'); + } + } else { + debug('no handler function available'); + } + + if (!res.writableEnded && !res.headersSent) { + debug('response was not sent: sending "not implemented" error'); + sendNotImplemented(res); + } + + debug('-- done --'); + } catch (e) { + try { + debug.error('attempting to handle error: %O', e); + + // @ts-expect-error: error is readonly to everyone but us + middlewareContext.runtime.error = e; + + if (useOnError) { + try { + debug.error( + 'selecting first middleware in error handling middleware chain' + ); + await startPullingChain(useOnError[Symbol.iterator](), debug.error); + } catch (err) { + // ? Error in error handler was unhandled + debug.error('error in error handling middleware chain: %O', err); + debug.error('throwing unhandled error'); + throw err; + } + } else { + debug.error('no error handling middleware found'); + debug.error('throwing unhandled error'); + throw e; + } + + // ? Error was unhandled, kick it up to the caller (usually Next itself) + if (!res.writableEnded && !res.headersSent) { + debug.error('throwing unhandled error'); + throw e; + } + } finally { + debug('-- done (with errors) --'); + } + } + }; +} + +/** + * Returns a `withMiddleware` function decorated with a preset configuration. + * `withMiddleware` optionally accepts its usual parameters, which will be + * appended onto the arguments to `withMiddlewareFactory` (the "preset + * parameters"); however, note that passed option keys will overwrite their + * preset counterparts. + * + * Useful when you don't want to repeatedly import, configure, and list a bunch + * of middleware every time you want to call `withMiddleware`. + */ +export function middlewareFactory< + Options extends Record = Record +>({ + use: defaultUse, + useOnError: defaultUseOnError, + options: defaultOptions +}: { + use: Middleware>[]; + useOnError?: Middleware>[]; + options?: Partial>['options']> & + NoInfer; +}) { + return = Record>( + handler: NextApiHandler | undefined, + params: { + descriptor: MiddlewareContext['runtime']['endpoint']['descriptor']; + prependUse?: Middleware>[]; + appendUse?: Middleware>[]; + prependUseOnError?: Middleware>[]; + appendUseOnError?: Middleware>[]; + options?: Partial>['options']> & + NoInfer; + } + ) => { + const { + descriptor, + prependUse, + appendUse, + prependUseOnError, + appendUseOnError, + options: passedOptions + } = { ...params }; + + return withMiddleware & NoInfer>(handler, { + descriptor, + use: [...(prependUse || []), ...defaultUse, ...(appendUse || [])], + useOnError: [ + ...(prependUseOnError || []), + ...(defaultUseOnError || []), + ...(appendUseOnError || []) + ], + options: { ...defaultOptions, ...passedOptions } as Partial< + MiddlewareContext & NoInfer>['options'] + > & + NoInfer & + NoInfer + }); + }; +} diff --git a/lib/next-api-glue/package.json b/lib/next-api-glue/package.json new file mode 100644 index 0000000..6b12909 --- /dev/null +++ b/lib/next-api-glue/package.json @@ -0,0 +1,3 @@ +{ + "name": "next-api-glue" +} diff --git a/lib/next-api-glue/unit.test.ts b/lib/next-api-glue/unit.test.ts new file mode 100644 index 0000000..25f5dc2 --- /dev/null +++ b/lib/next-api-glue/unit.test.ts @@ -0,0 +1,1144 @@ +import { testApiHandler } from 'next-test-api-route-handler'; +import { middlewareFactory, withMiddleware } from 'multiverse/next-api-glue'; +import { withDebugEnabled, mockOutputFactory } from 'testverse/setup'; +import { toss } from 'toss-expression'; +import { DummyError } from 'universe/error'; + +import type { NextApiRequest, NextApiResponse, NextConfig } from 'next'; +import type { Middleware, MiddlewareContext } from 'multiverse/next-api-glue'; + +const MAX_CONTENT_LENGTH_BYTES = 100000; +const MAX_CONTENT_LENGTH_BYTES_PLUS_1 = 100001; + +const withMockedOutput = mockOutputFactory({ passthrough: { stdErrSpy: false } }); + +const noopHandler = async (_req: NextApiRequest, res: NextApiResponse) => { + res.status(200).send({}); +}; + +describe('::withMiddleware', () => { + it('throws on bad parameters', async () => { + expect.hasAssertions(); + + expect(() => + withMiddleware(async () => undefined, { + // @ts-expect-error: testing bad param + use: true + }) + ).toThrow(/`use` parameter must be an array/); + + expect(() => + withMiddleware(async () => undefined, { + descriptor: '/fake', + use: [], + // @ts-expect-error: testing bad param + useOnError: true + }) + ).toThrow(/`useOnError` parameter must be an array/); + }); + + it('rejects requests that are too big when exporting config (next.js)', async () => { + expect.hasAssertions(); + + const handler = withMiddleware(noopHandler, { + descriptor: '/fake', + use: [] + }) as ReturnType & { config: NextConfig }; + + handler.config = { + api: { + bodyParser: { + get sizeLimit() { + return MAX_CONTENT_LENGTH_BYTES; + } + } + } + }; + + await testApiHandler({ + rejectOnHandlerError: true, + handler, + test: async ({ fetch }) => { + await expect( + fetch({ + method: 'POST', + body: 'x'.repeat(MAX_CONTENT_LENGTH_BYTES_PLUS_1) + }).then((r) => r.status) + ).resolves.toBe(413); + } + }); + }); + + it('lowercases headers automatically', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware( + async (req, res) => { + res.status(req.headers.key == '1234' ? 200 : 555).send({}); + }, + { descriptor: '/fake', use: [] } + ), + test: async ({ fetch }) => + expect((await fetch({ headers: { KEY: '1234' } })).status).toBe(200) + }); + }); + + it('parses url parameters', async () => { + expect.hasAssertions(); + + await testApiHandler({ + requestPatcher: (req) => { + req.url = '/?some=url&yes'; + }, + rejectOnHandlerError: true, + handler: withMiddleware( + async (req, res) => { + expect(req.query).toStrictEqual({ some: 'url', yes: '' }); + res.status(200).send({}); + }, + { descriptor: '/fake', use: [] } + ), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + } + }); + }); + + it('runs one middleware in primary chain', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [middleware] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + expect(middleware).toBeCalledTimes(1); + } + }); + }); + + it('runs multiple middleware in primary chain', async () => { + expect.hasAssertions(); + + const middleware = [jest.fn(), jest.fn()]; + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { descriptor: '/fake', use: middleware }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + middleware.forEach((m) => expect(m).toBeCalledTimes(1)); + } + }); + }); + + it('runs primary chain middleware then handler', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(() => + expect(handler).toBeCalledTimes(0) + ) as Middleware; + const handler = jest.fn(() => expect(middleware).toBeCalledTimes(1)); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(handler, { descriptor: '/fake', use: [middleware] }), + test: async ({ fetch }) => { + await fetch(); + expect(middleware).toBeCalledTimes(1); + expect(handler).toBeCalledTimes(1); + } + }); + }); + + it('runs handler even if no middleware used', async () => { + expect.hasAssertions(); + + const handler = jest.fn(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(handler, { descriptor: '/fake', use: [] }), + test: async ({ fetch }) => { + await fetch(); + expect(handler).toBeCalledTimes(1); + } + }); + }); + + it('skips running handler if not a function', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + // @ts-expect-error: bad handler + handler: withMiddleware(true, { + descriptor: '/fake', + use: [(_, res) => res.status(200).end()] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + } + }); + }); + + it('populates runtime.endpoint with endpoint metadata if available', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake/:path', + use: [ + (_, res, ctx) => res.status(200).send({ endpoint: ctx.runtime.endpoint }) + ] + }), + test: async ({ fetch }) => { + await expect((await fetch()).json()).resolves.toStrictEqual({ + endpoint: { + descriptor: '/fake/:path' + } + }); + } + }); + }); + + it('skips running handler if primary chain was aborted', async () => { + expect.hasAssertions(); + + const handler = jest.fn(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(handler, { + descriptor: '/fake', + use: [(_, __, ctx) => ctx.runtime.done()] + }), + test: async ({ fetch }) => { + await fetch(); + expect(handler).toBeCalledTimes(0); + } + }); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(handler, { + descriptor: '/fake', + use: [() => toss(new Error('bad'))] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ message: 'bad' }); + + expect(handler).toBeCalledTimes(0); + }); + }); + + it('sends 501 if handler is undefined', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { descriptor: '/fake', use: [] }), + test: async ({ fetch }) => expect((await fetch()).status).toBe(501) + }); + }); + + it('sends 501 if res.end not called by the time handler completes', async () => { + expect.hasAssertions(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(async () => undefined, { + descriptor: '/fake', + use: [] + }), + test: async ({ fetch }) => expect((await fetch()).status).toBe(501) + }); + }); + + it('only populates runtime.error for error handling middleware (and not primary)', async () => { + expect.hasAssertions(); + + const error = new Error('bad stuff happened'); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [ + (_, __, ctx) => expect(ctx.runtime.error).toBeUndefined(), + (_, __, ctx) => expect(ctx.runtime.error).toBeUndefined(), + () => toss(error) + ], + useOnError: [ + (_, __, ctx) => expect(ctx.runtime.error).toBe(error), + (_, __, ctx) => expect(ctx.runtime.error).toBe(error) + ] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).toReject(); + }); + }); + + it('runs one middleware in error handling chain on error in primary chain', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [() => toss(new Error('error'))], + useOnError: [middleware, (_, res) => res.end()] + }), + test: async ({ fetch }) => { + await fetch(); + expect(middleware).toBeCalledTimes(1); + } + }); + }); + }); + + it('runs multiple middleware in error handling chain on error in primary chain', async () => { + expect.hasAssertions(); + + const middleware = [jest.fn(), jest.fn(), ((_, res) => res.end()) as Middleware]; + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [() => toss(new Error('error'))], + useOnError: middleware + }), + test: async ({ fetch }) => { + await fetch(); + middleware.slice(0, -1).forEach((m) => expect(m).toBeCalledTimes(1)); + } + }); + }); + }); + + it('runs one middleware in error handling chain on error in handler', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(() => toss(new Error('error')), { + descriptor: '/fake', + use: [], + useOnError: [middleware, (_, res) => res.end()] + }), + test: async ({ fetch }) => { + await fetch(); + expect(middleware).toBeCalledTimes(1); + } + }); + }); + }); + + it('runs multiple middleware in error handling chain on error in handler', async () => { + expect.hasAssertions(); + + const middleware = [jest.fn(), jest.fn(), ((_, res) => res.end()) as Middleware]; + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(() => toss(new Error('error')), { + descriptor: '/fake', + use: [], + useOnError: middleware + }), + test: async ({ fetch }) => { + await fetch(); + middleware.slice(0, -1).forEach((m) => expect(m).toBeCalledTimes(1)); + } + }); + }); + }); + + it('skips remaining middleware if chain is aborted and aborts chain if runtime.done called', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + + await withMockedOutput(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [(_, __, ctx) => ctx.runtime.done(), middleware, middleware], + useOnError: [(_, __, ctx) => ctx.runtime.done(), middleware, middleware] + }), + test: async ({ fetch }) => { + await fetch(); + expect(middleware).toBeCalledTimes(0); + } + }); + + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(noopHandler, { + descriptor: '/fake', + use: [() => toss(new Error('bad')), middleware, middleware], + useOnError: [() => toss(new Error('bad')), middleware, middleware] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).toReject(); + + expect(middleware).toBeCalledTimes(0); + }); + }); + + it('throws on error in error handling chain', async () => { + expect.hasAssertions(); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [() => toss(new Error('bad'))], + useOnError: [() => toss(new Error('worse'))] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ message: 'worse' }); + }); + }); + + it('throws on error in primary chain if no error handling middleware available', async () => { + expect.hasAssertions(); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [() => toss(new Error('bad'))], + useOnError: [] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ message: 'bad' }); + }); + }); + + it('throws if res.end not called by the time error handling chain completes', async () => { + expect.hasAssertions(); + + await withMockedOutput(async () => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [() => toss(new Error('bad'))], + useOnError: [() => undefined] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ message: 'bad' }); + }); + }); + + it('makes runtime control functions noops if chain completes', async () => { + expect.hasAssertions(); + + const nextWarning = expect.stringContaining( + 'already finished executing; calling runtime.next() at this point is a noop' + ); + + const doneWarning = expect.stringContaining( + 'already finished executing; calling runtime.done() at this point is a noop' + ); + + let next: () => Promise, done: () => void; + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware( + async () => { + expect(stdErrSpy).not.toBeCalledWith(nextWarning); + expect(stdErrSpy).not.toBeCalledWith(doneWarning); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + done(); + expect(stdErrSpy).toBeCalledWith(doneWarning); + + throw new Error('badness'); + }, + { + options: { callDoneOnEnd: false }, + descriptor: '/fake', + use: [ + (_req, _res, { runtime }) => { + next = runtime.next; + done = runtime.done; + } + ], + useOnError: [ + (_req, res, { runtime }) => { + expect(runtime.error).toMatchObject({ message: 'badness' }); + + next = runtime.next; + done = runtime.done; + res.end(); + } + ] + } + ), + test: async ({ fetch }) => { + await fetch(); + + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + done(); + expect(stdErrSpy).toBeCalledWith(doneWarning); + } + }); + }); + }); + }); + + it('makes runtime control functions noops if chain aborts', async () => { + expect.hasAssertions(); + + const nextWarning = expect.stringContaining( + 'aborted; calling runtime.next() at this point is a noop' + ); + + const doneWarning = expect.stringContaining( + 'already aborted; calling runtime.done() at this point is a noop' + ); + + let next: () => Promise, done: () => void; + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await expect( + testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + (_req, _res, { runtime }) => { + next = runtime.next; + done = runtime.done; + throw new Error('aborted'); + } + ], + useOnError: [ + async (_req, _res, { runtime }) => { + expect(stdErrSpy).not.toBeCalledWith(nextWarning); + expect(stdErrSpy).not.toBeCalledWith(doneWarning); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + done(); + expect(stdErrSpy).toBeCalledWith(doneWarning); + + next = runtime.next; + done = runtime.done; + + throw new Error('aborted again'); + } + ] + }), + test: async ({ fetch }) => void (await fetch()) + }) + ).rejects.toMatchObject({ message: 'aborted again' }); + + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + done(); + expect(stdErrSpy).toBeCalledWith(doneWarning); + }); + }); + }); + + it('can pull entire chain (and then some) manually using runtime.next', async () => { + expect.hasAssertions(); + + const nextWarning = expect.stringContaining( + 'already finished executing; calling runtime.next() at this point is a noop' + ); + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + async (_req, res, { runtime: { next } }) => { + await next(); + expect(stdErrSpy).not.toBeCalledWith(nextWarning); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + res.status(200).end(); + } + ] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + } + }); + }); + }); + }); + + it('can pull entire chain manually using runtime.next with warning if called multiple times', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + const nextWarning = expect.stringContaining( + 'already finished executing; calling runtime.next() at this point is a noop' + ); + + await withMockedOutput(async ({ stdErrSpy }) => { + await withDebugEnabled(async () => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + async (_req, _res, { runtime: { next } }) => { + await next(); + expect(stdErrSpy).not.toBeCalledWith(nextWarning); + + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).toBeCalledWith(nextWarning); + + throw new Error('not good bad bad'); + }, + middleware, + middleware + ], + useOnError: [ + async (_req, _res, { runtime: { next, error } }) => { + expect(middleware).toBeCalledTimes(2); + expect(error).toMatchObject({ message: 'not good bad bad' }); + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).not.toBeCalledWith(nextWarning); + + stdErrSpy.mockClear(); + + await next(); + expect(stdErrSpy).toBeCalledWith( + expect.stringContaining( + 'aborted; calling runtime.next() at this point is a noop' + ) + ); + }, + middleware, + middleware, + (_, res) => { + expect(middleware).toBeCalledTimes(4); + res.status(200).end(); + } + ] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(200); + } + }); + }); + }); + }); + + it('skips non-function middleware in chain', async () => { + expect.hasAssertions(); + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + // @ts-expect-error: bad middleware value + 'bad', + // @ts-expect-error: bad middleware value + null, + // @ts-expect-error: bad middleware value + {}, + (_, res) => res.status(403).end() + ] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(403); + expect(stdErrSpy).toBeCalledWith( + expect.stringContaining( + 'skipping execution of non-function item in chain' + ) + ); + } + }); + }); + }); + }); + + it('calls runtime.done on res.end only if options.callDoneOnEnd is true', async () => { + expect.hasAssertions(); + + const middleware = jest.fn(); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [(_, res) => res.status(404).end(), middleware], + options: { callDoneOnEnd: false } + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(404); + expect(middleware).toBeCalledTimes(1); + } + }); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [(_, res) => res.status(403).end(), middleware], + options: { callDoneOnEnd: true } + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(403); + expect(middleware).toBeCalledTimes(1); + } + }); + }); + + it('calls runtime.done on res.end only if chain was not aborted', async () => { + expect.hasAssertions(); + + const skippedMessage = expect.stringContaining('skipped calling runtime.done'); + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + async (_, res, { runtime: { done } }) => { + done(); + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + res.status(404).end(); + expect(stdErrSpy).toBeCalledWith(skippedMessage); + } + ] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(404); + } + }); + + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware(undefined, { + descriptor: '/fake', + use: [ + async () => { + throw new Error('contrived'); + } + ], + useOnError: [ + async (_, res, { runtime: { done, error } }) => { + expect(error).toMatchObject({ message: 'contrived' }); + + done(); + + stdErrSpy.mockClear(); + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + res.status(404).end(); + expect(stdErrSpy).toBeCalledWith(skippedMessage); + } + ] + }), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(404); + } + }); + }); + }); + }); + + it('calls runtime.done on res.end only if chain has not already completed', async () => { + expect.hasAssertions(); + + const skippedMessage = expect.stringContaining('skipped calling runtime.done'); + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware( + async (_, res) => { + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + res.status(404).end(); + expect(stdErrSpy).toBeCalledWith(skippedMessage); + }, + { + descriptor: '/fake', + use: [] + } + ), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(404); + } + }); + }); + }); + }); + + it('does not call runtime.done on res.end if response was already sent', async () => { + expect.hasAssertions(); + + const skippedMessage = expect.stringContaining('skipped calling runtime.done'); + + await withDebugEnabled(async () => { + await withMockedOutput(async ({ stdErrSpy }) => { + await testApiHandler({ + rejectOnHandlerError: true, + handler: withMiddleware( + async (_, res) => { + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + res.status(404).end(); + expect(stdErrSpy).toBeCalledWith(skippedMessage); + stdErrSpy.mockClear(); + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + res.status(404).end(); + expect(stdErrSpy).not.toBeCalledWith(skippedMessage); + }, + { + descriptor: '/fake', + use: [] + } + ), + test: async ({ fetch }) => { + expect((await fetch()).status).toBe(404); + } + }); + }); + }); + }); + + it('supports type generics', async () => { + expect.assertions(0); + + type myMiddlewareOptions = { customOption: boolean }; + + const myMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext + ) => { + res.status(200).send(customOption); + }; + + const myPartialMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext> + ) => { + res.status(200).send(customOption); + }; + + withMiddleware(undefined, { + // @ts-expect-error: MiddlewareContext != MiddlewareContext + use: [myMiddleware] + }); + + withMiddleware(undefined, { + descriptor: '/fake', + use: [myMiddleware] + // TODO: improve TypeScript skills to enforce required options here + }); + + withMiddleware(undefined, { + use: [myMiddleware], + // @ts-expect-error: missing required property: customOption + options: {} + }); + + withMiddleware(undefined, { + use: [myMiddleware], + // @ts-expect-error: bad type for required property: customOption + options: { customOption: 5 } + }); + + withMiddleware(undefined, { + use: [ + myMiddleware, + (_, __, { options: { anotherOpt } }) => { + void anotherOpt; + } + ], + // @ts-expect-error: missing required property: anotherOpt + options: { customOption: true } + }); + + withMiddleware(undefined, { + descriptor: '/fake', + use: [myPartialMiddleware] + }); + + withMiddleware>(undefined, { + descriptor: '/fake', + use: [myPartialMiddleware], + options: {} + }); + }); +}); + +describe('::middlewareFactory', () => { + it('returns a pre-configured withMiddleware instance', async () => { + expect.hasAssertions(); + + type myMiddlewareOptions = { customOption: boolean }; + + const myMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext + ) => { + res.status(200).send({ customOption }); + }; + + const customOption = true; + + const handler = middlewareFactory({ + use: [myMiddleware], + options: { customOption } + })(undefined, { + descriptor: '/fake' + }); + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + await expect((await fetch()).json()).resolves.toStrictEqual({ customOption }); + } + }); + }); + + it('handles appending and prepending to middleware chains', async () => { + expect.hasAssertions(); + + type myMiddlewareOptions = { customOption: boolean }; + + const myMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext + ) => { + res.status(200).send({ customOption }); + }; + + const customOption = true; + + await testApiHandler({ + handler: middlewareFactory({ + use: [myMiddleware], + options: { customOption } + })(undefined, { + descriptor: '/fake', + prependUse: [(_, res) => res.status(201).send({ a: 1 })] + }), + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(201); + await expect(res.json()).resolves.toStrictEqual({ a: 1 }); + } + }); + + await testApiHandler({ + handler: middlewareFactory({ + use: [(_, res) => void res.status(202)] + })(undefined, { + descriptor: '/fake', + appendUse: [(_, res) => res.send({ b: 1 })] + }), + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(202); + await expect(res.json()).resolves.toStrictEqual({ b: 1 }); + } + }); + + await testApiHandler({ + handler: middlewareFactory({ + use: [myMiddleware], + options: { customOption } + })(undefined, { + descriptor: '/fake', + prependUse: [() => toss(new DummyError('bad bad not good'))], + prependUseOnError: [(_, res) => void res.status(203)], + appendUseOnError: [(_, res) => res.send({ c: 1 })] + }), + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(203); + await expect(res.json()).resolves.toStrictEqual({ c: 1 }); + } + }); + }); + + it('supports type generics', async () => { + expect.assertions(0); + + type myMiddlewareOptions = { customOption: boolean }; + + const myMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext + ) => { + res.status(200).send(customOption); + }; + + const myPartialMiddleware = ( + _: NextApiRequest, + res: NextApiResponse, + { options: { customOption } }: MiddlewareContext> + ) => { + res.status(200).send(customOption); + }; + + middlewareFactory({ + // @ts-expect-error: MiddlewareContext != MiddlewareContext + use: [myMiddleware] + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [myMiddleware] + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [myMiddleware], + // @ts-expect-error: missing required property: customOption + options: {} + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [myMiddleware], + // @ts-expect-error: bad type for required property: customOption + options: { customOption: 5 } + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [ + myMiddleware, + (_, __, { options: { anotherOpt } }) => { + void anotherOpt; + } + ], + // @ts-expect-error: missing required property: anotherOpt + options: { customOption: true } + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [myPartialMiddleware] + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory>({ + use: [myPartialMiddleware], + options: {} + })(undefined, { + descriptor: '/fake' + }); + + middlewareFactory({ + use: [myPartialMiddleware] + })(undefined, { + descriptor: '/fake', + // @ts-expect-error: MiddlewareContext != MiddlewareContext + appendUse: [myMiddleware] + }); + + middlewareFactory({ + use: [myPartialMiddleware] + })(undefined, { + descriptor: '/fake', + appendUse: [myPartialMiddleware], + appendUseOnError: [myPartialMiddleware] + }); + + middlewareFactory({ + use: [myPartialMiddleware] + })(undefined, { + descriptor: '/fake', + prependUse: [myMiddleware], + prependUseOnError: [myMiddleware] + }); + + middlewareFactory({ + use: [myPartialMiddleware] + })(undefined, { + descriptor: '/fake', + // @ts-expect-error: bad type for required property: customOption + options: { customOption: 5 } + }); + }); +}); diff --git a/lib/next-api-respond/index.ts b/lib/next-api-respond/index.ts new file mode 100644 index 0000000..1a0fb65 --- /dev/null +++ b/lib/next-api-respond/index.ts @@ -0,0 +1,206 @@ +import type { NextApiResponse } from 'next'; +import type { HttpStatusCode, JsonSuccess, JsonError } from '@xunnamius/types'; + +/** + * Sends a generic HTTP response with the given `statusCode` and optional + * `responseJson` body (defaults to `{}`). This is the "base" function called by + * all other response functions. + */ +export function sendGenericHttpResponse( + res: NextApiResponse, + statusCode: HttpStatusCode, + responseJson?: Record +) { + res + .setHeader('content-type', 'application/json') + .status(statusCode) + .send(responseJson || {}); +} + +/** + * Sends a generic "success" response and `responseJson` body, optionally with + * additional properties. This function is called by all 2xx response functions. + */ +export function sendHttpSuccessResponse( + res: NextApiResponse, + statusCode: HttpStatusCode, + responseJson?: Record +) { + const json: JsonSuccess = { success: true, ...responseJson }; + sendGenericHttpResponse(res, statusCode, json); + return json; +} + +/** + * Sends a generic "error" response and `responseJson` body, optionally with + * additional properties. This function is called by all non-2xx response + * functions. + */ +export function sendHttpErrorResponse( + res: NextApiResponse, + statusCode: HttpStatusCode, + responseJson: Record & { error: string } +) { + const json: JsonError = { success: false, ...responseJson }; + sendGenericHttpResponse(res, statusCode, json); + return json; +} + +/** + * Sends an HTTP 200 "ok" response with optional `responseJson` data. + */ +export function sendHttpOk( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpSuccessResponse(res, 200, responseJson); +} + +/** + * Sends an HTTP 400 "client error" response with optional `responseJson` data. + */ +export function sendHttpBadRequest( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 400, { + error: 'request was malformed or otherwise bad', + ...responseJson + }); +} + +/** + * Sends an HTTP 401 "unauthenticated" response with optional `responseJson` + * data. + */ +export function sendHttpUnauthenticated( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 401, { + error: 'client is not authenticated', + ...responseJson + }); +} + +/** + * Sends an HTTP 403 "forbidden" ("unauthorized") response with optional + * `responseJson` data. + */ +export function sendHttpUnauthorized( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 403, { + error: 'client is not authorized to access this resource', + ...responseJson + }); +} + +/** + * Sends an HTTP 404 "not found" response with optional `responseJson` data. + */ +export function sendHttpNotFound( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 404, { + error: 'resource was not found', + ...responseJson + }); +} + +/** + * Sends an HTTP 405 "bad method" response with optional `responseJson` data. + */ +export function sendHttpBadMethod( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 405, { + error: 'bad method', + ...responseJson + }); +} + +/** + * Sends an HTTP 413 "too big" response with optional `responseJson` data. + */ +export function sendHttpTooLarge( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 413, { + error: 'request body is too large', + ...responseJson + }); +} + +/** + * Sends an HTTP 415 "unsupported media type" response with optional + * `responseJson` data. + */ +export function sendHttpBadContentType( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 415, { + error: 'request payload is in an unsupported format', + ...responseJson + }); +} + +/** + * Sends an HTTP 429 "too many requests" response with optional `responseJson` + * data. + */ +export function sendHttpRateLimited( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 429, { + error: 'client is rate limited', + ...responseJson + }); +} + +/** + * Sends a generic HTTP 500 "error" response with `error` property and optional + * `responseJson` data. + */ +export function sendHttpError( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 500, { + error: '🤯 something unexpected happened on our end 🤯', + ...responseJson + }); +} + +/** + * Sends an HTTP 501 "not implemented" response with optional `responseJson` + * data. + */ +export function sendNotImplemented( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 501, { + error: 'this endpoint has not yet been implemented', + ...responseJson + }); +} + +/** + * Sends an HTTP 555 "contrived" response with optional `responseJson` data. + */ +export function sendHttpContrivedError( + res: NextApiResponse, + responseJson?: Record +) { + sendHttpErrorResponse(res, 555, { + error: '(note: do not report this contrived error)', + ...responseJson + }); +} diff --git a/lib/next-api-respond/package.json b/lib/next-api-respond/package.json new file mode 100644 index 0000000..0b40e8e --- /dev/null +++ b/lib/next-api-respond/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-api-respond" +} diff --git a/lib/next-api-respond/unit.test.ts b/lib/next-api-respond/unit.test.ts new file mode 100644 index 0000000..78a7bb2 --- /dev/null +++ b/lib/next-api-respond/unit.test.ts @@ -0,0 +1,533 @@ +import { + sendGenericHttpResponse, + sendHttpBadMethod, + sendHttpBadRequest, + sendHttpContrivedError, + sendHttpError, + sendHttpOk, + sendHttpErrorResponse, + sendHttpNotFound, + sendHttpRateLimited, + sendHttpSuccessResponse, + sendHttpTooLarge, + sendHttpUnauthenticated, + sendHttpUnauthorized, + sendNotImplemented, + sendHttpBadContentType +} from 'multiverse/next-api-respond'; +import { testApiHandler } from 'next-test-api-route-handler'; + +describe('::sendGenericHttpResponse', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendGenericHttpResponse(res, 201); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(201); + await expect(res.json()).resolves.toStrictEqual({}); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendGenericHttpResponse(res, 201, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(201); + await expect(res.json()).resolves.toStrictEqual({ json: 'data' }); + } + }); + }); + + it('sends application/json header', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendGenericHttpResponse(res, 200); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + expect(res.headers.get('content-type')).toStartWith('application/json'); + } + }); + }); +}); + +describe('::sendHttpBadMethod', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadMethod(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(405); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'bad method' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadMethod(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(405); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'bad method', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpBadRequest', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadRequest(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(400); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request was malformed or otherwise bad' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadRequest(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(400); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request was malformed or otherwise bad', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpContrivedError', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpContrivedError(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(555); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: '(note: do not report this contrived error)' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpContrivedError(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(555); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: '(note: do not report this contrived error)', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpError', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpError(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(500); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: '🤯 something unexpected happened on our end 🤯' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpError(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(500); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: '🤯 something unexpected happened on our end 🤯', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpOk', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpOk(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toStrictEqual({ + success: true + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpOk(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toStrictEqual({ + success: true, + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpErrorResponse', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpErrorResponse(res, 400, { json: 'data', error: 'error' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(400); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + json: 'data', + error: 'error' + }); + } + }); + }); +}); + +describe('::sendHttpNotFound', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpNotFound(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(404); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'resource was not found' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpNotFound(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(404); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'resource was not found', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpRateLimited', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpRateLimited(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(429); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is rate limited' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpRateLimited(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(429); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is rate limited', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpSuccessResponse', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpSuccessResponse(res, 202); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(202); + await expect(res.json()).resolves.toStrictEqual({ + success: true + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpSuccessResponse(res, 202, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(202); + await expect(res.json()).resolves.toStrictEqual({ + success: true, + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpTooLarge', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpTooLarge(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(413); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request body is too large' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpTooLarge(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(413); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request body is too large', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpBadContentType', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadContentType(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(415); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request payload is in an unsupported format' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpBadContentType(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(415); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'request payload is in an unsupported format', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpUnauthenticated', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpUnauthenticated(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(401); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is not authenticated' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpUnauthenticated(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(401); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is not authenticated', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendHttpUnauthorized', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendHttpUnauthorized(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(403); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is not authorized to access this resource' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendHttpUnauthorized(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(403); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'client is not authorized to access this resource', + json: 'data' + }); + } + }); + }); +}); + +describe('::sendNotImplemented', () => { + it('sends appropriate response given arguments', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: (_, res) => { + sendNotImplemented(res); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(501); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'this endpoint has not yet been implemented' + }); + } + }); + + await testApiHandler({ + handler: (_, res) => { + sendNotImplemented(res, { json: 'data' }); + }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(501); + await expect(res.json()).resolves.toStrictEqual({ + success: false, + error: 'this endpoint has not yet been implemented', + json: 'data' + }); + } + }); + }); +}); diff --git a/lib/next-auth/index.ts b/lib/next-auth/index.ts new file mode 100644 index 0000000..13c12c8 --- /dev/null +++ b/lib/next-auth/index.ts @@ -0,0 +1,699 @@ +import { getEnv } from 'multiverse/next-env'; +import { getDb } from 'multiverse/mongo-schema'; +import { toss } from 'toss-expression'; +import { isError } from '@xunnamius/types'; +import { debugFactory } from 'multiverse/debug-extended'; +import { randomUUID as generateUUID } from 'node:crypto'; +import { MongoServerError } from 'mongodb'; + +import { + AppValidationError, + GuruMeditationError, + InvalidAppConfigurationError, + InvalidSecretError +} from 'universe/error'; + +import type { WithId, WithoutId } from 'mongodb'; +import type { JsonValue, Merge } from 'type-fest'; + +// TODO: consider breaking this into multiple different files (and tests) when +// TODO: turned into a standalone package. Also, multiple debug identifiers. + +const debug = debugFactory('next-auth:index'); + +// * Well-known tokens + +/** + * This string is guaranteed never to appear in data generated during tests or + * in production. Hence, this string can be used to represent a `null` or + * non-existent token. This string cannot be used for authenticated HTTP access + * to the API. + */ +export const NULL_BEARER_TOKEN = '00000000-0000-0000-0000-000000000000'; + +/** + * This string allows authenticated API access only when running in a test + * environment (i.e. `NODE_ENV=test`). This string cannot be used for + * authenticated HTTP access to the API in production. + */ +export const DUMMY_BEARER_TOKEN = '12349b61-83a7-4036-b060-213784b491'; + +/** + * This string is guaranteed to be rate limited when running in a test + * environment (i.e. `NODE_ENV=test`). This string cannot be used for + * authenticated HTTP access to the API in production. + */ +export const BANNED_BEARER_TOKEN = 'banned-h54e-6rt7-gctfh-hrftdygct0'; + +/** + * This string can be used to authenticate with local and _non-web-facing_ test + * and preview deployments as a global administrator. This string cannot be used + * for authenticated HTTP access to the API in production. + */ +export const DEV_BEARER_TOKEN = 'dev-xunn-dev-294a-536h-9751-rydmj'; + +// * Well-known authentication (Authorization header) schemes and attributes + +/** + * An array of supported authentication schemes. + */ +// ! Must be lowercase alphanumeric (enforced by unit tests) +export const authSchemes = ['bearer'] as const; + +/** + * An array of allowed "auth" entry attributes. Each array element must + * correspond to a field in the {@link TokenAttributes} type and vice-versa. + */ +export const authAttributes = ['owner', 'isGlobalAdmin'] as const; + +/** + * A supported authentication scheme. + */ +export type AuthScheme = typeof authSchemes[number]; + +/** + * A supported "auth" entry attribute (field name). + */ +export type AuthAttribute = typeof authAttributes[number]; + +// * Well-known authorization (Authorization header) constraints + +/** + * An array of supported authorization constraints. + */ +export const authConstraints = [ + /** + * This constraint ensures that only "auth" entries that have the + * `globalAdmin` field set to `true` are successfully authenticated. + */ + 'isGlobalAdmin' +] as const; + +/** + * A supported authorization constraint. + */ +export type AuthConstraint = typeof authConstraints[number]; + +// * Base token interfaces + +/** + * The shape of a token and scheme data that might be contained within an entry + * in the well-known "auth" collection. + */ +export type TargetToken = Partial<{ + /** + * The authentication scheme of the target token. + */ + scheme: string; + /** + * The target token. + */ + token: Record; +}>; + +/** + * The shape of the actual token and scheme data contained within an entry in + * the well-known "auth" collection. + */ +export type Token = { + /** + * The authentication scheme this token supports. + */ + scheme: AuthScheme; + /** + * The actual token. + */ + token: Record; +}; + +/** + * The shape of the attributes associated with an entry in the well-known "auth" + * collection. Each property must correspond to an array element in the + * {@link authAttributes} array and vice-versa. + */ +// ! `owner` must be the only required property. All others must be optional. +export type TokenAttributes = { + /** + * A string (or stringified ObjectId) representing the owner of the token. + */ + owner: string; + /** + * If `true`, the token grants access to potentially dangerous abilities via + * the well-known "/sys" API endpoint. + * + * @default undefined + */ + isGlobalAdmin?: boolean; +}; + +/** + * The base shape of an entry in the well-known "auth" collection. **More + * complex entry types must extend from or intersect with this base type.** + */ +export type InternalAuthEntry = WithId< + { + /** + * Metadata attributes associated with this "auth" entry. + */ + attributes: TokenAttributes; + } & Token +>; + +/** + * The base shape of a new entry in the well-known "auth" collection. More + * complex entry types may or may not extend from or intersect with this type. + * + * Each API has the latitude to generate a token using whichever available + * scheme is most convenient. Hence, the only external data necessary to create + * a new auth entry is `attributes`. + */ +export type NewAuthEntry = Pick; + +/** + * The public base shape derived from an entry in the well-known "auth" + * collection. + */ +export type PublicAuthEntry = WithoutId; + +// * Bearer token interfaces + +/** + * The shape of a bearer token object. + */ +export type BearerToken = { + /** + * The authentication scheme this token supports. + */ + scheme: 'bearer'; + /** + * The bearer token. + */ + token: { + bearer: string; + }; +}; + +/** + * The shape of a bearer token entry in the well-known "auth" collection. + */ +export type InternalAuthBearerEntry = Merge; + +// * Type guards + +/** + * Type guard that returns `true` if `obj` satisfies the {@link AuthScheme} + * interface. Additional constraints may be enforced such that `obj` is among a + * _subset_ of allowable schemes via the `onlyAllowSubset` parameter. + */ +export function isAllowedScheme( + obj: unknown, + onlyAllowSubset?: AuthScheme | AuthScheme[] +): obj is AuthScheme { + return !![onlyAllowSubset || authSchemes].flat().includes(obj as AuthScheme); +} + +/** + * Type guard that returns `true` if `obj` satisfies the {@link TokenAttributes} + * interface. + */ +export function isTokenAttributes( + obj: unknown, + { patch }: { patch: boolean } = { patch: false } +): obj is TokenAttributes { + const attr = obj as TokenAttributes; + if (!!attr && typeof attr == 'object') { + const isValidOwner = !!attr.owner && typeof attr.owner == 'string'; + const isValidGlobalAdmin = + attr.isGlobalAdmin === undefined || typeof attr.isGlobalAdmin == 'boolean'; + const allKeysAreValid = Object.keys(attr).every((key) => + authAttributes.includes(key as AuthAttribute) + ); + + if (allKeysAreValid) { + if (patch) { + return (attr.owner === undefined || isValidOwner) && isValidGlobalAdmin; + } else { + return isValidOwner && isValidGlobalAdmin; + } + } + } + + return false; +} + +/** + * Type guard that returns `true` if `obj` satisfies the {@link NewAuthEntry} + * interface. + */ +export function isNewAuthEntry(obj: unknown): obj is NewAuthEntry { + const entry = obj as NewAuthEntry; + return isTokenAttributes(entry?.attributes); +} + +// * Token utilities + +/** + * Derives a token and scheme from an authentication string (such as an + * Authorization header). **Does not check the database for token existence**. + * Throws on invalid/missing authentication string. + */ +export async function deriveSchemeAndToken({ + authString, + allowedSchemes +}: { + /** + * The authentication string used to derive a token and scheme. + */ + authString?: string | undefined; + /** + * Accepted authentication schemes. By default, all schemes are accepted. + */ + allowedSchemes?: AuthScheme | AuthScheme[]; +}): Promise; +/** + * Derives a token and scheme from authentication data. Throws on + * invalid/missing authentication data. + */ +export async function deriveSchemeAndToken({ + authData, + allowedSchemes +}: { + /** + * The data used to derive a token and scheme. + */ + authData?: TargetToken; + /** + * Accepted authentication schemes. By default, all schemes are accepted. + */ + allowedSchemes?: AuthScheme | AuthScheme[]; +}): Promise; +export async function deriveSchemeAndToken({ + authString, + authData, + allowedSchemes +}: { + /** + * The authentication string used to derive a token and scheme. + */ + authString?: string | undefined; + /** + * The parameters used to derive a token and scheme. + */ + authData?: TargetToken; + /** + * Accepted authentication schemes. By default, all schemes are accepted. + */ + allowedSchemes?: AuthScheme | AuthScheme[]; +}): Promise { + if (authString !== undefined) { + if ( + !authString || + typeof authString != 'string' || + !/^\S+ \S/.test(authString) || + authString.length > getEnv().AUTH_HEADER_MAX_LENGTH + ) { + throw new InvalidSecretError('auth string'); + } + + let scheme: AuthScheme; + const [rawScheme, ...rawCredentials] = authString.split(/\s/gi); + const maybeScheme = rawScheme.toLowerCase(); + + debug(`deriving token of scheme "${maybeScheme}" from auth string`); + + if (isAllowedScheme(maybeScheme, allowedSchemes)) { + scheme = maybeScheme; + } else { + throw new InvalidSecretError('scheme (disallowed or unknown)'); + } + + const credentials = rawCredentials.flatMap((c) => c.split(',')).filter(Boolean); + + if (scheme == 'bearer') { + if (credentials.length == 1) { + return { scheme, token: { bearer: credentials[0] } }; + } else { + throw new InvalidSecretError('token syntax'); + } + } /*else if(scheme == '...') { + ... + }*/ else { + throw new GuruMeditationError( + `auth string handler for scheme "${scheme}" is not implemented` + ); + } + } else if (authData !== undefined) { + if (!authData || typeof authData != 'object') { + throw new InvalidSecretError('auth data'); + } + + let scheme: AuthScheme; + const maybeScheme = authData.scheme?.toLowerCase(); + + debug(`deriving token of scheme "${maybeScheme}" from auth data`); + + if (isAllowedScheme(maybeScheme, allowedSchemes)) { + scheme = maybeScheme; + } else { + throw new InvalidSecretError('scheme (disallowed or unknown)'); + } + + if (scheme == 'bearer') { + if ( + authData.token && + typeof authData.token == 'object' && + Object.keys(authData.token).length == 1 && + authData.token.bearer && + typeof authData.token.bearer == 'string' + ) { + return { scheme, token: { bearer: authData.token.bearer } }; + } else { + throw new InvalidSecretError('token syntax'); + } + } /*else if(scheme == '...') { + ... + }*/ else { + throw new GuruMeditationError( + `auth data handler for scheme "${scheme}" is not implemented` + ); + } + } else { + throw new InvalidSecretError('invocation'); + } +} + +/** + * Transform an internal entry from the well-known "auth" MongoDB collection + * into one that is safe for public consumption. + */ +export function toPublicAuthEntry(entry: InternalAuthEntry): PublicAuthEntry { + const { _id, ...publicEntry } = entry; + return publicEntry; +} + +// * Authorization header checks + +/** + * Authenticates a client via their Authorization header using the well-known + * "auth" MongoDB collection. Does not throw on invalid/missing header string. + * + * Despite the unfortunate name of the "Authorization" header, this function is + * only used for authentication, not authorization. + */ +export async function authenticateHeader({ + header, + allowedSchemes +}: { + /** + * Contents of the HTTP Authorization header. + */ + header: string | undefined; + /** + * Accepted authentication schemes. By default, all schemes are accepted. + */ + allowedSchemes?: AuthScheme | AuthScheme[]; +}): Promise<{ authenticated: boolean; error?: unknown }> { + let scheme: Awaited>['scheme']; + let token: Awaited>['token']; + + try { + ({ scheme, token } = await deriveSchemeAndToken({ + authString: header, + allowedSchemes + })); + } catch (e) { + return { + authenticated: false, + error: `bad Authorization header: ${ + isError(e) ? e.message : /* istanbul ignore next */ e + }` + }; + } + + return { + authenticated: await ( + await getDb({ name: 'root' }) + ) + .collection('auth') + // ? To hit the index, order matters + .findOne({ scheme, token }) + .then((r) => !!r) + }; +} + +/** + * Authorizes a client via their Authorization header using the well-known + * "auth" MongoDB collection. Does not throw on invalid/missing header string. + */ +export async function authorizeHeader({ + header, + constraints +}: { + /** + * Contents of the HTTP Authorization header. + */ + header: string | undefined; + /** + * Constraints a client must satisfy to be considered authorized. + */ + constraints?: AuthConstraint | AuthConstraint[]; +}): Promise<{ authorized: boolean; error?: unknown }> { + let attributes: Awaited>; + + try { + attributes = await getAttributes({ + target: await deriveSchemeAndToken({ authString: header }) + }); + } catch (e) { + return { + authorized: false, + error: `bad Authorization header: ${ + isError(e) ? e.message : /* istanbul ignore next */ e + }` + }; + } + + if ( + typeof constraints != 'string' && + (!Array.isArray(constraints) || !constraints.length) + ) { + debug.warn('header authorization was vacuous (no constraints)'); + } else { + const constraintsArray = [constraints].flat(); + const finalConstraints = Array.from(new Set(constraintsArray)); + + if (finalConstraints.length != constraintsArray.length) { + throw new InvalidAppConfigurationError( + 'encountered duplicate authorization constraints' + ); + } else { + try { + await Promise.all( + finalConstraints.map(async (constraint) => { + debug(`evaluating authorization constraint "${constraint}"`); + + const failAuthorization = () => { + throw `failed to satisfy authorization constraint "${constraint}"`; + }; + + if (constraint == 'isGlobalAdmin') { + if (!attributes.isGlobalAdmin) { + failAuthorization(); + } + } /*else if(constraint == '...') { + ... + }*/ else { + throw new InvalidAppConfigurationError( + `encountered unknown or unhandled authorization constraint "${constraint}"` + ); + } + }) + ); + } catch (error) { + if (isError(error)) { + throw error; + } + + return { authorized: false, error }; + } + } + } + + return { authorized: true }; +} + +// * MongoDB "auth" collection accessors and mutators + +/** + * Returns an entry's attributes by matching the target data against the + * well-known "auth" MongoDB collection. Throws on invalid/missing data. + */ +export async function getAttributes({ + target +}: { + target?: TargetToken; +}): Promise { + const { scheme, token } = await deriveSchemeAndToken({ authData: target }); + + const { attributes } = + (await (await getDb({ name: 'root' })) + .collection('auth') + .findOne<{ attributes: T }>( + // ? To hit the index, order matters + { scheme, token }, + { projection: { _id: false, attributes: true } } + )) || + toss(new InvalidSecretError('authentication scheme and token combination')); + + return attributes; +} + +/** + * Updates an entry's attributes by matching the provided data against the + * well-known "auth" MongoDB collection. Throws on invalid/missing target or + * entry data. + * + * **Note that the new `attributes` object will _patch_, not replace, the old + * object.** + */ +export async function updateAttributes({ + target, + attributes +}: { + /** + * The target `token` and its `scheme` whose attributes will be updated. + */ + target?: TargetToken; + /** + * The updated attributes + */ + attributes?: Partial; +}): Promise { + const { scheme, token } = await deriveSchemeAndToken({ authData: target }); + + if (isTokenAttributes(attributes, { patch: true })) { + if (Object.keys(attributes).length) { + const result = await (await getDb({ name: 'root' })) + .collection('auth') + .updateOne( + // ? To hit the index, order matters + { scheme, token }, + // * Aggregation pipeline: https://stackoverflow.com/a/56604200/1367414 + [{ $addFields: { attributes } }] + ); + + if (result.matchedCount != 1) { + throw new InvalidSecretError('authentication scheme and token combination'); + } + } + } else { + throw new InvalidSecretError('attributes'); + } +} + +/** + * Returns all entries with a matching `owner` attribute in the well-known + * "auth" MongoDB collection. Throws on invalid/missing `owner` attribute. + */ +export async function getOwnersEntries({ + owners: rawOwners +}: { + /** + * An array of one or more valid `owner` tokens. + * + * @see {@link TokenAttributes} + */ + owners: (TokenAttributes['owner'] | undefined)[]; +}): Promise { + const owners = rawOwners.filter((owner): owner is string => owner !== undefined); + + const isValidOwnerArray = ( + owners: unknown[] + ): owners is TokenAttributes['owner'][] => { + return owners.every((owner) => isTokenAttributes({ owner })); + }; + + const returnAll = owners.length == 0; + + if (returnAll || isValidOwnerArray(owners)) { + return (await getDb({ name: 'root' })) + .collection('auth') + .find( + // * Query is covered by the index + returnAll ? {} : { 'attributes.owner': { $in: owners } }, + { projection: { _id: false }, sort: { _id: 1 } } + ) + .toArray(); + } else { + throw new InvalidSecretError('owner(s)'); + } +} + +/** + * Generates a new entry in the well-known "auth" MongoDB collection, including + * the provided attribute metadata (if any). Throws on invalid entry data. + * + * The current version of this function uses the `bearer` scheme to create v4 + * UUID "bearer tokens". This _implementation detail_ may change at any time. + */ +export async function createEntry({ + entry +}: { + /** + * Data used to generate a new "auth" entry. + */ + entry?: Partial; +}): Promise { + if (isNewAuthEntry(entry)) { + const newEntry: WithoutId = { + attributes: entry.attributes, + scheme: 'bearer', + // ! Due to how MongoDB works, it is EXTREMELY important that new entries' + // ! token object properties are ALWAYS in an consistent, expected order. + // ! This only matters when entry.token has more than one property. + token: { bearer: generateUUID() } + }; + + try { + await (await getDb({ name: 'root' })) + .collection('auth') + .insertOne({ ...newEntry }); + } catch (e) { + /* istanbul ignore else */ + if (e instanceof MongoServerError && e.code == 11000) { + throw new AppValidationError('token collision'); + } else { + throw e; + } + } + return newEntry; + } else { + throw new InvalidSecretError('entry data'); + } +} + +/** + * Deletes an entry in the well-known "auth" MongoDB collection by matching + * against the target data. Throws on invalid/missing target data. + */ +export async function deleteEntry({ + target +}: { + /** + * The target `token` and its `scheme` to delete. + */ + target?: TargetToken; +}): Promise { + const { scheme, token } = await deriveSchemeAndToken({ authData: target }); + + const result = await (await getDb({ name: 'root' })) + .collection('auth') + .deleteOne( + // ? To hit the index, order matters + { scheme, token } + ); + + if (result.deletedCount != 1) { + throw new InvalidSecretError('authentication scheme and token combination'); + } +} diff --git a/lib/next-auth/package.json b/lib/next-auth/package.json new file mode 100644 index 0000000..508d875 --- /dev/null +++ b/lib/next-auth/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-auth" +} diff --git a/lib/next-auth/unit.test.ts b/lib/next-auth/unit.test.ts new file mode 100644 index 0000000..9b8f77f --- /dev/null +++ b/lib/next-auth/unit.test.ts @@ -0,0 +1,1318 @@ +import { useMockDateNow, dummyRootData } from 'multiverse/mongo-common'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { getDb } from 'multiverse/mongo-schema'; +import { ObjectId } from 'mongodb'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { randomUUID } from 'node:crypto'; + +import { + BANNED_BEARER_TOKEN, + DEV_BEARER_TOKEN, + DUMMY_BEARER_TOKEN, + NULL_BEARER_TOKEN, + deriveSchemeAndToken, + authenticateHeader, + getAttributes, + authSchemes, + authConstraints, + authorizeHeader, + updateAttributes, + isAllowedScheme, + isTokenAttributes, + isNewAuthEntry, + getOwnersEntries, + deleteEntry, + createEntry, + toPublicAuthEntry +} from 'multiverse/next-auth'; + +import * as NextAuthSpyTarget from 'multiverse/next-auth'; + +import type { + AuthScheme, + AuthAttribute, + TokenAttributes, + AuthConstraint, + TargetToken, + InternalAuthEntry, + PublicAuthEntry +} from 'multiverse/next-auth'; + +import type { WithoutId } from 'mongodb'; + +setupMemoryServerOverride(); +useMockDateNow(); + +jest.mock('node:crypto'); + +const mockRandomUUID = asMockedFunction(randomUUID); + +const _authSchemes = authSchemes.slice(); +const _authConstraints = authConstraints.slice(); +const mutableAuthSchemes = authSchemes as unknown as string[]; +const mutableAuthConstraints = authConstraints as unknown as string[]; + +beforeEach(() => { + mockRandomUUID.mockReturnValue(DUMMY_BEARER_TOKEN); +}); + +afterEach(() => { + mutableAuthSchemes.splice(0, mutableAuthSchemes.length, ..._authSchemes); + mutableAuthConstraints.splice( + 0, + mutableAuthConstraints.length, + ..._authConstraints + ); +}); + +test('ensure authSchemes contains only lowercase alphanumeric strings', () => { + expect.hasAssertions(); + const isLowercaseAlphanumeric = /^[a-z0-9]+$/; + + expect( + authSchemes.every( + (scheme) => typeof scheme == 'string' && isLowercaseAlphanumeric.test(scheme) + ) + ).toBeTrue(); +}); + +test("ensure authAttributes forms a bijection on TokenAttributes's fields", () => { + expect.hasAssertions(); + + // ? This is a TypeScript-only "test" where type checking will fail if + // ? `TokenAttributes` does not match `authAttributes`. While this won't fail + // ? when run via jest, this will fail the pre-commit hook. + const x: keyof TokenAttributes = '' as AuthAttribute; + const y: AuthAttribute = '' as keyof TokenAttributes; + expect(x).toBe(y); +}); + +describe('::isAllowedScheme', () => { + it('returns true only if passed an AuthScheme', async () => { + expect.hasAssertions(); + + expect(isAllowedScheme('bearer')).toBeTrue(); + expect(isAllowedScheme('nope')).toBeFalse(); + + mutableAuthSchemes.push('nope'); + + expect(isAllowedScheme('nope')).toBeTrue(); + }); + + it('returns true only if passed an allowed AuthScheme when using onlyAllowSubset', async () => { + expect.hasAssertions(); + + expect(isAllowedScheme('bearer')).toBeTrue(); + expect(isAllowedScheme('bearer', [])).toBeFalse(); + expect(isAllowedScheme('bearer', ['nope' as AuthScheme])).toBeFalse(); + expect(isAllowedScheme('nope', ['nope' as AuthScheme])).toBeTrue(); + expect(isAllowedScheme('nope', 'nope' as AuthScheme)).toBeTrue(); + expect(isAllowedScheme('nope', 'bearer')).toBeFalse(); + }); +}); + +describe('::isTokenAttributes', () => { + it('returns true only if passed TokenAttributes', async () => { + expect.hasAssertions(); + + expect(isTokenAttributes(undefined)).toBeFalse(); + expect(isTokenAttributes(null)).toBeFalse(); + expect(isTokenAttributes(1)).toBeFalse(); + expect(isTokenAttributes('1')).toBeFalse(); + expect(isTokenAttributes({})).toBeFalse(); + expect(isTokenAttributes({ owner: true })).toBeFalse(); + expect(isTokenAttributes({ owner: null })).toBeFalse(); + expect(isTokenAttributes({ owner: undefined })).toBeFalse(); + expect(isTokenAttributes({ owner: '' })).toBeFalse(); + expect(isTokenAttributes({ owner: 'owner', isGlobalAdmin: 1 })).toBeFalse(); + expect(isTokenAttributes({ owner: 'owner', isGlobalAdmin: 'true' })).toBeFalse(); + + expect( + isTokenAttributes({ owner: 'owner', isGlobalAdmin: false, extra: 'prop' }) + ).toBeFalse(); + + expect(isTokenAttributes({ owner: 'owner' })).toBeTrue(); + expect(isTokenAttributes({ owner: 'owner', isGlobalAdmin: false })).toBeTrue(); + expect(isTokenAttributes({ isGlobalAdmin: false })).toBeFalse(); + }); + + it('returns true if passed partial TokenAttributes in patch mode', async () => { + expect.hasAssertions(); + + expect(isTokenAttributes(undefined, { patch: true })).toBeFalse(); + expect(isTokenAttributes(null, { patch: true })).toBeFalse(); + expect(isTokenAttributes(1, { patch: true })).toBeFalse(); + expect(isTokenAttributes('1', { patch: true })).toBeFalse(); + expect(isTokenAttributes({}, { patch: true })).toBeTrue(); + expect(isTokenAttributes({ owner: true }, { patch: true })).toBeFalse(); + expect(isTokenAttributes({ owner: null }, { patch: true })).toBeFalse(); + expect(isTokenAttributes({ owner: undefined }, { patch: true })).toBeTrue(); + expect(isTokenAttributes({ owner: '' })).toBeFalse(); + + expect( + isTokenAttributes({ owner: 'owner', isGlobalAdmin: 1 }, { patch: true }) + ).toBeFalse(); + + expect( + isTokenAttributes({ owner: 'owner', isGlobalAdmin: 'true' }, { patch: true }) + ).toBeFalse(); + + expect( + isTokenAttributes({ owner: 'owner', isGlobalAdmin: false, extra: 'prop' }) + ).toBeFalse(); + + expect(isTokenAttributes({ owner: 'owner' }, { patch: true })).toBeTrue(); + + expect( + isTokenAttributes({ owner: 'owner', isGlobalAdmin: false }, { patch: true }) + ).toBeTrue(); + + expect(isTokenAttributes({ isGlobalAdmin: false }, { patch: true })).toBeTrue(); + }); +}); + +describe('::isNewAuthEntry', () => { + it('returns true only if passed a NewAuthEntry', async () => { + expect.hasAssertions(); + + expect(isNewAuthEntry(undefined)).toBeFalse(); + expect(isNewAuthEntry(null)).toBeFalse(); + expect(isNewAuthEntry(1)).toBeFalse(); + expect(isNewAuthEntry('1')).toBeFalse(); + expect(isNewAuthEntry({})).toBeFalse(); + expect(isNewAuthEntry({ attributes: undefined })).toBeFalse(); + expect(isNewAuthEntry({ attributes: null })).toBeFalse(); + expect(isNewAuthEntry({ attributes: { owner: true } })).toBeFalse(); + expect(isNewAuthEntry({ attributes: { owner: null } })).toBeFalse(); + + expect( + isNewAuthEntry({ attributes: { owner: 'owner', isGlobalAdmin: 1 } }) + ).toBeFalse(); + + expect( + isNewAuthEntry({ attributes: { owner: 'owner', isGlobalAdmin: 'true' } }) + ).toBeFalse(); + + expect( + isNewAuthEntry({ + attributes: { owner: 'owner', isGlobalAdmin: false, extra: 'prop' } + }) + ).toBeFalse(); + + expect( + isNewAuthEntry({ attributes: { owner: 'owner', isGlobalAdmin: false } }) + ).toBeTrue(); + + expect(isNewAuthEntry({ attributes: { owner: 'owner' } })).toBeTrue(); + }); +}); + +describe('::deriveSchemeAndToken', () => { + it('handles schemes case-insensitively', async () => { + expect.hasAssertions(); + + const expected1 = await deriveSchemeAndToken({ authString: 'bearer 123' }); + + await expect( + deriveSchemeAndToken({ authString: 'bEaReR 123' }) + ).resolves.toStrictEqual(expected1); + + await expect( + deriveSchemeAndToken({ authString: 'BeaRer 123' }) + ).resolves.toStrictEqual(expected1); + + await expect( + deriveSchemeAndToken({ authString: 'BEARER 123' }) + ).resolves.toStrictEqual(expected1); + + const expected2 = await deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: '123' } } + }); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: '123' } } + }) + ).resolves.toStrictEqual(expected2); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: '123' } } + }) + ).resolves.toStrictEqual(expected2); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: '123' } } + }) + ).resolves.toStrictEqual(expected2); + }); + + it('handles bearer scheme with token', async () => { + expect.hasAssertions(); + + await expect( + deriveSchemeAndToken({ authString: 'bearer abc-123' }) + ).resolves.toStrictEqual({ + scheme: 'bearer', + token: { bearer: 'abc-123' } + }); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: 'abc-123' } } + }) + ).resolves.toStrictEqual({ + scheme: 'bearer', + token: { bearer: 'abc-123' } + }); + }); + + it('handles allowedSchemes as an AuthScheme or an array of AuthSchemes', async () => { + expect.hasAssertions(); + + await expect( + deriveSchemeAndToken({ authString: 'bearer abc-123', allowedSchemes: 'bearer' }) + ).resolves.toStrictEqual({ + scheme: 'bearer', + token: { bearer: 'abc-123' } + }); + + await expect( + deriveSchemeAndToken({ + authData: { + scheme: 'bearer', + token: { bearer: 'abc-123' } + }, + allowedSchemes: ['bearer'] + }) + ).resolves.toStrictEqual({ + scheme: 'bearer', + token: { bearer: 'abc-123' } + }); + + // ? Unlike with authorizeHeader and its constraints, duplicate AuthSchemes + // ? are not a big deal here and so are not checked against. + await expect( + deriveSchemeAndToken({ + authData: { + scheme: 'bearer', + token: { bearer: 'abc-123' } + }, + allowedSchemes: ['bearer', 'bearer'] + }) + ).resolves.toStrictEqual({ + scheme: 'bearer', + token: { bearer: 'abc-123' } + }); + }); + + it('rejects bearer scheme with multipart token', async () => { + expect.hasAssertions(); + + await expect( + deriveSchemeAndToken({ + authString: 'bearer abc-123,\ndef-234,ghi-345,\n\njkl-456,mno-567' + }) + ).rejects.toThrow('invalid token syntax'); + + await expect( + deriveSchemeAndToken({ + authData: { + scheme: 'bearer', + token: { bearer: ['abc-123', 'def-234', 'ghi-345', 'jkl-456', 'mno-567'] } + } + }) + ).rejects.toThrow('invalid token syntax'); + }); + + it('rejects on missing and null data', async () => { + expect.hasAssertions(); + + await expect(deriveSchemeAndToken({ authString: '' })).rejects.toThrow( + 'invalid auth string' + ); + + await expect(deriveSchemeAndToken({ authString: undefined })).rejects.toThrow( + 'invalid invocation' + ); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await expect(deriveSchemeAndToken({ authString: null as any })).rejects.toThrow( + 'invalid auth string' + ); + + await expect( + deriveSchemeAndToken({ authData: { scheme: 'bearer', token: { bearer: '' } } }) + ).rejects.toThrow('invalid token syntax'); + + await expect( + deriveSchemeAndToken({ authData: { scheme: 'bearer', token: { bearer: '' } } }) + ).rejects.toThrow('invalid token syntax'); + + await expect( + deriveSchemeAndToken({ authData: { scheme: '', token: { bearer: 'abc-123' } } }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + + await expect( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + deriveSchemeAndToken({ authData: { something: 'else' } as any }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + + await expect(deriveSchemeAndToken({ authData: undefined })).rejects.toThrow( + 'invalid invocation' + ); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await expect(deriveSchemeAndToken({ authData: null as any })).rejects.toThrow( + 'invalid auth data' + ); + }); + + it('rejects on badly formatted headers', async () => { + expect.hasAssertions(); + + await expect(deriveSchemeAndToken({ authString: 'bearer' })).rejects.toThrow( + 'invalid auth string' + ); + + await expect( + deriveSchemeAndToken({ authString: 'bearer-bearer' }) + ).rejects.toThrow('invalid auth string'); + }); + + it('rejects on unknown schemes', async () => { + expect.hasAssertions(); + + await expect( + deriveSchemeAndToken({ authString: 'unknown xyz', allowedSchemes: 'bearer' }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'unknown', token: { bearer: 'xyz' } }, + allowedSchemes: 'bearer' + }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + }); + + it('rejects if using a disallowed scheme', async () => { + expect.hasAssertions(); + + await expect( + deriveSchemeAndToken({ + authString: 'bearer 123', + allowedSchemes: ['none' as unknown as AuthScheme] + }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + + await expect( + deriveSchemeAndToken({ + authData: { scheme: 'bearer', token: { bearer: '123' } }, + allowedSchemes: 'none' as unknown as AuthScheme + }) + ).rejects.toThrow('invalid scheme (disallowed or unknown)'); + }); + + it('rejects if handler for scheme is mistakenly unimplemented', async () => { + expect.hasAssertions(); + + mutableAuthSchemes.push('none'); + + await expect(deriveSchemeAndToken({ authString: 'none 123' })).rejects.toThrow( + 'auth string handler for scheme "none" is not implemented' + ); + + await expect( + deriveSchemeAndToken({ authData: { scheme: 'none', token: { bearer: '123' } } }) + ).rejects.toThrow('auth data handler for scheme "none" is not implemented'); + }); +}); + +describe('::authenticateHeader', () => { + it('returns an authenticated response if bearer token exists in database', async () => { + expect.hasAssertions(); + + await expect( + authenticateHeader({ + header: `bearer ${DUMMY_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authenticated: true }); + + await expect( + authenticateHeader({ + header: `BEARER ${DEV_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authenticated: true }); + }); + + // ? Rejecting banned tokens is handled at a different layer than validation + it('returns an authenticated response even if bearer token is banned', async () => { + expect.hasAssertions(); + + await expect( + authenticateHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authenticated: true }); + }); + + it('returns a not-authenticated response if bearer token does not exist in database', async () => { + expect.hasAssertions(); + + await expect( + authenticateHeader({ + header: `bearer ${NULL_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authenticated: false }); + }); + + it('returns a not-authenticated response with an "error" prop if using a disallowed scheme', async () => { + expect.hasAssertions(); + + await expect( + authenticateHeader({ + header: 'bearer 123', + allowedSchemes: ['none' as unknown as AuthScheme] + }) + ).resolves.toStrictEqual({ + authenticated: false, + error: expect.stringContaining('(disallowed or unknown)') + }); + }); +}); + +describe('::authorizeHeader', () => { + it('returns a vacuously authorized response if bearer token exists in database', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${DUMMY_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authorized: true }); + + await expect( + authorizeHeader({ + header: `BEARER ${DEV_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authorized: true }); + }); + + // ? Rejecting banned tokens is handled at a different layer than authorization + it('returns a vacuously authorized response even if bearer token is banned', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ authorized: true }); + }); + + it('returns a vacuously authorized response if passed no constraints', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + constraints: [] + }) + ).resolves.toStrictEqual({ authorized: true }); + }); + + it('returns a not-authorized response with an "error" prop if bearer token does not exist in database', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${NULL_BEARER_TOKEN}` + }) + ).resolves.toStrictEqual({ + authorized: false, + error: + 'bad Authorization header: invalid authentication scheme and token combination' + }); + }); + + it('returns a not-authorized response with an "error" prop only if the isGlobalAdmin constraint fails', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ + authorized: false, + error: 'failed to satisfy authorization constraint "isGlobalAdmin"' + }); + + await expect( + authorizeHeader({ + header: `bearer ${DEV_BEARER_TOKEN}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ + authorized: true + }); + }); + + it('rejects if duplicate constraints provided', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + constraints: ['isGlobalAdmin', 'isGlobalAdmin'] + }) + ).rejects.toMatchObject({ + message: expect.stringContaining( + 'encountered duplicate authorization constraints' + ) + }); + }); + + it('rejects if a non-existent constraint is provided', async () => { + expect.hasAssertions(); + + await expect( + authorizeHeader({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + constraints: ['fake-constraint' as AuthConstraint] + }) + ).rejects.toMatchObject({ + message: expect.stringContaining( + 'encountered unknown or unhandled authorization constraint "fake-constraint"' + ) + }); + }); +}); + +describe('::getAttributes', () => { + it('returns attributes if bearer token exists in database', async () => { + expect.hasAssertions(); + + await expect( + getAttributes({ + target: { scheme: 'bearer', token: { bearer: DUMMY_BEARER_TOKEN } } + }) + ).resolves.toStrictEqual(dummyRootData.auth[1].attributes); + + await expect( + getAttributes({ + target: { scheme: 'bearer', token: { bearer: DEV_BEARER_TOKEN } } + }) + ).resolves.toStrictEqual(dummyRootData.auth[0].attributes); + }); + + // ? Rejecting banned tokens is handled at a different layer than validation + it('returns attributes even if bearer token is banned', async () => { + expect.hasAssertions(); + + await expect( + getAttributes({ + target: { scheme: 'bearer', token: { bearer: BANNED_BEARER_TOKEN } } + }) + ).resolves.toStrictEqual(dummyRootData.auth[2].attributes); + }); + + it('throws if bearer token does not exist in database', async () => { + expect.hasAssertions(); + + await expect( + getAttributes({ + target: { scheme: 'bearer', token: { bearer: NULL_BEARER_TOKEN } } + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('scheme and token combination') + }); + }); +}); + +describe('::updateAttributes', () => { + it('updates (patches) an existing auth entry', async () => { + expect.hasAssertions(); + + const authDb = (await getDb({ name: 'root' })).collection( + 'auth' + ); + + await expect( + updateAttributes({ + target: { + scheme: 'bearer', + token: { bearer: dummyRootData.auth[0].token.bearer } + }, + attributes: { isGlobalAdmin: false } + }) + ).resolves.toBeUndefined(); + + await expect( + authDb.findOne({ _id: dummyRootData.auth[0]._id }) + ).resolves.toStrictEqual({ + ...dummyRootData.auth[0], + attributes: { ...dummyRootData.auth[0].attributes, isGlobalAdmin: false } + }); + + await expect( + updateAttributes({ + target: { + scheme: 'bearer', + token: { bearer: dummyRootData.auth[1].token.bearer } + }, + attributes: { owner: 'name' } + }) + ).resolves.toBeUndefined(); + + await expect( + authDb.findOne({ _id: dummyRootData.auth[1]._id }) + ).resolves.toStrictEqual({ + ...dummyRootData.auth[1], + attributes: { ...dummyRootData.auth[1].attributes, owner: 'name' } + }); + + await expect( + updateAttributes({ + target: { + scheme: 'bearer', + token: { bearer: dummyRootData.auth[0].token.bearer } + }, + attributes: { owner: 'name', isGlobalAdmin: true } + }) + ).resolves.toBeUndefined(); + + await expect( + authDb.findOne({ _id: dummyRootData.auth[0]._id }) + ).resolves.toStrictEqual({ + ...dummyRootData.auth[0], + attributes: { + ...dummyRootData.auth[0].attributes, + owner: 'name', + isGlobalAdmin: true + } + }); + + await expect( + updateAttributes({ + target: { + scheme: 'bearer', + token: { bearer: dummyRootData.auth[1].token.bearer } + }, + attributes: { isGlobalAdmin: true } + }) + ).resolves.toBeUndefined(); + + await expect( + authDb.findOne({ _id: dummyRootData.auth[1]._id }) + ).resolves.toStrictEqual({ + ...dummyRootData.auth[1], + attributes: { + ...dummyRootData.auth[1].attributes, + owner: 'name', + isGlobalAdmin: true + } + }); + }); + + it('allows empty data (no-op)', async () => { + expect.hasAssertions(); + + await expect( + updateAttributes({ + target: { scheme: 'bearer', token: { bearer: DEV_BEARER_TOKEN } }, + attributes: {} + }) + ).resolves.toBeUndefined(); + }); + + it('does not reject when demonstrating idempotency', async () => { + expect.hasAssertions(); + + await expect( + updateAttributes({ + target: { scheme: 'bearer', token: { bearer: DEV_BEARER_TOKEN } }, + attributes: dummyRootData.auth[0].attributes + }) + ).resolves.toBeUndefined(); + }); + + it('rejects if the auth entry is not found', async () => { + expect.hasAssertions(); + + await expect( + updateAttributes({ + target: { scheme: 'bearer', token: { bearer: NULL_BEARER_TOKEN } }, + attributes: { isGlobalAdmin: false } + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('authentication scheme and token combination') + }); + }); + + it('rejects if passed invalid data', async () => { + expect.hasAssertions(); + + const errors: [ + params: Partial[0]>, + error: string + ][] = [ + [{ target: undefined }, 'invalid invocation'], + [{ target: null as unknown as TargetToken }, 'invalid auth data'], + [{ target: false as unknown as TargetToken }, 'invalid auth data'], + [{ target: true as unknown as TargetToken }, 'invalid auth data'], + [{ target: {} }, 'invalid scheme'], + [{ target: { scheme: '' } }, 'invalid scheme'], + [{ target: { scheme: 'bearer', token: {} } }, 'token syntax'], + [{ target: { scheme: 'bearer', token: { fake: 1 } } }, 'token syntax'], + [{ target: { scheme: 'bearer', token: { bearer: null } } }, 'token syntax'], + [{}, 'invalid attributes'], + [{ attributes: undefined }, 'invalid attributes'], + [{ attributes: null as unknown as TokenAttributes }, 'invalid attributes'], + [{ attributes: false as unknown as TokenAttributes }, 'invalid attributes'], + [{ attributes: true as unknown as TokenAttributes }, 'invalid attributes'], + [ + { attributes: { isGlobalAdmin: null } as unknown as TokenAttributes }, + 'invalid attributes' + ], + [ + { attributes: { isGlobalAdmin: 1 } as unknown as TokenAttributes }, + 'invalid attributes' + ], + [ + { attributes: { name: 'owner' } as unknown as TokenAttributes }, + 'invalid attributes' + ], + [ + { attributes: { owner: null } as unknown as TokenAttributes }, + 'invalid attributes' + ], + [ + { + attributes: { + owner: 'name', + isGlobalAdmin: 1 + } as unknown as TokenAttributes + }, + 'invalid attributes' + ], + [ + { + attributes: { + owner: 'name', + isGlobalAdmin: null + } as unknown as TokenAttributes + }, + 'invalid attributes' + ], + [ + { + attributes: { + owner: 'name', + isGlobalAdmin: 'true' + } as unknown as TokenAttributes + }, + 'invalid attributes' + ], + [ + { + attributes: { + owner: 'name', + extra: 1 + } as unknown as TokenAttributes + }, + 'invalid attributes' + ] + ]; + + await Promise.all( + errors.map(async ([params, error]) => { + await expect( + updateAttributes({ + target: { scheme: 'bearer', token: { bearer: DEV_BEARER_TOKEN } }, + ...params + }) + ).rejects.toMatchObject({ message: expect.stringContaining(error) }); + }) + ); + }); +}); + +describe('::getOwnersEntries', () => { + it('returns array of all auth entries owned by the targets', async () => { + expect.hasAssertions(); + + const owners = [ + dummyRootData.auth[0].attributes.owner, + dummyRootData.auth[1].attributes.owner + ]; + + const newAuthEntry1: InternalAuthEntry = { + _id: new ObjectId(), + attributes: { owner: owners[0] }, + scheme: 'bearer', + token: { bearer: jest.requireActual('node:crypto').randomUUID() } + }; + + const newAuthEntry2: InternalAuthEntry = { + _id: new ObjectId(), + attributes: { owner: owners[1] }, + scheme: 'bearer', + token: { bearer: jest.requireActual('node:crypto').randomUUID() } + }; + + await expect(getOwnersEntries({ owners })).resolves.toIncludeSameMembers([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(dummyRootData.auth[1]) + ]); + + await (await getDb({ name: 'root' })) + .collection('auth') + .insertMany([newAuthEntry1, newAuthEntry2]); + + await expect(getOwnersEntries({ owners })).resolves.toIncludeSameMembers([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(dummyRootData.auth[1]), + toPublicAuthEntry(newAuthEntry1), + toPublicAuthEntry(newAuthEntry2) + ]); + + await expect( + getOwnersEntries({ owners: [...owners, undefined] }) + ).resolves.toIncludeSameMembers([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(dummyRootData.auth[1]), + toPublicAuthEntry(newAuthEntry1), + toPublicAuthEntry(newAuthEntry2) + ]); + }); + + it('returns empty array if target owners do not exist', async () => { + expect.hasAssertions(); + + await expect( + getOwnersEntries({ owners: ['does-not-exist'] }) + ).resolves.toStrictEqual([]); + + await expect( + getOwnersEntries({ owners: ['does-not-exist-1', 'does-not-exist-2'] }) + ).resolves.toStrictEqual([]); + }); + + it('returns all auth entries if no owners specified', async () => { + expect.hasAssertions(); + + await expect(getOwnersEntries({ owners: [] })).resolves.toStrictEqual( + dummyRootData.auth.map(toPublicAuthEntry) + ); + + await expect(getOwnersEntries({ owners: [undefined] })).resolves.toStrictEqual( + dummyRootData.auth.map(toPublicAuthEntry) + ); + + await expect( + getOwnersEntries({ owners: [undefined, undefined] }) + ).resolves.toStrictEqual(dummyRootData.auth.map(toPublicAuthEntry)); + }); + + it('rejects if passed invalid data', async () => { + expect.hasAssertions(); + + type Params = Parameters[0]; + + const errors: [params: Params, error: string][] = [ + [{ owners: [false] as unknown as Params['owners'] }, 'invalid owner(s)'], + [{ owners: [true] as unknown as Params['owners'] }, 'invalid owner(s)'], + [{ owners: [''] }, 'invalid owner(s)'], + [{ owners: [5] as unknown as Params['owners'] }, 'invalid owner(s)'], + [{ owners: [null] as unknown as Params['owners'] }, 'invalid owner(s)'] + ]; + + await Promise.all( + errors.map(async ([params, error]) => { + await expect(getOwnersEntries(params as Params)).rejects.toMatchObject({ + message: expect.stringContaining(error) + }); + }) + ); + }); +}); + +describe('::createEntry', () => { + it('creates an auth entry and returns the new token', async () => { + expect.hasAssertions(); + + const crypto = jest.requireActual('node:crypto'); + const newToken1 = crypto.randomUUID(); + const newToken2 = crypto.randomUUID(); + + mockRandomUUID.mockReturnValueOnce(newToken1); + mockRandomUUID.mockReturnValueOnce(newToken2); + + const authDb = (await getDb({ name: 'root' })).collection( + 'auth' + ); + + await expect( + authDb.countDocuments({ 'attributes.owner': 'new-owner' }) + ).resolves.toBe(0); + + await expect( + createEntry({ entry: { attributes: { owner: 'new-owner' } } }) + ).resolves.toStrictEqual({ + attributes: { owner: 'new-owner' }, + scheme: 'bearer', + token: { bearer: newToken1 } + }); + + await expect( + authDb.countDocuments({ + attributes: { owner: 'new-owner' }, + scheme: 'bearer', + token: { bearer: newToken1 } + }) + ).resolves.toBe(1); + + await expect( + createEntry({ + entry: { attributes: { owner: 'new-owner', isGlobalAdmin: true } } + }) + ).resolves.toStrictEqual({ + attributes: { owner: 'new-owner', isGlobalAdmin: true }, + scheme: 'bearer', + token: { bearer: newToken2 } + }); + + await expect( + authDb.countDocuments({ + attributes: { owner: 'new-owner', isGlobalAdmin: true }, + scheme: 'bearer', + token: { bearer: newToken2 } + }) + ).resolves.toBe(1); + + await expect( + authDb.countDocuments({ 'attributes.owner': 'new-owner' }) + ).resolves.toBe(2); + }); + + it('rejects if a duplicate token is accidentally generated', async () => { + expect.hasAssertions(); + + await expect( + createEntry({ entry: { attributes: { owner: 'new-owner' } } }) + ).rejects.toMatchObject({ + message: expect.stringContaining('token collision') + }); + }); + + it('rejects if passed invalid data', async () => { + expect.hasAssertions(); + + const errors: [ + params: Partial[0]>, + error: string + ][] = [ + [{}, 'invalid entry data'], + [{ entry: { attributes: undefined } }, 'invalid entry data'], + [ + { entry: { attributes: null as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { entry: { attributes: false as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { entry: { attributes: true as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { entry: { attributes: {} as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { + entry: { attributes: { isGlobalAdmin: null } as unknown as TokenAttributes } + }, + 'invalid entry data' + ], + [ + { entry: { attributes: { isGlobalAdmin: 1 } as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { + entry: { attributes: { isGlobalAdmin: true } as unknown as TokenAttributes } + }, + 'invalid entry data' + ], + [ + { entry: { attributes: { name: 'owner' } as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { entry: { attributes: { owner: null } as unknown as TokenAttributes } }, + 'invalid entry data' + ], + [ + { + entry: { + attributes: { + owner: 'name', + isGlobalAdmin: 1 + } as unknown as TokenAttributes + } + }, + 'invalid entry data' + ], + [ + { + entry: { + attributes: { + owner: 'name', + isGlobalAdmin: null + } as unknown as TokenAttributes + } + }, + 'invalid entry data' + ], + [ + { + entry: { + attributes: { + owner: 'name', + isGlobalAdmin: 'true' + } as unknown as TokenAttributes + } + }, + 'invalid entry data' + ], + [ + { + entry: { + attributes: { + owner: 'name', + extra: 1 + } as unknown as TokenAttributes + } + }, + 'invalid entry data' + ] + ]; + + await Promise.all( + errors.map(async ([params, error]) => { + await expect(createEntry(params)).rejects.toMatchObject({ + message: expect.stringContaining(error) + }); + }) + ); + }); +}); + +describe('::deleteEntry', () => { + it('deletes an auth entry', async () => { + expect.hasAssertions(); + + const authDb = (await getDb({ name: 'root' })).collection('auth'); + + await expect(authDb.countDocuments()).resolves.toBe(dummyRootData.auth.length); + + await expect( + deleteEntry({ + target: { scheme: 'bearer', token: { bearer: DUMMY_BEARER_TOKEN } } + }) + ).resolves.toBeUndefined(); + + await expect(authDb.countDocuments()).resolves.toBe( + dummyRootData.auth.length - 1 + ); + + await expect( + deleteEntry({ + target: { scheme: 'bearer', token: { bearer: DEV_BEARER_TOKEN } } + }) + ).resolves.toBeUndefined(); + + await expect(authDb.countDocuments()).resolves.toBe( + dummyRootData.auth.length - 2 + ); + }); + + it('rejects if the auth entry is not found', async () => { + expect.hasAssertions(); + + await expect( + deleteEntry({ + target: { scheme: 'bearer', token: { bearer: NULL_BEARER_TOKEN } } + }) + ).rejects.toMatchObject({ + message: expect.stringContaining('authentication scheme and token combination') + }); + }); + + it('rejects if passed invalid data', async () => { + expect.hasAssertions(); + + const errors: [ + params: Partial[0]>, + error: string + ][] = [ + [{}, 'invalid invocation'], + [{ target: undefined }, 'invalid invocation'], + [{ target: null as unknown as TargetToken }, 'invalid auth data'], + [{ target: false as unknown as TargetToken }, 'invalid auth data'], + [{ target: true as unknown as TargetToken }, 'invalid auth data'], + [{ target: {} }, 'invalid scheme'], + [{ target: { scheme: '' } }, 'invalid scheme'], + [{ target: { scheme: 'bearer', token: {} } }, 'token syntax'], + [{ target: { scheme: 'bearer', token: { fake: 1 } } }, 'token syntax'], + [{ target: { scheme: 'bearer', token: { bearer: null } } }, 'token syntax'] + ]; + + await Promise.all( + errors.map(async ([params, error]) => { + await expect(deleteEntry(params)).rejects.toMatchObject({ + message: expect.stringContaining(error) + }); + }) + ); + }); +}); + +it('allows multiple different auth entries of various schemes to coexist', async () => { + expect.hasAssertions(); + + mockRandomUUID.mockImplementation(jest.requireActual('node:crypto').randomUUID); + + const uuid = randomUUID(); + const authDb = (await getDb({ name: 'root' })).collection('auth'); + + mutableAuthSchemes.push('new-scheme-1'); + mutableAuthSchemes.push('new-scheme-2'); + + const newEntryRed: WithoutId = { + attributes: { + owner: 'owner-red', + isGlobalAdmin: false, + createdAt: Date.now() + } as TokenAttributes, + scheme: 'new-scheme-1' as AuthScheme, + token: { id1: uuid.slice(0, 32), id2: uuid.slice(32) } + }; + + const newEntryBlue: WithoutId = { + attributes: { owner: 'owner-blue', isGlobalAdmin: true }, + scheme: 'new-scheme-2' as AuthScheme, + token: { + uuid, + salt: uuid.slice(0, 3), + granter: { key: `${uuid.slice(0, 3)}-${uuid}` } + } + }; + + const actual_deriveSchemeAndToken = deriveSchemeAndToken; + + jest + .spyOn(NextAuthSpyTarget, 'deriveSchemeAndToken') + .mockImplementation(async function ({ + authString, + authData + }: { + authString?: string; + authData?: TargetToken; + }): Promise { + let ret: NextAuthSpyTarget.Token | undefined; + + if ( + authString?.startsWith('new-scheme-1') || + authData?.scheme?.startsWith('new-scheme-1') + ) { + ret = { + scheme: 'new-scheme-1' as AuthScheme, + token: { id1: uuid.slice(0, 32), id2: uuid.slice(32) } + }; + } else if ( + authString?.startsWith('new-scheme-2') || + authData?.scheme?.startsWith('new-scheme-2') + ) { + ret = { + scheme: 'new-scheme-2' as AuthScheme, + token: { + uuid, + salt: uuid.slice(0, 3), + granter: { key: `${uuid.slice(0, 3)}-${uuid}` } + } + }; + } else { + // eslint-disable-next-line prefer-rest-params + ret = await actual_deriveSchemeAndToken(arguments[0]); + } + + return Promise.resolve(ret); + } as typeof deriveSchemeAndToken); + + jest.spyOn(NextAuthSpyTarget, 'isTokenAttributes').mockReturnValue(true); + + const newEntry1 = await createEntry({ + entry: { attributes: { owner: 'owner-1' } } + }); + const newEntry2 = await createEntry({ + entry: { attributes: { owner: 'owner-2', isGlobalAdmin: true } } + }); + + // * Pseudo-createEntry calls + await authDb.insertOne(newEntryRed); + await authDb.insertOne(newEntryBlue); + + await expect( + authenticateHeader({ header: `${newEntry1.scheme} ${newEntry1.token.bearer}` }) + ).resolves.toStrictEqual({ authenticated: true }); + + await expect( + authenticateHeader({ header: `${newEntry2.scheme} ${newEntry2.token.bearer}` }) + ).resolves.toStrictEqual({ authenticated: true }); + + await expect( + authenticateHeader({ header: `${newEntryRed.scheme} ${newEntryRed.token.id1}` }) + ).resolves.toStrictEqual({ authenticated: true }); + + await expect( + authenticateHeader({ + header: `${newEntryBlue.scheme} ${newEntryBlue.token.uuid}` + }) + ).resolves.toStrictEqual({ authenticated: true }); + + await expect( + authenticateHeader({ header: `${newEntry1.scheme} ${newEntryBlue.token.uuid}` }) + ).resolves.toStrictEqual({ authenticated: false }); + + await expect( + authorizeHeader({ + header: `${newEntry1.scheme} ${newEntry1.token.bearer}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ authorized: false, error: expect.any(String) }); + + await expect( + authorizeHeader({ + header: `${newEntry2.scheme} ${newEntry2.token.bearer}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ authorized: true }); + + await expect( + authorizeHeader({ + header: `${newEntryRed.scheme} ${newEntryRed.token.id1}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ authorized: false, error: expect.any(String) }); + + await expect( + authorizeHeader({ + header: `${newEntryBlue.scheme} ${newEntryBlue.token.uuid}`, + constraints: 'isGlobalAdmin' + }) + ).resolves.toStrictEqual({ authorized: true }); +}); diff --git a/lib/next-contrived/index.ts b/lib/next-contrived/index.ts new file mode 100644 index 0000000..97c67f2 --- /dev/null +++ b/lib/next-contrived/index.ts @@ -0,0 +1,33 @@ +import { getEnv } from 'multiverse/next-env'; +import { debugFactory } from 'multiverse/debug-extended'; +import { getDb } from 'multiverse/mongo-schema'; + +const debug = debugFactory('next-contrived:isDueForContrivedError'); + +/** + * Returns `true` if a request should be rejected with a pseudo-error. + * + * Note that this is a per-serverless-function request counter and not global + * across all Vercel virtual machines. + */ +export async function isDueForContrivedError() { + const { REQUESTS_PER_CONTRIVED_ERROR: reqPerErr } = getEnv(); + + if (reqPerErr) { + const x = (await getDb({ name: 'root' })).collection('request-log'); + const count = await x.estimatedDocumentCount(); + + debug(`${count}%${reqPerErr} = ${count % reqPerErr}`); + + if (count % reqPerErr == 0) { + debug('determined request is due for contrived error'); + return true; + } + } else { + debug( + `skipped contrived error check (cause: REQUESTS_PER_CONTRIVED_ERROR=${reqPerErr})` + ); + } + + return false; +} diff --git a/lib/next-contrived/package.json b/lib/next-contrived/package.json new file mode 100644 index 0000000..7d1b5b9 --- /dev/null +++ b/lib/next-contrived/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-contrived" +} diff --git a/lib/next-contrived/unit.test.ts b/lib/next-contrived/unit.test.ts new file mode 100644 index 0000000..10c8f69 --- /dev/null +++ b/lib/next-contrived/unit.test.ts @@ -0,0 +1,139 @@ +import { dummyRootData, useMockDateNow } from 'multiverse/mongo-common'; +import { getDb } from 'multiverse/mongo-schema'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { isDueForContrivedError } from 'multiverse/next-contrived'; +import { mockEnvFactory } from 'testverse/setup'; + +setupMemoryServerOverride(); +useMockDateNow(); + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); +const { _id, ...entry } = dummyRootData['request-log'][0]; + +beforeEach(async () => { + await (await getDb({ name: 'root' })).collection('request-log').deleteMany({}); +}); + +describe('::isDueForContrivedError', () => { + it('returns true every 1st call', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '1' } + ); + }); + + it('returns true every 2nd call', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '2' } + ); + }); + + it('returns true every 3rd call', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '3' } + ); + }); + + it('returns true every 4th call', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '4' } + ); + }); + + it('returns true every 5th call', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeTrue(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeTrue(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '5' } + ); + }); + + it('middleware disabled when REQUESTS_PER_CONTRIVED_ERROR=0', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('request-log'); + + await withMockedEnv( + async () => { + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + await db.insertOne({ ...entry }); + await expect(isDueForContrivedError()).resolves.toBeFalse(); + }, + { REQUESTS_PER_CONTRIVED_ERROR: '0' } + ); + }); +}); diff --git a/lib/next-env/index.ts b/lib/next-env/index.ts new file mode 100644 index 0000000..90a148a --- /dev/null +++ b/lib/next-env/index.ts @@ -0,0 +1,183 @@ +import { parse as parseAsBytes } from 'bytes'; +import { isServer } from 'is-server-side'; +import { InvalidAppEnvironmentError } from 'named-app-errors'; +import { toss } from 'toss-expression'; +import { validHttpMethods } from '@xunnamius/types'; +import { debugFactory } from 'multiverse/debug-extended'; + +import type { ValidHttpMethod } from '@xunnamius/types'; +import type { Primitive } from 'type-fest'; + +const debug = debugFactory('next-env:env'); + +// * NOTE: next-env does not invoke dotenv or load any .env files for you, +// * you'll have to do that manually. For Next.js apps, this is the desired +// * behavior since environment variables are defined as secrets. Further note +// * that Webpack and Jest configurations are setup to load .env files for you. + +/** + * This method takes an environment variable value (string), removes illegal + * characters, and then splits the string by its commas, returning the resulting + * array with all nullish members filtered out. + */ +export const envToArray = (envVal: string) => { + return envVal + .replace(/[^A-Za-z0-9=.<>,-^~_*]+/g, '') + .split(',') + .filter(Boolean); +}; + +export type Environment = Record; + +type OverrideEnvExpect = 'force-check' | 'force-no-check' | undefined; + +/** + * Returns an object representing the current runtime environment. + */ +export function getEnv(customizedEnv?: T) { + debug( + `environment definitions (resolved as NODE_ENV) listed in order of precedence:` + ); + debug(`APP_ENV: ${process.env.APP_ENV ?? '(undefined)'}`); + debug(`NODE_ENV: ${process.env.NODE_ENV ?? '(undefined)'}`); + debug(`BABEL_ENV: ${process.env.BABEL_ENV ?? '(undefined)'}`); + + const env = { + OVERRIDE_EXPECT_ENV: + process.env.OVERRIDE_EXPECT_ENV == 'force-check' || + process.env.OVERRIDE_EXPECT_ENV == 'force-no-check' || + process.env.OVERRIDE_EXPECT_ENV === undefined + ? (process.env.OVERRIDE_EXPECT_ENV as OverrideEnvExpect) + : toss( + new InvalidAppEnvironmentError( + 'OVERRIDE_EXPECT_ENV must have value "force-check", "force-no-check", or undefined' + ) + ), + NODE_ENV: + process.env.APP_ENV || + process.env.NODE_ENV || + process.env.BABEL_ENV || + 'unknown', + MONGODB_URI: process.env.MONGODB_URI || '', + MONGODB_MS_PORT: !!process.env.MONGODB_MS_PORT + ? Number(process.env.MONGODB_MS_PORT) + : null, + DISABLED_API_VERSIONS: !!process.env.DISABLED_API_VERSIONS + ? envToArray(process.env.DISABLED_API_VERSIONS.toLowerCase()) + : [], + RESULTS_PER_PAGE: Number(process.env.RESULTS_PER_PAGE) || 100, + IGNORE_RATE_LIMITS: + !!process.env.IGNORE_RATE_LIMITS && process.env.IGNORE_RATE_LIMITS !== 'false', + LOCKOUT_ALL_CLIENTS: + !!process.env.LOCKOUT_ALL_CLIENTS && + process.env.LOCKOUT_ALL_CLIENTS !== 'false', + DISALLOWED_METHODS: !!process.env.DISALLOWED_METHODS + ? envToArray(process.env.DISALLOWED_METHODS.toUpperCase()) + : [], + MAX_CONTENT_LENGTH_BYTES: + parseAsBytes(process.env.MAX_CONTENT_LENGTH_BYTES ?? '-Infinity') || 102400, + AUTH_HEADER_MAX_LENGTH: Number(process.env.AUTH_HEADER_MAX_LENGTH) || 500, + DEBUG: process.env.DEBUG ?? null, + DEBUG_INSPECTING: !!process.env.VSCODE_INSPECTOR_OPTIONS, + REQUESTS_PER_CONTRIVED_ERROR: + Number(process.env.REQUESTS_PER_CONTRIVED_ERROR) || 0, + + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: !!process.env + .BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS + ? Number(process.env.BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS) + : null, + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: !!process.env + .BAN_HAMMER_MAX_REQUESTS_PER_WINDOW + ? Number(process.env.BAN_HAMMER_MAX_REQUESTS_PER_WINDOW) + : null, + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: !!process.env + .BAN_HAMMER_RESOLUTION_WINDOW_SECONDS + ? Number(process.env.BAN_HAMMER_RESOLUTION_WINDOW_SECONDS) + : null, + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: !!process.env + .BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES + ? Number(process.env.BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES) + : null, + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: !!process.env + .BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER + ? Number(process.env.BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER) + : null, + + PRUNE_DATA_MAX_LOGS_BYTES: + parseAsBytes(process.env.PRUNE_DATA_MAX_LOGS_BYTES ?? '-Infinity') || null, + PRUNE_DATA_MAX_BANNED_BYTES: + parseAsBytes(process.env.PRUNE_DATA_MAX_BANNED_BYTES ?? '-Infinity') || null, + + ...customizedEnv + }; + + debug('resolved env vars:'); + debug(env); + + // TODO: when the following logic is retired, consider renaming this package + // TODO: to `@xunnamius/env` or something similar since it's not next-specific + + // TODO: when in production, perhaps these checks should only be run once? + // TODO: Maybe this entire module should be cached? How does that work with + // TODO: downstream getEnv decorators (like `universe/env`)? + + // TODO: retire all of the following logic when expect-env is created. Also, + // TODO: expect-env should have the ability to skip runs on certain NODE_ENV + // TODO: unless OVERRIDE_EXPECT_ENV is properly defined. + /* istanbul ignore next */ + if ( + (env.NODE_ENV != 'test' && env.OVERRIDE_EXPECT_ENV != 'force-no-check') || + env.OVERRIDE_EXPECT_ENV == 'force-check' + ) { + const errors = []; + const envIsGtZero = (name: keyof typeof env) => { + if ( + typeof env[name] != 'number' || + isNaN(env[name] as number) || + (env[name] as number) < 0 + ) { + errors.push( + `bad ${name}, saw "${env[name]}" (expected a non-negative number)` + ); + } + }; + + if (env.NODE_ENV == 'unknown') errors.push(`bad NODE_ENV, saw "${env.NODE_ENV}"`); + + // TODO: expect-env should cover this use-case (server-only) as well. + if (isServer()) { + if (env.MONGODB_URI === '') + errors.push(`bad MONGODB_URI, saw "${env.MONGODB_URI}"`); + + ( + [ + 'RESULTS_PER_PAGE', + 'MAX_CONTENT_LENGTH_BYTES', + 'AUTH_HEADER_MAX_LENGTH' + ] as (keyof typeof env)[] + ).forEach((name) => envIsGtZero(name)); + + env.DISALLOWED_METHODS.forEach((method) => { + if (!validHttpMethods.includes(method as ValidHttpMethod)) { + errors.push( + `unknown method "${method}", must be one of: ${validHttpMethods.join( + ', ' + )}` + ); + } + }); + + if (env.MONGODB_MS_PORT && env.MONGODB_MS_PORT <= 1024) { + errors.push(`optional environment variable MONGODB_MS_PORT must be > 1024`); + } + } + + if (errors.length) { + throw new InvalidAppEnvironmentError( + `bad variables:\n - ${errors.join('\n - ')}` + ); + } + } + + return env as typeof env & T; +} diff --git a/lib/next-env/package.json b/lib/next-env/package.json new file mode 100644 index 0000000..cf3f12a --- /dev/null +++ b/lib/next-env/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-env" +} diff --git a/lib/next-env/unit.test.ts b/lib/next-env/unit.test.ts new file mode 100644 index 0000000..278a37d --- /dev/null +++ b/lib/next-env/unit.test.ts @@ -0,0 +1,178 @@ +import { getEnv } from 'multiverse/next-env'; +import { withMockedEnv } from 'testverse/setup'; + +describe('::getEnv', () => { + it('returns object with respect to process.env', async () => { + expect.hasAssertions(); + + await withMockedEnv( + () => { + expect(getEnv()).toStrictEqual({ + OVERRIDE_EXPECT_ENV: undefined, + NODE_ENV: 'known', + MONGODB_URI: 'uri', + MONGODB_MS_PORT: null, + DISABLED_API_VERSIONS: [], + RESULTS_PER_PAGE: 5, + IGNORE_RATE_LIMITS: false, + LOCKOUT_ALL_CLIENTS: false, + DISALLOWED_METHODS: [], + MAX_CONTENT_LENGTH_BYTES: 1024, + AUTH_HEADER_MAX_LENGTH: 500, + DEBUG: null, + DEBUG_INSPECTING: false, + REQUESTS_PER_CONTRIVED_ERROR: 0, + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: null, + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: null, + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: null, + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: null, + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: null, + PRUNE_DATA_MAX_LOGS_BYTES: null, + PRUNE_DATA_MAX_BANNED_BYTES: null + }); + }, + { + BABEL_ENV: 'known', + MONGODB_URI: 'uri', + RESULTS_PER_PAGE: '5', + MAX_CONTENT_LENGTH_BYTES: '1KB' + } + ); + + // TODO: retire this test and/or merge it into expect-env + await withMockedEnv(() => { + expect(() => getEnv()).toThrow(`bad variables: + - bad NODE_ENV, saw "unknown" + - bad MONGODB_URI, saw ""`); + }, {}); + }); + + // TODO: retire the next two checks and fold them into expect-env instead + + it('does not run expect-env if NODE_ENV is not "test"', async () => { + expect.hasAssertions(); + + await withMockedEnv( + () => { + expect(getEnv()).toStrictEqual({ + OVERRIDE_EXPECT_ENV: undefined, + NODE_ENV: 'test', + MONGODB_URI: 'uri', + MONGODB_MS_PORT: 1234, + DISABLED_API_VERSIONS: ['one', '2', 'three'], + RESULTS_PER_PAGE: 5, + IGNORE_RATE_LIMITS: false, + LOCKOUT_ALL_CLIENTS: true, + DISALLOWED_METHODS: ['FAKE'], + MAX_CONTENT_LENGTH_BYTES: 1024, + AUTH_HEADER_MAX_LENGTH: 50, + DEBUG: 'false', + DEBUG_INSPECTING: true, + REQUESTS_PER_CONTRIVED_ERROR: 5, + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: 10, + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: 15, + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: 20, + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: 25, + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: 30, + PRUNE_DATA_MAX_LOGS_BYTES: 35, + PRUNE_DATA_MAX_BANNED_BYTES: 1024 + }); + }, + { + NODE_ENV: 'test', + MONGODB_URI: 'uri', + MONGODB_MS_PORT: '1234', + DISABLED_API_VERSIONS: 'one, 2, three', + RESULTS_PER_PAGE: '5', + IGNORE_RATE_LIMITS: 'false', + LOCKOUT_ALL_CLIENTS: 'true', + DISALLOWED_METHODS: 'FAKE', + MAX_CONTENT_LENGTH_BYTES: '1KB', + AUTH_HEADER_MAX_LENGTH: '50', + DEBUG: 'false', + VSCODE_INSPECTOR_OPTIONS: 'inspector', + REQUESTS_PER_CONTRIVED_ERROR: '5', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '10', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '15', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '20', + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '25', + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '30', + PRUNE_DATA_MAX_LOGS_BYTES: '35', + PRUNE_DATA_MAX_BANNED_BYTES: '1kb' + } + ); + }); + + it('respects OVERRIDE_EXPECT_ENV', async () => { + expect.hasAssertions(); + + await withMockedEnv( + () => { + expect(() => getEnv()).toThrow(/bad/); + }, + { + OVERRIDE_EXPECT_ENV: 'force-check', + NODE_ENV: 'test', + MONGODB_URI: 'uri', + MONGODB_MS_PORT: '1234', + DISABLED_API_VERSIONS: 'one, 2, three', + RESULTS_PER_PAGE: '5', + IGNORE_RATE_LIMITS: 'false', + LOCKOUT_ALL_CLIENTS: 'true', + DISALLOWED_METHODS: 'FAKE', + MAX_CONTENT_LENGTH_BYTES: '1KB', + AUTH_HEADER_MAX_LENGTH: '50', + DEBUG: 'false', + VSCODE_INSPECTOR_OPTIONS: 'inspector', + REQUESTS_PER_CONTRIVED_ERROR: '5', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '10', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '15', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '20', + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '25', + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '30', + PRUNE_DATA_MAX_LOGS_BYTES: '35b', + PRUNE_DATA_MAX_BANNED_BYTES: '40b' + } + ); + + await withMockedEnv( + () => { + expect(() => getEnv()).not.toThrow(); + }, + { + OVERRIDE_EXPECT_ENV: 'force-no-check', + NODE_ENV: 'test', + MONGODB_URI: 'uri', + MONGODB_MS_PORT: '1234', + DISABLED_API_VERSIONS: 'one, 2, three', + RESULTS_PER_PAGE: '5', + IGNORE_RATE_LIMITS: 'false', + LOCKOUT_ALL_CLIENTS: 'true', + DISALLOWED_METHODS: 'FAKE', + MAX_CONTENT_LENGTH_BYTES: '1KB', + AUTH_HEADER_MAX_LENGTH: '50', + DEBUG: 'false', + VSCODE_INSPECTOR_OPTIONS: 'inspector', + REQUESTS_PER_CONTRIVED_ERROR: '5', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '10', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '15', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '20', + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '25', + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '30', + PRUNE_DATA_MAX_LOGS_BYTES: '35b', + PRUNE_DATA_MAX_BANNED_BYTES: '40b' + } + ); + }); + + it('throws on invalid OVERRIDE_EXPECT_ENV', async () => { + expect.hasAssertions(); + + await withMockedEnv( + () => { + expect(() => getEnv()).toThrow(/must have value "force-check"/); + }, + { OVERRIDE_EXPECT_ENV: '' } + ); + }); +}); diff --git a/lib/next-limit/index.ts b/lib/next-limit/index.ts new file mode 100644 index 0000000..fa8ed6e --- /dev/null +++ b/lib/next-limit/index.ts @@ -0,0 +1,115 @@ +import { getDb } from 'multiverse/mongo-schema'; +import { getEnv } from 'multiverse/next-env'; +import { getClientIp } from 'request-ip'; +import { ValidationError } from 'universe/error'; + +import type { NextApiRequest } from 'next'; +import type { UnixEpochMs } from '@xunnamius/types'; +import type { UpdateResult, WithId, WithoutId } from 'mongodb'; + +/** + * The shape of an entry in the well-known "limited log" collection. + */ +export type InternalLimitedLogEntry = WithId< + | { + until: UnixEpochMs; + ip: string; + header?: never; + } + | { + until: UnixEpochMs; + ip?: never; + header: string; + } +>; + +/** + * The shape of a new entry in the well-known "limited log" collection. + */ +export type NewLimitedLogEntry = WithoutId; + +/** + * Returns an object with two keys: `isLimited` and `retryAfter`. If `isLimited` + * is true, then the request should be rejected. The client should be instructed + * to retry their request after `retryAfter` milliseconds have passed. + */ +export async function clientIsRateLimited(req: NextApiRequest) { + const ip = getClientIp(req); + const header = req.headers.authorization + ?.slice(0, getEnv().AUTH_HEADER_MAX_LENGTH) + .toLowerCase(); + + const limited = await ( + await getDb({ name: 'root' }) + ) + .collection('limited-log') + .find({ + $or: [...(ip ? [{ ip }] : []), ...(header ? [{ header }] : [])], + until: { $gt: Date.now() } // ? Skip the recently unbanned + }) + .sort({ until: -1 }) + .limit(1) + .next(); + + return { + isLimited: !!limited, + retryAfter: Math.max( + 0, + ((limited?.until as number) || Date.now()) - Date.now() + ) as UnixEpochMs + }; +} + +/** + * Removes a rate limit on a client matched against either `ip`, `header`, or + * both. Matching against both removes rate limits that match either criterion. + * + * @returns The number of rate limits removed. + */ +export async function removeRateLimit({ + target +}: { + target: { ip?: string; header?: string } | undefined; +}) { + if (target) { + const { ip, header } = target; + + if (ip !== undefined || header !== undefined) { + if (ip !== undefined && (typeof ip != 'string' || !ip)) { + throw new ValidationError('ip must be a non-empty string'); + } + + if (header !== undefined && (typeof header != 'string' || !header)) { + throw new ValidationError('header must be a non-empty string'); + } + + const now = Date.now(); + const result = (await (await getDb({ name: 'root' })) + .collection('limited-log') + .updateMany( + { + $or: [...(ip ? [{ ip }] : []), ...(header ? [{ header }] : [])], + until: { $gt: now } // ? Skip the recently unbanned + }, + { $set: { until: now } } + )) as UpdateResult; + + return result.modifiedCount; + } + } + + throw new ValidationError('must provide either an ip or a header'); +} + +/** + * Retrieve all active rate limits. + */ +export async function getAllRateLimits() { + return (await getDb({ name: 'root' })) + .collection('limited-log') + .find>( + { until: { $gt: Date.now() } }, + { sort: { _id: -1 }, projection: { _id: false } } + ) + .toArray(); +} diff --git a/lib/next-limit/package.json b/lib/next-limit/package.json new file mode 100644 index 0000000..9d0ad6f --- /dev/null +++ b/lib/next-limit/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-limit" +} diff --git a/lib/next-limit/unit.test.ts b/lib/next-limit/unit.test.ts new file mode 100644 index 0000000..bc0e804 --- /dev/null +++ b/lib/next-limit/unit.test.ts @@ -0,0 +1,334 @@ +import { dummyRootData, useMockDateNow } from 'multiverse/mongo-common'; +import { getDb } from 'multiverse/mongo-schema'; +import { BANNED_BEARER_TOKEN } from 'multiverse/next-auth'; +import { + clientIsRateLimited, + getAllRateLimits, + removeRateLimit +} from 'multiverse/next-limit'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; + +import type { InternalLimitedLogEntry } from 'multiverse/next-limit'; +import type { NextApiRequest } from 'next'; + +setupMemoryServerOverride(); +useMockDateNow(); + +describe('::clientIsRateLimited', () => { + it('returns true if ip or header (case-insensitive) are rate limited', async () => { + expect.hasAssertions(); + + const req1 = await clientIsRateLimited({ + headers: { 'x-forwarded-for': '1.2.3.4' }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest); + + const req2 = await clientIsRateLimited({ + headers: { + 'x-forwarded-for': '8.8.8.8', + // ? Should work with different cases too + authorization: `BEARER ${BANNED_BEARER_TOKEN}` + }, + method: 'GET', + url: '/api/route/path2' + } as unknown as NextApiRequest); + + const req3 = await clientIsRateLimited({ + headers: { + 'x-forwarded-for': '1.2.3.4', + authorization: 'bearer fake-header' + }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest); + + const req4 = await clientIsRateLimited({ + headers: { + 'x-forwarded-for': '5.6.7.8' + }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest); + + const req5 = await clientIsRateLimited({ + headers: { + 'x-forwarded-for': '1.2.3.4', + authorization: `bearer ${BANNED_BEARER_TOKEN}` + }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest); + + const req6 = await clientIsRateLimited({ + headers: { + // ? Should work with different cases too + authorization: `bEaReR ${BANNED_BEARER_TOKEN}` + }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest); + + expect(req1.isLimited).toBeTrue(); + expect(req2.isLimited).toBeTrue(); + expect(req3.isLimited).toBeTrue(); + expect(req4.isLimited).toBeTrue(); + expect(req5.isLimited).toBeTrue(); + expect(req6.isLimited).toBeTrue(); + + const minToMs = (minutes: number) => 1000 * 60 * minutes; + expect(req1.retryAfter).toBeWithin(minToMs(15) - 1000, minToMs(15) + 1000); + expect(req2.retryAfter).toBeWithin(minToMs(60) - 1000, minToMs(60) + 1000); + expect(req3.retryAfter).toBeWithin(minToMs(15) - 1000, minToMs(15) + 1000); + expect(req4.retryAfter).toBeWithin(minToMs(15) - 1000, minToMs(15) + 1000); + // ? Should return greater of the two ban times (header time > ip time) + expect(req5.retryAfter).toBeWithin(minToMs(60) - 1000, minToMs(60) + 1000); + expect(req6.retryAfter).toBeWithin(minToMs(60) - 1000, minToMs(60) + 1000); + }); + + it('returns false if both ip and header (if provided) are not rate limited', async () => { + expect.hasAssertions(); + const req1 = { + headers: { 'x-forwarded-for': '1.2.3.5' }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest; + + const req2 = { + headers: { + 'x-forwarded-for': '8.8.8.8', + authorization: 'bearer fake-header' + }, + method: 'GET', + url: '/api/route/path2' + } as unknown as NextApiRequest; + + await expect(clientIsRateLimited(req1)).resolves.toStrictEqual({ + isLimited: false, + retryAfter: 0 + }); + await expect(clientIsRateLimited(req2)).resolves.toStrictEqual({ + isLimited: false, + retryAfter: 0 + }); + }); + + it('returns false if "until" time has passed', async () => { + expect.hasAssertions(); + + const req = { + headers: { 'x-forwarded-for': '1.2.3.4' }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest; + + await expect(clientIsRateLimited(req)).resolves.toStrictEqual({ + isLimited: true, + retryAfter: expect.any(Number) + }); + + await (await getDb({ name: 'root' })) + .collection('limited-log') + .updateOne({ ip: '1.2.3.4' }, { $set: { until: Date.now() - 10 ** 5 } }); + + await expect(clientIsRateLimited(req)).resolves.toStrictEqual({ + isLimited: false, + retryAfter: 0 + }); + }); +}); + +describe('::removeRateLimit', () => { + it('removes an active rate limit by ip, header', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + + await expect( + db.countDocuments({ + ip: dummyRootData['limited-log'][0].ip, + until: { $gt: Date.now() } + }) + ).resolves.toBe(1); + + await expect( + removeRateLimit({ target: { ip: dummyRootData['limited-log'][0].ip } }) + ).resolves.toBe(1); + + await expect( + db.countDocuments({ + ip: dummyRootData['limited-log'][0].ip, + until: { $gt: Date.now() } + }) + ).resolves.toBe(0); + + await expect( + db.countDocuments({ + header: dummyRootData['limited-log'][2].header, + until: { $gt: Date.now() } + }) + ).resolves.toBe(1); + + await expect( + removeRateLimit({ target: { header: dummyRootData['limited-log'][2].header } }) + ).resolves.toBe(1); + + await expect( + db.countDocuments({ + header: dummyRootData['limited-log'][2].header, + until: { $gt: Date.now() } + }) + ).resolves.toBe(0); + }); + + it('removes an active rate limit by ip or header (simultaneously)', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + + await expect( + db.countDocuments({ + $or: [ + { ip: dummyRootData['limited-log'][1].ip }, + { header: dummyRootData['limited-log'][2].header } + ], + until: { $gt: Date.now() } + }) + ).resolves.toBe(2); + + await expect( + removeRateLimit({ + target: { + ip: dummyRootData['limited-log'][1].ip, + header: dummyRootData['limited-log'][2].header + } + }) + ).resolves.toBe(2); + + await expect( + db.countDocuments({ + $or: [ + { ip: dummyRootData['limited-log'][1].ip }, + { header: dummyRootData['limited-log'][2].header } + ], + until: { $gt: Date.now() } + }) + ).resolves.toBe(0); + }); + + it('only removes active rate limits', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + + await db.updateOne( + { ip: dummyRootData['limited-log'][1].ip }, + { $set: { until: Date.now() } } + ); + + await expect( + removeRateLimit({ + target: { + ip: dummyRootData['limited-log'][1].ip, + header: dummyRootData['limited-log'][2].header + } + }) + ).resolves.toBe(1); + }); + + it('returns 0 if no active rate limit was found', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + + await db.updateOne( + { ip: dummyRootData['limited-log'][1].ip }, + { $set: { until: Date.now() } } + ); + + await expect( + removeRateLimit({ target: { ip: dummyRootData['limited-log'][1].ip } }) + ).resolves.toBe(0); + }); + + it('rejects if passed invalid data', async () => { + expect.hasAssertions(); + await Promise.all([ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(removeRateLimit({} as any)).rejects.toMatchObject({ + message: 'must provide either an ip or a header' + }), + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(removeRateLimit({ something: 'else' } as any)).rejects.toMatchObject({ + message: 'must provide either an ip or a header' + }), + + expect(removeRateLimit({ target: undefined })).rejects.toMatchObject({ + message: 'must provide either an ip or a header' + }), + + expect(removeRateLimit({ target: {} })).rejects.toMatchObject({ + message: 'must provide either an ip or a header' + }), + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(removeRateLimit({ target: { ip: true } } as any)).rejects.toMatchObject({ + message: 'ip must be a non-empty string' + }), + + expect( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeRateLimit({ target: { header: true } as any }) + ).rejects.toMatchObject({ + message: 'header must be a non-empty string' + }), + + expect( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeRateLimit({ target: { ip: '', header: true } as any }) + ).rejects.toMatchObject({ + message: 'ip must be a non-empty string' + }), + + expect( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeRateLimit({ target: { ip: null, header: '' } as any }) + ).rejects.toMatchObject({ + message: 'ip must be a non-empty string' + }), + + expect( + removeRateLimit({ target: { ip: undefined, header: undefined } }) + ).rejects.toMatchObject({ message: 'must provide either an ip or a header' }), + expect( + removeRateLimit({ target: { ip: '', header: '' } }) + ).rejects.toMatchObject({ + message: 'ip must be a non-empty string' + }) + ]); + }); +}); + +describe('::getAllRateLimits', () => { + it('returns all active rate limits in the system', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + + await expect(getAllRateLimits()).resolves.toIncludeSameMembers( + await db + .find({ until: { $gt: Date.now() } }, { projection: { _id: false } }) + .toArray() + ); + }); + + it('does not crash if database is empty', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'root' })).collection('limited-log'); + await db.deleteMany({}); + + await expect(getAllRateLimits()).resolves.toStrictEqual([]); + }); +}); diff --git a/lib/next-log/index.ts b/lib/next-log/index.ts new file mode 100644 index 0000000..9471e7c --- /dev/null +++ b/lib/next-log/index.ts @@ -0,0 +1,77 @@ +import { getClientIp } from 'request-ip'; + +import { getEnv } from 'multiverse/next-env'; +import { getDb } from 'multiverse/mongo-schema'; + +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { HttpStatusCode, UnixEpochMs } from '@xunnamius/types'; +import type { WithId, WithoutId } from 'mongodb'; + +/** + * The shape of an entry in the well-known "request log" collection. + */ +export type InternalRequestLogEntry = WithId<{ + ip: string | null; + header: string | null; + route: string | null; + endpoint: string | null; + method: string | null; + resStatusCode: HttpStatusCode; + createdAt: UnixEpochMs; + durationMs: number; +}>; + +/** + * The shape of a new entry in the well-known "request log" collection. + */ +export type NewRequestLogEntry = WithoutId; + +/** + * This function adds a request metadata entry to the database. + * + * Note that this async function **does not have to be awaited**. It's fire and + * forget! + * + * @example + * ``` + * doSomeStuff(); + * void addToRequestLog({ req, res, endpoint }); + * doSomeOtherStuff(); + * ``` + */ +export async function addToRequestLog({ + req, + res, + endpoint, + durationMs +}: { + req: NextApiRequest; + res: NextApiResponse; + endpoint: string | null | undefined; + durationMs: number; +}): Promise { + if (!endpoint) { + // eslint-disable-next-line no-console + console.warn( + `${ + req.url ? `API endpoint at ${req.url}` : 'an API endpoint' + } is missing its descriptor metadata` + ); + } + + await (await getDb({ name: 'root' })) + .collection('request-log') + .insertOne({ + ip: getClientIp(req), + header: + req.headers.authorization + ?.slice(0, getEnv().AUTH_HEADER_MAX_LENGTH) + .toLowerCase() || null, + method: req.method?.toUpperCase() || null, + route: req.url || null, + endpoint: endpoint || null, + resStatusCode: res.statusCode as HttpStatusCode, + createdAt: Date.now(), + durationMs + }); +} diff --git a/lib/next-log/package.json b/lib/next-log/package.json new file mode 100644 index 0000000..483df65 --- /dev/null +++ b/lib/next-log/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/next-log" +} diff --git a/lib/next-log/unit.test.ts b/lib/next-log/unit.test.ts new file mode 100644 index 0000000..ef6fc3a --- /dev/null +++ b/lib/next-log/unit.test.ts @@ -0,0 +1,238 @@ +import { useMockDateNow, mockDateNowMs } from 'multiverse/mongo-common'; +import { getDb } from 'multiverse/mongo-schema'; +import { BANNED_BEARER_TOKEN } from 'multiverse/next-auth'; +import { addToRequestLog } from 'multiverse/next-log'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; + +import { withMockedOutput } from 'testverse/setup'; + +import type { InternalRequestLogEntry } from 'multiverse/next-log'; +import type { HttpStatusCode } from '@xunnamius/types'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +setupMemoryServerOverride(); +useMockDateNow(); + +const mockPerfNow = 1234; + +describe('::addToRequestLog', () => { + it('adds request to mongo collection', async () => { + expect.hasAssertions(); + + const req1 = { + headers: { 'x-forwarded-for': '9.9.9.9' }, + method: 'POST', + url: '/api/route/path1' + } as unknown as NextApiRequest; + + const req2 = { + headers: { + 'x-forwarded-for': '8.8.8.8', + authorization: `bearer ${BANNED_BEARER_TOKEN}` + }, + method: 'GET', + url: '/api/route/path2' + } as unknown as NextApiRequest; + + const res1 = { statusCode: 1111 } as NextApiResponse; + const res2 = { statusCode: 2222 } as NextApiResponse; + + await addToRequestLog({ + req: req1, + res: res1, + endpoint: '/fake', + durationMs: 1234 + }); + + await addToRequestLog({ + req: req2, + res: res2, + endpoint: '/fake', + durationMs: 1234 + }); + + const reqlog = ( + await getDb({ name: 'root' }) + ).collection('request-log'); + + await expect( + reqlog.findOne({ resStatusCode: 1111 as HttpStatusCode }) + ).resolves.toStrictEqual({ + _id: expect.anything(), + ip: '9.9.9.9', + header: null, + route: '/api/route/path1', + endpoint: '/fake', + method: 'POST', + createdAt: mockDateNowMs, + resStatusCode: 1111, + durationMs: mockPerfNow + }); + + await expect( + reqlog.findOne({ resStatusCode: 2222 as HttpStatusCode }) + ).resolves.toStrictEqual({ + _id: expect.anything(), + ip: '8.8.8.8', + header: `bearer ${BANNED_BEARER_TOKEN}`, + route: '/api/route/path2', + endpoint: '/fake', + method: 'GET', + createdAt: mockDateNowMs, + resStatusCode: 2222, + durationMs: mockPerfNow + }); + }); + + it('handles null method and/or url and lowercases schema', async () => { + expect.hasAssertions(); + + const req1 = { + headers: { 'x-forwarded-for': '9.9.9.9' }, + method: null, + url: '/api/route/path1' + } as unknown as NextApiRequest; + + const req2 = { + headers: { + 'x-forwarded-for': '8.8.8.8', + authorization: `BeArEr ${BANNED_BEARER_TOKEN}` + }, + method: 'GET', + url: null + } as unknown as NextApiRequest; + + const res1 = { statusCode: 1111 } as NextApiResponse; + const res2 = { statusCode: 2222 } as NextApiResponse; + + await addToRequestLog({ + req: req1, + res: res1, + endpoint: '/fake', + durationMs: 1234 + }); + + await addToRequestLog({ + req: req2, + res: res2, + endpoint: '/fake', + durationMs: 1234 + }); + + const reqlog = ( + await getDb({ name: 'root' }) + ).collection('request-log'); + + await expect( + reqlog.findOne({ resStatusCode: 1111 as HttpStatusCode }) + ).resolves.toStrictEqual({ + _id: expect.anything(), + ip: '9.9.9.9', + header: null, + route: '/api/route/path1', + endpoint: '/fake', + method: null, + createdAt: mockDateNowMs, + resStatusCode: 1111, + durationMs: mockPerfNow + }); + + await expect( + reqlog.findOne({ resStatusCode: 2222 as HttpStatusCode }) + ).resolves.toStrictEqual({ + _id: expect.anything(), + ip: '8.8.8.8', + header: `bearer ${BANNED_BEARER_TOKEN}`, + route: null, + endpoint: '/fake', + method: 'GET', + createdAt: mockDateNowMs, + resStatusCode: 2222, + durationMs: mockPerfNow + }); + }); + + it('handles null or undefined endpoint metadata with warnings', async () => { + expect.hasAssertions(); + + const req1 = { + headers: { 'x-forwarded-for': '9.9.9.9' }, + method: 'GET', + url: '/api/route/path1' + } as unknown as NextApiRequest; + + const req2 = { + headers: { 'x-forwarded-for': '8.8.8.8' }, + method: 'GET', + url: null + } as unknown as NextApiRequest; + + const res1 = { statusCode: 1111 } as NextApiResponse; + const res2 = { statusCode: 2222 } as NextApiResponse; + const res3 = { statusCode: 3333 } as NextApiResponse; + + const reqlog = ( + await getDb({ name: 'root' }) + ).collection('request-log'); + + await withMockedOutput(async ({ warnSpy }) => { + await addToRequestLog({ + req: req1, + res: res1, + endpoint: null, + durationMs: 1234 + }); + + expect(warnSpy).toBeCalledWith( + expect.stringContaining(`API endpoint at ${req1.url}`) + ); + + await expect( + reqlog.findOne({ resStatusCode: 1111 as HttpStatusCode }) + ).resolves.toStrictEqual( + expect.objectContaining({ + endpoint: null + }) + ); + + await addToRequestLog({ + req: req2, + res: res2, + endpoint: undefined, + durationMs: 1234 + }); + + expect(warnSpy).toBeCalledWith(expect.stringContaining('an API endpoint')); + + await expect( + reqlog.findOne({ resStatusCode: 2222 as HttpStatusCode }) + ).resolves.toStrictEqual( + expect.objectContaining({ + endpoint: null + }) + ); + + // @ts-expect-error: purposely missing endpoint parameter + await addToRequestLog({ req: req2, res: res3 }); + + expect(warnSpy).toBeCalledTimes(3); + + await expect( + reqlog.findOne({ resStatusCode: 3333 as HttpStatusCode }) + ).resolves.toStrictEqual( + expect.objectContaining({ + endpoint: null + }) + ); + + await addToRequestLog({ + req: req2, + res: res3, + endpoint: '/fake', + durationMs: 1234 + }); + + expect(warnSpy).toBeCalledTimes(3); + }); + }); +}); diff --git a/lib/suppress-experimental-warnings/index.ts b/lib/suppress-experimental-warnings/index.ts new file mode 100644 index 0000000..7373bd1 --- /dev/null +++ b/lib/suppress-experimental-warnings/index.ts @@ -0,0 +1,39 @@ +import { debugFactory } from 'multiverse/debug-extended'; + +const debug = debugFactory('suppress-warnings:debug'); + +/** + * Prevent Node from emitting specific warnings when running third-party code. + */ +export function suppressWarnings( + /** + * The exact (case-sensitive) names of the warnings that will be suppressed. + * + * @default ['ExperimentalWarning'] + */ + names?: string[] +) { + const ignoredWarningNames = names || ['ExperimentalWarning']; + const warningListeners = process.listeners('warning'); + let alreadyWarned = false; + + if (warningListeners[0]) { + const originalWarningListener = warningListeners[0]; + process.removeAllListeners('warning'); + + process.prependListener('warning', (warning) => { + if (!ignoredWarningNames.includes(warning.name)) { + originalWarningListener(warning); + } else if (!alreadyWarned) { + debug.warn('one or more warnings were suppressed'); + alreadyWarned = true; + } + }); + } + + if (warningListeners.length != 1) { + debug.warn( + `expected 1 listener on the process "warning" event, but removed ${warningListeners.length}` + ); + } +} diff --git a/lib/suppress-experimental-warnings/package.json b/lib/suppress-experimental-warnings/package.json new file mode 100644 index 0000000..47c207c --- /dev/null +++ b/lib/suppress-experimental-warnings/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/suppress-warnings" +} diff --git a/lib/throttled-fetch/index.ts b/lib/throttled-fetch/index.ts new file mode 100644 index 0000000..913df5a --- /dev/null +++ b/lib/throttled-fetch/index.ts @@ -0,0 +1,919 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import fetch, { Headers } from 'node-fetch'; +import autobind from 'auto-bind'; +import { GuruMeditationError, HttpError, makeNamedError } from 'named-app-errors'; +import { toss } from 'toss-expression'; +import { setTimeout as wait } from 'node:timers/promises'; + +import { debugFactory } from 'multiverse/debug-extended'; + +const debug = debugFactory(`throttled-fetch:index`); + +import type { Promisable } from 'type-fest'; +import type { RequestInfo, RequestInit } from 'node-fetch'; + +/** + * An internal representation of the node-fetch function's parameters. + */ +type FetchParams = Parameters; + +/** + * An internal representation of the `addRequestToQueue` and + * `prependRequestToQueue` parameters. + */ +type ExtendedFetchParams = [ + url: RequestInfo, + init?: RequestInit | undefined, + state?: Record +]; + +/** + * An internal function used to eventually resolve the `addRequestToQueue` and + * `prependRequestToQueue` calls with the response data. + */ +type RequestQueueCallback = { + (err: null, retval: unknown): void; + (err: Error, retval?: undefined): void; +}; + +/** + * A `RequestInit` instance guaranteed to have a non-falsy signal property. + */ +type RequestInitWithSignal = RequestInit & { + signal: NonNullable; +}; + +/** + * The shape of a request-inspecting function. + */ +export type RequestInspector = (params: { + queue: RequestQueue; + requestInfo: RequestInfo; + requestInit: RequestInitWithSignal; + state: Record; +}) => Promisable; + +/** + * The shape of a response-inspecting function. + */ +export type ResponseInspector = (params: { + response: unknown; + queue: RequestQueue; + requestInfo: RequestInfo; + requestInit: RequestInitWithSignal; + state: Record; +}) => Promisable; + +/** + * The shape of a FetchError-inspecting function. + */ +export type FetchErrorInspector = (params: { + error: unknown; + queue: RequestQueue; + requestInfo: RequestInfo; + requestInit: RequestInitWithSignal; + state: Record; +}) => Promisable; + +/** + * Thrown in response to a queue-related error. + */ +export class RequestQueueError extends Error {} +makeNamedError(RequestQueueError, 'RequestQueueError'); + +/** + * Thrown by `addRequestToQueue` when the request was removed from the queue + * without being sent or otherwise processed. + */ +export class RequestQueueClearedError extends RequestQueueError {} +makeNamedError(RequestQueueClearedError, 'RequestQueueClearedError'); + +/** + * The default `RequestInspector` used by each `RequestQueue` instance unless + * otherwise configured. Simply passes through the given fetch parameters. + */ +export const defaultRequestInspector: RequestInspector = () => undefined; + +/** + * The default `ResponseInspector` used by each `RequestQueue` instance unless + * otherwise configured. Simply passes through the `Response` instance. + */ +export const defaultResponseInspector: ResponseInspector = ({ response: res }) => res; + +/** + * The default `FetchErrorInspector` used by each `RequestQueue` instance unless + * otherwise configured. Re-throws the `FetchError` instance. + */ +export const defaultFetchErrorInspector: FetchErrorInspector = ({ error: e }) => { + throw e; +}; + +/** + * Execute requests present in the request queue with respect to backoff data, + * flow control, rate limits, and other data. + */ +export class RequestQueue { + /** + * If non-zero, no new requests will be made until this many milliseconds have + * transpired. + */ + #delayRequestProcessingByMs = 0; + + /** + * Once this is set to false and requestQueue is empty, + * `queueAbortController.abort()` will be called automatically. + */ + #keepProcessingRequestQueue = true; + + /** + * Determines when queue processing is "soft-paused," which allows the + * processor to avoid wasting cycles scheduling intervals when the request + * queue is empty. + */ + #queueProcessingIsSoftPaused = false; + + /** + * Used to abort the request queue processor. + */ + #timeoutId: NodeJS.Timeout | null = null; + + /** + * Used to immediately end the delaying period. + */ + #terminationAbortController = new AbortController(); + + /** + * A function used to individual requests based on feedback from request data. + */ + #requestInspector: RequestInspector; + + /** + * A function used to alter the behavior of the queue based on feedback from + * response data. + */ + #responseInspector: ResponseInspector; + + /** + * A function used to alter the behavior of the queue when the fetch function + * rejects. + */ + #fetchErrorInspector: FetchErrorInspector; + + /** + * Default request initialization parameters sent along with every request. + */ + #defaultRequestInit: RequestInit = {}; + + /** + * Used to facilitate "waiting" for the queue to stop processing requests. + */ + #queueStoppedPromise: Promise = Promise.resolve(); + + /** + * Used to facilitate "waiting" for the queue to stop processing requests. + */ + /* istanbul ignore next */ + #queueStoppedPromiseResolver: () => void = () => undefined; + + /** + * The maximum number of requests processed in a single interval. + */ + #maxRequestsPerInterval: number; + + /** + * The number of milliseconds between intervals. + */ + #intervalPeriodMs: number; + + /** + * A queue of requests waiting to be processed. The response JSON data will be + * returned via the `resolve` function. + */ + #requestQueue: [ + fetchParams: FetchParams, + callback: RequestQueueCallback, + state: Record + ][] = []; + + /** + * A counter used only in debug and stats output. + */ + #debugIntervalCounter = 0; + + /** + * A counter used only in debug and stats output. + */ + #debugAddRequestCounter = 0; + + /** + * A counter used only in debug and stats output. + */ + #debugSentRequestCounter = 0; + + /** + * Create, configure, and return a new RequestQueue instance. All instance + * methods are auto-bound. + */ + constructor({ + maxRequestsPerInterval, + intervalPeriodMs, + requestInspector, + responseInspector, + fetchErrorInspector, + autoStart = false + }: { + /** + * A maximum of `maxRequestsPerInterval` requests will be processed every + * `>=intervalPeriodMs` milliseconds. + */ + maxRequestsPerInterval: number; + /** + * A maximum of `maxRequestsPerInterval` requests will be processed every + * `>=intervalPeriodMs` milliseconds. + */ + intervalPeriodMs: number; + /** + * A function used to alter the behavior of individual requests based on + * available parameters. This function must do one of the following before + * terminating: + * + * - Mutate `addRequestToQueue`'s params before letting it continuing. + * - BYO fetch library and return a promise that resolves how you want. + * - Call `addRequestToQueue` again and return it (beware infinite loops). + * - Await a `setTimeout` promise to delay the request before continuing. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * Delaying a request using `requestInspector` will have no effect on the + * processing of other requests or the period between intervals, making it + * ideal for more complex (e.g. isolated, per-endpoint) throttling + * requirements. + * + * If this function returns `undefined` or a promise that resolves to + * `undefined`, an internal `fetch()` will be made using the request params + * passed to (and potentially mutated by) this function. The fetch result + * will be passed to `responseInspector`. Otherwise, the resolved defined + * value of this function will be passed to `responseInspector` directly (no + * additional internal `fetch()` happens). + * + * If this function throws, the corresponding `addRequestToQueue` call will + * reject and `responseInspector` will not be called. + * + * @default defaultRequestInspector (see export) + */ + requestInspector?: RequestInspector; + /** + * A function used to reshape response data before returning it through the + * resolved `addRequestToQueue` promise. This function must do one of the + * following before terminating: + * + * - Return a JSON representation of the response, e.g. `response.json()`. + * - Interpret and/or transform the response data and return any value. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * The return value of this function will eventually be used as the resolved + * value of the promise returned by the corresponding `addRequestToQueue` + * call that triggered it. Similarly, if this function throws, the + * corresponding `addRequestToQueue` call will reject. + * + * @default defaultResponseInspector (see export) + */ + responseInspector?: ResponseInspector; + /** + * A function used to take some action after the node-fetch `fetch` function + * rejects due to failure. Like `requestInspector`, this function must do + * one of the following before terminating: + * + * - Return a promise that resolves how you want. + * - Call `addRequestToQueue` again and return it (beware infinite loops). + * - Await a `setTimeout` promise to delay the request before continuing. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * Delaying a request using `requestInspector` will have no effect on the + * processing of other requests or the period between intervals, making it + * ideal to retry failed fetch requests. + * + * The resolved value of this function will always be passed to + * `responseInspector` directly (no additional internal `fetch()` happens) + * unless an error is thrown, in which case the `addRequestToQueue` return + * value will reject. + * + * @default defaultFetchErrorInspector (see export) + */ + fetchErrorInspector?: FetchErrorInspector; + /** + * If `true`, `beginProcessingRequestQueue` and + * `gracefullyStopProcessingRequestQueue` will be called immediately after + * the new instance is created. This allows you to start adding requests to + * the queue immediately without worrying about managing the processor + * runtime. + * + * Note that using `await` in the same context as the queue instance, and + * before adding all of your desired requests, could cause the queue to stop + * processing earlier than you might expect. If this is happening, you'll + * have to call `beginProcessingRequestQueue` and + * `gracefullyStopProcessingRequestQueue` manually instead of using + * `autoStart`. + * + * @default false + */ + autoStart?: boolean; + }) { + this.#maxRequestsPerInterval = maxRequestsPerInterval; + this.#intervalPeriodMs = intervalPeriodMs; + this.#requestInspector = requestInspector ?? defaultRequestInspector; + this.#responseInspector = responseInspector ?? defaultResponseInspector; + this.#fetchErrorInspector = fetchErrorInspector ?? defaultFetchErrorInspector; + + // ? Note that this only auto-binds public methods + autobind(this); + + if (autoStart) { + this.beginProcessingRequestQueue(); + this.gracefullyStopProcessingRequestQueue(); + } + } + + /** + * Returns `true` if the request queue is currently being processed or `false` + * otherwise. + */ + get isProcessingRequestQueue() { + return !!this.#timeoutId; + } + + /** + * If non-zero, no new requests will be made until this many milliseconds have + * transpired. This value is relative to when `delayRequestProcessingByMs` was + * last called, so querying this property isn't useful without that additional + * context. + */ + get requestProcessingDelayMs() { + return this.#delayRequestProcessingByMs; + } + + /** + * A function used to alter the behavior of individual requests based on + * available parameters. This function must do one of the following before + * terminating: + * + * - Mutate `addRequestToQueue`'s params before letting it continuing. + * - BYO fetch library and return a promise that resolves how you want. + * - Call `addRequestToQueue` again and return it (beware infinite loops). + * - Await a `setTimeout` promise to delay the request before continuing. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * Delaying a request using `requestInspector` will have no effect on the + * processing of other requests or the period between intervals, making it + * ideal for more complex (e.g. isolated, per-endpoint) throttling + * requirements. + * + * If this function returns `undefined` or a promise that resolves to + * `undefined`, an internal `fetch()` will be made using the request params + * passed to (and potentially mutated by) this function. The fetch result + * will be passed to `responseInspector`. Otherwise, the resolved defined + * value of this function will be passed to `responseInspector` directly (no + * internal `fetch()` happens). + * + * If this function throws, the corresponding `addRequestToQueue` call will + * reject and `responseInspector` will not be called. + * + * @default defaultRequestInspector (see export) + */ + get requestInspector() { + return this.#requestInspector; + } + + set requestInspector(inspector) { + debug('set new requestInspector'); + this.#requestInspector = inspector; + } + + /** + * A function used to reshape response data before returning it through the + * resolved `addRequestToQueue` promise. This function must do one of the + * following before terminating: + * + * - Return a JSON representation of the response, e.g. `response.json()`. + * - Interpret and/or transform the response data and return any value. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * The return value of this function will eventually be used as the resolved + * value of the promise returned by the corresponding `addRequestToQueue` + * call that triggered it. Similarly, if this function throws, the + * corresponding `addRequestToQueue` call will reject. + * + * @default defaultResponseInspector (see export) + */ + get responseInspector() { + return this.#responseInspector; + } + + set responseInspector(inspector) { + debug('set new responseInspector'); + this.#responseInspector = inspector; + } + + /** + * A function used to take some action after the node-fetch `fetch` function + * rejects due to failure. Like `requestInspector`, this function must do one + * of the following before terminating: + * + * - Return a promise that resolves how you want. + * - Call `addRequestToQueue` again and return it (beware infinite loops). + * - Await a `setTimeout` promise to delay the request before continuing. + * - Throw an error causing the `addRequestToQueue` method to reject. + * + * Delaying a request using `requestInspector` will have no effect on the + * processing of other requests or the period between intervals, making it + * ideal to retry failed fetch requests. + * + * The resolved value of this function will always be passed to + * `responseInspector` directly (no additional internal `fetch()` happens) + * unless an error is thrown, in which case the `addRequestToQueue` return + * value will reject. + * + * @default defaultFetchErrorInspector (see export) + */ + get fetchErrorInspector() { + return this.#fetchErrorInspector; + } + + set fetchErrorInspector(inspector) { + debug('set new fetchErrorInspector'); + this.#fetchErrorInspector = inspector; + } + + /** + * Default request initialization parameters sent along with every request. + */ + get defaultRequestInit() { + return this.#defaultRequestInit; + } + + set defaultRequestInit(init) { + debug('set defaultRequestInit: %O => %O', this.#defaultRequestInit, init); + this.#defaultRequestInit = init; + } + + /** + * A maximum of `maxRequestsPerInterval` requests will be processed every + * `>=intervalPeriodMs` milliseconds. + */ + get maxRequestsPerInterval() { + return this.#maxRequestsPerInterval; + } + + set maxRequestsPerInterval(count) { + debug(`set maxRequestsPerInterval: ${this.#maxRequestsPerInterval} => ${count}`); + this.#maxRequestsPerInterval = count; + } + + /** + * A maximum of `maxRequestsPerInterval` requests will be processed every + * `>=intervalPeriodMs` milliseconds. + */ + get intervalPeriodMs() { + return this.#intervalPeriodMs; + } + + set intervalPeriodMs(period) { + debug(`set intervalPeriodMs: ${this.#intervalPeriodMs} => ${period}`); + this.#intervalPeriodMs = period; + } + + #scheduleNextInterval() { + this.#timeoutId = setTimeout( + this.#processRequestQueue.bind(this), + this.intervalPeriodMs + ); + + if (this.#queueProcessingIsSoftPaused) { + debug('queue processing unpaused'); + this.#queueProcessingIsSoftPaused = false; + } + + debug(`scheduled next interval in ${this.intervalPeriodMs}ms`); + } + + #finishGracefulStop() { + this.#timeoutId = null; + this.#queueProcessingIsSoftPaused = false; + + debug('queue processing stopped gracefully'); + + this.#queueStoppedPromiseResolver(); + } + + /** + * An internal function to execute requests present in the request queue with + * respect to constraints available in the response data. + */ + async #processRequestQueue() { + const subDebug = debug.extend(`interval#${++this.#debugIntervalCounter}`); + subDebug('entered queue processing interval function'); + + if (this.#requestQueue.length) { + let previousMaxRequestsPerInterval = this.maxRequestsPerInterval; + const terminationAbortController = this.#terminationAbortController; + + subDebug( + `processing at most ${previousMaxRequestsPerInterval} requests in this interval` + ); + + let count = 0; + + for (; count < this.maxRequestsPerInterval; ++count) { + const reqDebug = subDebug.extend(`request#${count + 1}`); + + if ( + !terminationAbortController.signal.aborted && + this.#delayRequestProcessingByMs + ) { + reqDebug('detected non-zero delayRequestProcessingByMs, processing paused'); + reqDebug(`resuming in ${this.#delayRequestProcessingByMs}ms...`); + + const previousDelayRequestProcessingByMs = this.#delayRequestProcessingByMs; + this.#delayRequestProcessingByMs = 0; + + // ? If we need to delay, pause processing request queue immediately + // eslint-disable-next-line no-await-in-loop + await wait(previousDelayRequestProcessingByMs, undefined, { + signal: terminationAbortController.signal + }); + + reqDebug(`processing resumed`); + + if (this.#delayRequestProcessingByMs != 0) { + subDebug.warn( + `resetting delayRequestProcessingByMs after resuming queue processing` + ); + this.#delayRequestProcessingByMs = 0; + } + } + + if (previousMaxRequestsPerInterval != this.maxRequestsPerInterval) { + subDebug.warn( + `maxRequestsPerInterval changed while processing request #${ + count + 1 + }: ${previousMaxRequestsPerInterval} => ${this.maxRequestsPerInterval}` + ); + previousMaxRequestsPerInterval = this.maxRequestsPerInterval; + } + + if (terminationAbortController.signal.aborted) { + // ? Immediately terminate request queue processing + reqDebug(`detected abort signal: queue processing will terminate`); + break; + } + + const enqueuedRequest = this.#requestQueue.shift(); + + if (!enqueuedRequest) { + reqDebug(`early end of queue: nothing left to process in this interval`); + break; + } + + const [fetchParams, callback, state] = enqueuedRequest; + + // ? Guarantees fetchParams[1] is non-nullish + fetchParams[1] = { + ...this.#defaultRequestInit, + ...fetchParams[1], + headers: new Headers( + Object.entries({ + // ? Ensure default headers can be overwritten + ...new Headers(this.#defaultRequestInit?.headers).raw(), + ...new Headers(fetchParams[1]?.headers).raw() + }).map(([k, v]) => [k, v.join(',')]) + ), + signal: terminationAbortController.signal + } as RequestInitWithSignal; + + const isFinal = count + 1 >= this.maxRequestsPerInterval; + + reqDebug(`preparing to fetch: ${fetchParams[0]}`); + + void (async () => { + try { + reqDebug('triggering request inspector'); + + const inspectorParams = { + queue: this, + requestInfo: fetchParams[0], + requestInit: fetchParams[1] as RequestInitWithSignal, + state + }; + + const requestInspectorResult = await this.requestInspector( + inspectorParams + ); + + let res: unknown; + + if (requestInspectorResult === undefined) { + try { + reqDebug(`internal fetch: ${inspectorParams.requestInfo}`); + this.#debugSentRequestCounter++; + + res = await fetch( + inspectorParams.requestInfo, + inspectorParams.requestInit + ); + + reqDebug('response received'); + } catch (e) { + reqDebug.error( + `triggering error inspector for in-flight request error: ${e}` + ); + + try { + res = await this.fetchErrorInspector({ + error: e, + queue: this, + requestInfo: inspectorParams.requestInfo, + requestInit: inspectorParams.requestInit, + state: inspectorParams.state + }); + } catch (e) { + reqDebug.error(`unhandled error during in-flight request: ${e}`); + callback( + e instanceof Error + ? e + : /* istanbul ignore next */ + new HttpError(res as Response, String(e)) + ); + return; + } + } + } else { + reqDebug( + 'skipping internal fetch (due to defined requestInspector result)' + ); + res = requestInspectorResult; + } + + if (isFinal) { + reqDebug('this is the final request to be processed in this interval'); + } + + try { + reqDebug('triggering response inspector'); + + callback( + null, + await this.responseInspector({ + response: res, + queue: this, + requestInfo: inspectorParams.requestInfo, + requestInit: inspectorParams.requestInit, + state: inspectorParams.state + }) + ); + + reqDebug('finished processing request'); + } catch (e) { + reqDebug.error(`unhandled error during response inspection: ${e}`); + callback(e instanceof Error ? e : new HttpError(String(e))); + } + } catch (e) { + reqDebug.error(`unhandled error during request inspection: ${e}`); + callback(e instanceof Error ? e : new HttpError(String(e))); + } + })(); + } + + subDebug( + `processing complete: ${count} request${count > 1 ? 's' : ''} in-flight` + ); + + this.#scheduleNextInterval(); + } else if (this.#keepProcessingRequestQueue) { + subDebug('queue empty: nothing to process in this interval'); + + this.#queueProcessingIsSoftPaused = true; + + subDebug( + 'queue processing soft-paused (next interval will be scheduled on new request)' + ); + } else { + subDebug( + 'queue empty and graceful stop requested: queue processing will be terminated' + ); + + this.#finishGracefulStop(); + } + } + + /** + * Append a request to the request queue. This function returns a promise that + * will resolve with the request's response data as determined by + * `responseInspector`. + */ + addRequestToQueue(...params: ExtendedFetchParams): Promise { + debug('adding (appending) new request to queue'); + this.#debugAddRequestCounter++; + + if (this.#queueProcessingIsSoftPaused && this.#keepProcessingRequestQueue) { + this.#scheduleNextInterval(); + } + + return new Promise((resolve, reject) => { + this.#requestQueue.push([ + [params[0], params[1]], + (err, retval) => { + err ? reject(err) : resolve(retval as TT); + }, + params[2] || {} + ]); + }); + } + + /** + * Exactly the same as `addRequestToQueue` in every way, except the request is + * _prepended_ rather than appended to the queue. + */ + prependRequestToQueue(...params: ExtendedFetchParams): Promise { + debug('adding (prepending) new request to queue'); + this.#debugAddRequestCounter++; + + if (this.#queueProcessingIsSoftPaused && this.#keepProcessingRequestQueue) { + this.#scheduleNextInterval(); + } + + return new Promise((resolve, reject) => { + this.#requestQueue.unshift([ + [params[0], params[1]], + (err, retval) => { + err ? reject(err) : resolve(retval as TT); + }, + params[2] || {} + ]); + }); + } + + /** + * Calling this function will cause the request processor to wait `delay` + * milliseconds before sending any subsequent requests. Requests that have + * already been sent will resolve without delay. + * + * After the delay period transpires, the internal delay value will be reset + * to `0` _regardless of calls to `delayRequestProcessingByMs` in the + * interim_. Hence, due to the asynchronous nature of request processing, + * calling `delayRequestProcessingByMs` asynchronously (e.g. via + * `requestInspector` or `responseInspector`) **does not guarantee that the + * new value will be respected.** + * + * To implement backoff or other complex throttling functionality, consider + * instead using per-request delays manually (e.g. via `setTimeout`) at the + * `requestInspector` level. + */ + delayRequestProcessingByMs(delay: number) { + if (delay < 0) { + throw new RequestQueueError( + `delayRequestProcessingByMs must be a non-negative integer, saw ${delay} instead` + ); + } + + debug( + `set delayRequestProcessingByMs: ${ + this.#delayRequestProcessingByMs + } => ${delay}` + ); + this.#delayRequestProcessingByMs = delay; + } + + /** + * Begin asynchronously processing the request queue. If the queue is already + * being processed, calling this function again will throw. + */ + beginProcessingRequestQueue() { + if (this.isProcessingRequestQueue) { + debug.error( + 'attempted to call beginProcessingRequestQueue while already processing request queue' + ); + throw new RequestQueueError('already processing request queue'); + } else { + debug(`beginning queue processing (${this.intervalPeriodMs}ms intervals)`); + + this.#keepProcessingRequestQueue = true; + this.#queueProcessingIsSoftPaused = false; + + this.#terminationAbortController = new AbortController(); + + this.#queueStoppedPromise = new Promise((resolve) => { + this.#queueStoppedPromiseResolver = resolve; + }); + + this.#scheduleNextInterval(); + } + } + + /** + * Signal to the queue processor that, once the queue is empty, request + * processing is to stop. This means no further requests will be dequeued or + * executed. + * + * Requests can still be added to the queue after request processing + * eventually stops (via `addRequestToQueue`), but they will not be dequeued + * and executed until `beginProcessingRequestQueue` is called again. + * + * This function will throw if called when the queue is not being processed. + */ + gracefullyStopProcessingRequestQueue() { + if (!this.isProcessingRequestQueue) { + debug.error( + 'attempted to call gracefullyStopProcessingRequestQueue when processing already stopped' + ); + throw new RequestQueueError('request queue processing already stopped'); + } + + debug('graceful stop signal received: processing will stop when queue is empty'); + + this.#keepProcessingRequestQueue = false; + + if (this.#queueProcessingIsSoftPaused) { + this.#finishGracefulStop(); + } + } + + /** + * Signal to the queue processor to stop all request processing immediately, + * regardless of if the queue is empty or not. After calling this method, no + * new or queued requests will be processed, though the queue is not cleared. + * Requests can still be added to the queue (via `addRequestToQueue`) but they + * will not be processed until `beginProcessingRequestQueue` is called again. + * + * If a request is in-flight when this method is called, the request will be + * aborted and the corresponding promise rejected with an `AbortError`. The + * aborted request must be re-added to the queue manually as it will not be + * retried automatically. + * + * This function will throw if called when the queue is not being processed. + */ + immediatelyStopProcessingRequestQueue() { + if (!this.isProcessingRequestQueue) { + debug.error( + 'attempted to call immediatelyStopProcessingRequestQueue when processing already stopped' + ); + throw new RequestQueueError('request queue processing already stopped'); + } + + debug('immediately halting queue processing and aborting in-flight requests'); + + this.#keepProcessingRequestQueue = false; + this.#queueProcessingIsSoftPaused = false; + + clearTimeout(this.#timeoutId as NodeJS.Timeout); + this.#timeoutId = null; + + this.#terminationAbortController.abort(); + + debug('queue processing halted abruptly and in-flight requests aborted'); + + this.#queueStoppedPromiseResolver(); + } + + /** + * Remove all pending and unprocessed requests from the request queue, + * rejecting their corresponding promises with a `RequestQueueClearedError`. + * In-flight requests will still complete unless + * `immediatelyStopProcessingRequestQueue` has been called. + */ + clearRequestQueue() { + const count = this.#requestQueue.length; + debug(`clearing request queue (${count} requests will reject)`); + + for (let i = 0; i < count; ++i) { + const [, callback] = + this.#requestQueue.shift() || + /* istanbul ignore next */ + toss(new GuruMeditationError('queue length invariant violated')); + callback(new RequestQueueClearedError()); + } + + debug('request queue cleared'); + } + + /** + * Returns a promise that resolves after queue processing stops. Before + calling this function, you should ensure that + `gracefullyStopProcessingRequestQueue` or + `immediatelyStopProcessingRequestQueue` have already been or will + eventually be called or this promise will never settle. + */ + waitForQueueProcessingToStop() { + debug('caller is waiting for queue processing to terminate...'); + return this.#queueStoppedPromise; + } + + /** + * Returns various statistics about the queue runtime. + */ + getStats() { + return { + intervals: this.#debugIntervalCounter, + requestsEnqueued: this.#debugAddRequestCounter, + internalRequestsSent: this.#debugSentRequestCounter + }; + } +} diff --git a/lib/throttled-fetch/package.json b/lib/throttled-fetch/package.json new file mode 100644 index 0000000..a9ae21e --- /dev/null +++ b/lib/throttled-fetch/package.json @@ -0,0 +1,3 @@ +{ + "name": "@xunnamius/throttled-fetch" +} diff --git a/lib/throttled-fetch/unit.test.ts b/lib/throttled-fetch/unit.test.ts new file mode 100644 index 0000000..a8bf22b --- /dev/null +++ b/lib/throttled-fetch/unit.test.ts @@ -0,0 +1,1564 @@ +/* eslint-disable jest/unbound-method */ +/* eslint-disable jest/prefer-lowercase-title */ +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { DummyError, TrialError } from 'named-app-errors'; +import { Headers, Response } from 'node-fetch'; +import { asMockedFunction } from '@xunnamius/jest-types'; +import { setTimeout } from 'node:timers/promises'; + +import { + defaultRequestInspector, + defaultResponseInspector, + RequestQueue, + RequestQueueClearedError, + RequestQueueError +} from 'multiverse/throttled-fetch'; + +jest.mock('node:timers/promises'); + +const useFakeTimers = () => + jest.useFakeTimers({ + timerLimit: 10, + doNotFake: [ + 'Date', + 'hrtime', + 'nextTick', + 'performance', + 'queueMicrotask', + 'requestAnimationFrame', + 'cancelAnimationFrame', + 'requestIdleCallback', + 'cancelIdleCallback', + 'setImmediate', + 'clearImmediate', + 'setInterval', + 'clearInterval' + ] + }); + +const mockSetTimeout = asMockedFunction(setTimeout); + +const server = setupServer( + rest.all('*', async (req, res, ctx) => { + const { method, headers, params } = req; + const body = await req.text(); + + return res( + ctx.status( + body.startsWith('status=') + ? Number.parseInt(body.split('status=').at(-1) || '200') + : 200 + ), + ctx.json({ method, headers: headers.raw(), params, body }) + ); + }) +); + +/** + * Ensure that a promise does not settle before it's supposed to. + * + * @returns a `setThreshold` function and a `sentinel` function (via array) + */ +const promiseSettledSentinel = () => { + let threshold = 0; + + return [ + (newThreshold: number) => (threshold = newThreshold), + { + resolve(count = Infinity) { + return (result: T) => { + if (count > threshold) { + throw new TrialError( + `promise #${count} resolved before it was supposed to` + ); + } + return Promise.resolve(result); + }; + }, + + reject(count = Infinity) { + return (result: T) => { + if (count > threshold) { + throw new TrialError( + `promise #${count} rejected before it was supposed to` + ); + } + return Promise.reject(result); + }; + } + } + ] as const; +}; + +/** + * Ensure no new intervals are being processed by the given queue. This function + * should be called at the very end of your test when you want to ensure queue + * processing has terminated. **Otherwise, you'll have to deal with an extra + * enqueued request.** + */ +const assertRequestQueueProcessingStopped = (queue: RequestQueue) => { + const mockSetTimeoutCalls = mockSetTimeout.mock.calls.length; + const previousRequestDelayMs = queue.requestProcessingDelayMs; + + // ? First, ensure graceful stop happens + jest.advanceTimersByTime(queue.intervalPeriodMs); + + // ? Now, if we didn't stop, then this request delay will trip mockSetTimeout + queue.delayRequestProcessingByMs(1000); + void queue.addRequestToQueue('https://fake-url'); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + expect(mockSetTimeout).toBeCalledTimes(mockSetTimeoutCalls); + expect(queue.isProcessingRequestQueue).toBeFalse(); + + // ? Reset things back to the way they were + queue.delayRequestProcessingByMs(previousRequestDelayMs); +}; + +beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); + +beforeEach(() => { + mockSetTimeout.mockReturnValue(Promise.resolve()); + useFakeTimers(); +}); + +afterEach(() => { + server.resetHandlers(); + jest.clearAllTimers(); + jest.useRealTimers(); +}); + +afterAll(() => server.close()); + +describe('RequestQueue::constructor', () => { + it('can be initialized without inspectors', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 30 + }); + + expect(queue.intervalPeriodMs).toBe(1000); + expect(queue.maxRequestsPerInterval).toBe(30); + expect(queue.requestInspector).toBe(defaultRequestInspector); + expect(queue.responseInspector).toBe(defaultResponseInspector); + }); + + it('can be initialized with inspectors', async () => { + expect.hasAssertions(); + + const requestInspector = () => undefined; + const responseInspector = () => undefined; + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 30, + requestInspector, + responseInspector + }); + + expect(queue.intervalPeriodMs).toBe(1000); + expect(queue.maxRequestsPerInterval).toBe(30); + expect(queue.requestInspector).toBe(requestInspector); + expect(queue.responseInspector).toBe(responseInspector); + }); + + it('begins processing queue upon instantiation and gracefully ends automatically if autoStart is true', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + const pRes = queue.addRequestToQueue('https://fake-url'); + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + await expect(pRes).resolves.toBeInstanceOf(Response); + + assertRequestQueueProcessingStopped(queue); + }); +}); + +describe('RequestQueue::addRequestToQueue', () => { + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { addRequestToQueue } = queue; + const pRes = addRequestToQueue('https://fake-url'); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersToNextTimer(); + await expect(pRes).resolves.toBeInstanceOf(Response); + }); + + it('can override return type information provided at instantiation', async () => { + expect.assertions(0); + + const { addRequestToQueue } = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const pRes = addRequestToQueue<{ a: 1 }>('https://fake-url'); + const res = pRes as unknown as Awaited; + + void res.a; + // @ts-expect-error: if an error occurs, then the type check was successful! + void res.json; + }); +}); + +describe('RequestQueue::prependRequestToQueue', () => { + it('prepends new requests to the queue', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pRes1 = queue + .prependRequestToQueue('https://fake-url-2') + .then(sentinel.resolve(2), sentinel.reject()); + + const pRes2 = queue + .addRequestToQueue('https://fake-url-1') + .then(sentinel.resolve(3), sentinel.reject()); + + const pRes3 = queue + .prependRequestToQueue('https://fake-url-3') + .then(sentinel.resolve(1), sentinel.reject()); + + setThreshold(1); + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pRes3).resolves.toBeInstanceOf(Response); + + setThreshold(2); + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pRes1).resolves.toBeInstanceOf(Response); + + setThreshold(3); + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pRes2).resolves.toBeInstanceOf(Response); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { prependRequestToQueue } = queue; + const pRes = prependRequestToQueue('https://fake-url'); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersToNextTimer(); + await expect(pRes).resolves.toBeInstanceOf(Response); + }); + + it('can override return type information provided at instantiation', async () => { + expect.assertions(0); + + const { prependRequestToQueue } = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const pRes = prependRequestToQueue<{ a: 1 }>('https://fake-url'); + const res = pRes as unknown as Awaited; + + void res.a; + // @ts-expect-error: if an error occurs, then the type check was successful! + void res.json; + }); +}); + +describe('RequestQueue::delayRequestProcessingByMs', () => { + it('causes delaying of entire request processing routine when set', async () => { + expect.hasAssertions(); + + let delayPromiseResolver: ((value?: unknown) => void) | undefined; + + mockSetTimeout.mockImplementation((_, __) => { + return new Promise((resolve) => { + delayPromiseResolver = resolve; + }); + }); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pRes1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(1), sentinel.reject()); + + queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(), sentinel.reject()); + + jest.advanceTimersByTime(10 * queue.intervalPeriodMs); + + expect(delayPromiseResolver).toBeDefined(); + + setThreshold(1); + delayPromiseResolver?.(); + + await expect(pRes1).resolves.toBeInstanceOf(Response); + }); + + it('is reset before a delay transpires', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + void queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + expect(queue.requestProcessingDelayMs).toBe(0); + }); + + it('is reset after a delay transpires, preventing unnecessary slowdowns', async () => { + expect.hasAssertions(); + + let delayPromiseResolver: ((value?: unknown) => void) | undefined; + + mockSetTimeout.mockImplementation((_, __) => { + return new Promise((resolve) => { + delayPromiseResolver = resolve; + }); + }); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + const pRes1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + expect(delayPromiseResolver).toBeDefined(); + + queue.delayRequestProcessingByMs(10000); + delayPromiseResolver?.(); + + await expect(pRes1).resolves.toBeInstanceOf(Response); + expect(queue.requestProcessingDelayMs).toBe(0); + }); + + it('throws when attempting to set negative delay', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + expect(() => queue.delayRequestProcessingByMs(-1)).toThrow(RequestQueueError); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { delayRequestProcessingByMs } = queue; + expect(() => delayRequestProcessingByMs(-1)).toThrow(RequestQueueError); + }); +}); + +describe('RequestQueue::beginProcessingRequestQueue', () => { + it('processes requests added to the queue', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const requests = [ + queue + .addRequestToQueue('https://fake-url', { method: 'GET' }) + .then(sentinel.resolve(1)), + queue + .addRequestToQueue('https://fake-url', { method: 'POST' }) + .then(sentinel.resolve(2)), + queue + .addRequestToQueue('https://fake-url', { method: 'PUT' }) + .then(sentinel.resolve(3)), + queue + .addRequestToQueue('https://fake-url', { method: 'PATCH' }) + .then(sentinel.resolve(4)), + queue + .addRequestToQueue('https://fake-url', { method: 'DELETE' }) + .then(sentinel.resolve(5)) + ]; + + queue.beginProcessingRequestQueue(); + + setThreshold(1); + jest.advanceTimersByTime(1 * queue.intervalPeriodMs); + + await expect((await requests[0]).json()).resolves.toHaveProperty('method', 'GET'); + + setThreshold(2); + jest.advanceTimersByTime(1 * queue.intervalPeriodMs); + + await expect((await requests[1]).json()).resolves.toHaveProperty( + 'method', + 'POST' + ); + + setThreshold(5); + jest.advanceTimersByTime(3 * queue.intervalPeriodMs); + + await expect((await requests[2]).json()).resolves.toHaveProperty('method', 'PUT'); + await expect((await requests[3]).json()).resolves.toHaveProperty( + 'method', + 'PATCH' + ); + + await expect((await requests[4]).json()).resolves.toHaveProperty( + 'method', + 'DELETE' + ); + }); + + it('respects headers passed to addRequestToQueue', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 3 + }); + + const requests = [ + queue.addRequestToQueue('https://fake-url', { + method: 'GET', + headers: { a: '1' } + }), + queue.addRequestToQueue('https://fake-url', { + method: 'GET', + headers: [['b', '2']] + }), + queue.addRequestToQueue('https://fake-url', { + method: 'GET', + headers: new Headers({ c: '3' }) + }) + ].map((req) => req.then((res) => res.json())); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + const responses = await Promise.all(requests); + + expect(responses[0]).toHaveProperty( + 'headers', + expect.objectContaining({ a: '1' }) + ); + + expect(responses[1]).toHaveProperty( + 'headers', + expect.objectContaining({ b: '2' }) + ); + + expect(responses[2]).toHaveProperty( + 'headers', + expect.objectContaining({ c: '3' }) + ); + }); + + it('keeps processing even if queue is empty', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(3 * queue.intervalPeriodMs); + + const pReq = queue.addRequestToQueue('https://fake-url', { method: 'GET' }); + jest.advanceTimersByTime(3 * queue.intervalPeriodMs); + await expect((await pReq).json()).resolves.toHaveProperty('method', 'GET'); + + const pReq2 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + jest.advanceTimersByTime(3 * queue.intervalPeriodMs); + await expect((await pReq2).json()).resolves.toHaveProperty('method', 'POST'); + }); + + it('passes state from addRequestToQueue all the way through the inspectors', async () => { + expect.hasAssertions(); + + const globalState = { state: 'stateful' }; + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 2, + autoStart: true, + requestInspector: ({ state }) => { + state.stateless = false; + return 'custom-value'; + }, + responseInspector: ({ response: val, state }) => { + state.val = val; + return state; + } + }); + + const pReq1 = queue.addRequestToQueue('https://fake-url', undefined, globalState); + const pReq2 = queue.addRequestToQueue('https://fake-url', undefined, { + localState: true + }); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBe(globalState); + await expect(pReq1).resolves.toStrictEqual({ + state: 'stateful', + stateless: false, + val: 'custom-value' + }); + + await expect(pReq2).resolves.not.toBe(globalState); + await expect(pReq2).resolves.toStrictEqual({ + localState: true, + stateless: false, + val: 'custom-value' + }); + }); + + it('throws if called while already processing queue', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + expect(() => queue.beginProcessingRequestQueue()).toThrow(RequestQueueError); + }); + + it('causes addRequestToQueue promise to reject when fetch function rejects', async () => { + expect.hasAssertions(); + + const responseInspector = jest.fn(); + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + responseInspector + }); + + queue.beginProcessingRequestQueue(); + + const pReq = queue.addRequestToQueue('bad-url'); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq).rejects.toBeInstanceOf(Error); + expect(responseInspector).not.toBeCalled(); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { beginProcessingRequestQueue } = queue; + const pRes = queue.addRequestToQueue('https://fake-url', { method: 'GET' }); + + beginProcessingRequestQueue(); + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pRes).resolves.toBeInstanceOf(Response); + }); +}); + +describe('RequestQueue::gracefullyStopProcessingRequestQueue', () => { + it('terminates after request queue is exhausted but still allows queue processing to begin again later', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + jest.advanceTimersByTime(3 * queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + + const pReq2 = queue.addRequestToQueue('https://fake-url'); + + queue.gracefullyStopProcessingRequestQueue(); + jest.advanceTimersByTime(1 * queue.intervalPeriodMs); + + await expect(pReq2).resolves.toBeInstanceOf(Response); + + assertRequestQueueProcessingStopped(queue); + + const pReq3 = queue.addRequestToQueue('https://fake-url'); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + await expect(pReq3).resolves.toBeInstanceOf(Response); + }); + + it('does not abort in-flight requests', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + queue.gracefullyStopProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + const pReq2 = queue.addRequestToQueue('https://fake-url'); + const pReq3 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(4 * queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + await expect(pReq2).resolves.toBeInstanceOf(Response); + await expect(pReq3).resolves.toBeInstanceOf(Response); + + assertRequestQueueProcessingStopped(queue); + }); + + it('throws if queue processing already stopped', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + expect(() => queue.gracefullyStopProcessingRequestQueue()).toThrow( + RequestQueueError + ); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { gracefullyStopProcessingRequestQueue } = queue; + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(queue.intervalPeriodMs); + gracefullyStopProcessingRequestQueue(); + + assertRequestQueueProcessingStopped(queue); + }); +}); + +describe('RequestQueue::immediatelyStopProcessingRequestQueue', () => { + it('terminates immediately even if in the middle of a delay timeout', async () => { + expect.hasAssertions(); + + let abortSignal: AbortSignal | undefined; + let delayPromiseResolver: ((value?: unknown) => void) | undefined; + + mockSetTimeout.mockImplementation((_, __, options) => { + abortSignal = options?.signal; + return new Promise((resolve) => { + delayPromiseResolver = resolve; + }); + }); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 2 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + expect(mockSetTimeout).toBeCalledTimes(0); + + const [, sentinel] = promiseSettledSentinel(); + + queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(), sentinel.reject()); + queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(), sentinel.reject()); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + expect(mockSetTimeout).toBeCalledTimes(1); + expect(abortSignal?.aborted).toBeFalse(); + expect(delayPromiseResolver).toBeDefined(); + + queue.immediatelyStopProcessingRequestQueue(); + + expect(abortSignal?.aborted).toBeTrue(); + delayPromiseResolver?.(); + + assertRequestQueueProcessingStopped(queue); + }); + + it('terminates immediately even if queue is not empty and in-flight requests are pending but still allows queue processing to resume later', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pReq1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(1), sentinel.reject(1)); + const pReq2 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(2), sentinel.reject(2)); + const pReq3 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(3), sentinel.reject(3)); + + setThreshold(1); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + queue.immediatelyStopProcessingRequestQueue(); + + await expect(pReq1).rejects.toMatchObject({ name: 'AbortError' }); + + setThreshold(2); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq2).resolves.toBeInstanceOf(Response); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + queue.immediatelyStopProcessingRequestQueue(); + + setThreshold(3); + + await expect(pReq3).rejects.toMatchObject({ name: 'AbortError' }); + assertRequestQueueProcessingStopped(queue); + }); + + it('throws if queue processing already stopped', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + expect(() => queue.immediatelyStopProcessingRequestQueue()).toThrow( + RequestQueueError + ); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { immediatelyStopProcessingRequestQueue } = queue; + + queue.beginProcessingRequestQueue(); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + const pReq1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(), sentinel.reject(1)); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + immediatelyStopProcessingRequestQueue(); + + setThreshold(1); + + await expect(pReq1).rejects.toMatchObject({ name: 'AbortError' }); + assertRequestQueueProcessingStopped(queue); + }); +}); + +describe('RequestQueue::clearRequestQueue', () => { + it('clears only pending/unprocessed requests from the queue and rejects them', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + const pReq2 = queue.addRequestToQueue('https://fake-url'); + const pReq3 = queue.addRequestToQueue('https://fake-url'); + const pReq4 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + queue.clearRequestQueue(); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + await expect(pReq2).rejects.toBeInstanceOf(RequestQueueClearedError); + await expect(pReq3).rejects.toBeInstanceOf(RequestQueueClearedError); + await expect(pReq4).rejects.toBeInstanceOf(RequestQueueClearedError); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { clearRequestQueue } = queue; + const pReq = queue.addRequestToQueue('https://fake-url'); + + clearRequestQueue(); + + await expect(pReq).rejects.toBeInstanceOf(RequestQueueClearedError); + }); +}); + +describe('RequestQueue::waitForQueueProcessingToStop', () => { + it('resolves only after queue processing is stopped', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + queue.gracefullyStopProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(4 * queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + await expect(queue.waitForQueueProcessingToStop()).resolves.toBeUndefined(); + + assertRequestQueueProcessingStopped(queue); + }); + + it('is auto-bound at instantiation', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const { waitForQueueProcessingToStop } = queue; + + queue.beginProcessingRequestQueue(); + queue.gracefullyStopProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + await expect(waitForQueueProcessingToStop()).resolves.toBeUndefined(); + + assertRequestQueueProcessingStopped(queue); + }); +}); + +describe('RequestQueue::getStats', () => { + it('returns accurate statistics about the queue runtime', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + queue.gracefullyStopProcessingRequestQueue(); + + void queue.addRequestToQueue('https://fake-url'); + void queue.addRequestToQueue('https://fake-url'); + void queue.addRequestToQueue('https://fake-url'); + void queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(10 * queue.intervalPeriodMs); + + await queue.waitForQueueProcessingToStop(); + void queue.addRequestToQueue('https://fake-url'); + + expect(queue.getStats()).toStrictEqual({ + intervals: 5, + requestsEnqueued: 5, + internalRequestsSent: 4 + }); + }); +}); + +describe('RequestQueue::isProcessingRequestQueue', () => { + it('returns true when request queue is being processed and false otherwise', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + expect(queue.isProcessingRequestQueue).toBeTrue(); + + queue.gracefullyStopProcessingRequestQueue(); + expect(queue.isProcessingRequestQueue).toBeTrue(); + + queue.immediatelyStopProcessingRequestQueue(); + expect(queue.isProcessingRequestQueue).toBeFalse(); + }); +}); + +describe('RequestQueue::requestInspector', () => { + it('causes addRequestToQueue promise to resolve with return value', async () => { + expect.hasAssertions(); + + const globalState = {}; + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true, + requestInspector: ({ queue: q, requestInfo, requestInit, state }) => { + expect(q).toBe(queue); + expect(requestInfo).toBe('https://fake-url'); + expect(requestInit.method).toBe('POST'); + expect(state).toStrictEqual(globalState); + + return { hello: 'world' }; + } + }); + + const pReq1 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).resolves.toStrictEqual({ hello: 'world' }); + }); + + it('causes addRequestToQueue promise to reject when rejected or when Error is thrown', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.requestInspector = async () => { + throw new DummyError('bad'); + }; + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).rejects.toThrow(/bad/); + + queue.requestInspector = () => { + throw new DummyError('worse'); + }; + + const pReq2 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq2).rejects.toThrow(/worse/); + }); + + it('causes addRequestToQueue promise to reject with HttpError if non-Error is thrown/rejected', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true, + requestInspector: async () => { + throw 'goodbye, world!'; + } + }); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq1).rejects.toMatchObject({ + name: 'HttpError', + message: expect.stringContaining('goodbye, world!') + }); + + queue.requestInspector = () => { + throw 'goodbye, cruel world!'; + }; + + const pReq2 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq2).rejects.toMatchObject({ + name: 'HttpError', + message: expect.stringContaining('goodbye, cruel world!') + }); + }); + + it('can leverage state parameter and queue methods', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.requestInspector = async ({ queue: q, state }) => { + if (state.hello == 'world') { + const pReqRedirect = q.addRequestToQueue('https://rake-url', { + method: 'PUT', + body: 'status=403' + }); + + return Promise.resolve().then(() => { + jest.advanceTimersByTime(queue.intervalPeriodMs); + return pReqRedirect; + }); + } + }; + + const pReq1 = queue.addRequestToQueue( + 'https://fake-url', + { method: 'POST' }, + { hello: 'world' } + ); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + const req1 = await pReq1; + + expect(req1.url).toBe('https://rake-url/'); + expect(req1.status).toBe(403); + await expect(req1.json()).resolves.toHaveProperty('method', 'PUT'); + }); + + it('can mutate request parameters', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.requestInspector = async (params) => { + params.requestInfo = 'https://rake-url'; + params.requestInit.method = 'GET'; + params.requestInit.headers = new Headers({ somewhere: 'in a better place' }); + }; + + const pReq1 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + jest.advanceTimersByTime(queue.intervalPeriodMs); + const req1 = await pReq1; + const json = await req1.json(); + + expect(req1.url).toBe('https://rake-url/'); + expect(json.method).toBe('GET'); + expect(json.headers).toMatchObject({ somewhere: 'in a better place' }); + }); + + it('skips internal fetch when passing defined return value', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.requestInspector = () => { + return { short: 'circuit' }; + }; + + const pReq1 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq1).resolves.toStrictEqual({ short: 'circuit' }); + }); +}); + +describe('RequestQueue::responseInspector', () => { + it('causes addRequestToQueue promise to resolve with return value', async () => { + expect.hasAssertions(); + + const globalState = {}; + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true, + responseInspector: ({ + response, + queue: q, + requestInfo, + requestInit, + state + }) => { + expect(response).toBeInstanceOf(Response); + expect(q).toBe(queue); + expect(requestInfo).toBe('https://fake-url'); + expect(requestInit.method).toBe('POST'); + expect(state).toStrictEqual(globalState); + + return { hello: 'world' }; + } + }); + + const pReq1 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).resolves.toStrictEqual({ hello: 'world' }); + }); + + it('causes addRequestToQueue promise to reject when rejected or when Error is thrown', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.responseInspector = async () => { + throw new DummyError('bad'); + }; + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).rejects.toThrow(/bad/); + + queue.responseInspector = () => { + throw new DummyError('worse'); + }; + + const pReq2 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq2).rejects.toThrow(/worse/); + }); + + it('causes addRequestToQueue promise to reject with HttpError if non-Error is thrown/rejected', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true, + responseInspector: async () => { + throw 'goodbye, world!'; + } + }); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq1).rejects.toMatchObject({ + name: 'HttpError', + message: expect.stringContaining('goodbye, world!') + }); + + queue.responseInspector = () => { + throw 'goodbye, cruel world!'; + }; + + const pReq2 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq2).rejects.toMatchObject({ + name: 'HttpError', + message: expect.stringContaining('goodbye, cruel world!') + }); + }); + + it('can leverage state parameter and queue methods', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1, + autoStart: true + }); + + queue.responseInspector = async ({ response, queue: q, state }) => { + const res = response as Response; + + if (res.status == 200) { + const pReqRetry = q.addRequestToQueue( + 'https://fake-url', + { method: 'POST', body: 'status=403' }, + { isARetry: true } + ); + + return Promise.resolve().then(() => { + jest.advanceTimersByTime(queue.intervalPeriodMs); + return pReqRetry; + }); + } + + return `hello, ${state.isARetry ? 'retry' : 'world'}!`; + }; + + const pReq1 = queue.addRequestToQueue('https://fake-url', { method: 'POST' }); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).resolves.toBe('hello, retry!'); + }); +}); + +describe('RequestQueue::defaultRequestInit', () => { + test('default request init parameters are passed along with every request', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.defaultRequestInit = { method: 'POST', body: 'yes' }; + queue.beginProcessingRequestQueue(); + + const pReq1 = queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect((await pReq1).json()).resolves.toStrictEqual( + expect.objectContaining({ method: 'POST', body: 'yes' }) + ); + }); + + test('default headers coexist peacefully with headers passed to addRequestToQueue', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + queue.beginProcessingRequestQueue(); + + queue.defaultRequestInit.headers = { a: '1' }; + const pReq1 = queue.addRequestToQueue('https://fake-url'); + const pReq11 = queue.addRequestToQueue('https://fake-url', { + headers: [ + ['a', 'one'], + ['z', 'Z'] + ] + }); + + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + + queue.defaultRequestInit.headers = [['b', '2']]; + const pReq2 = queue.addRequestToQueue('https://fake-url'); + const pReq22 = queue.addRequestToQueue('https://fake-url', { + headers: new Headers([ + ['b', 'two'], + ['z', 'Z'] + ]) + }); + + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + + queue.defaultRequestInit.headers = new Headers({ c: '3' }); + const pReq3 = queue.addRequestToQueue('https://fake-url'); + const pReq33 = queue.addRequestToQueue('https://fake-url', { + headers: { c: 'three', z: 'Z' } + }); + + jest.advanceTimersByTime(2 * queue.intervalPeriodMs); + + await expect((await pReq1).json()).resolves.toStrictEqual( + expect.objectContaining({ headers: expect.objectContaining({ a: '1' }) }) + ); + + await expect((await pReq11).json()).resolves.toStrictEqual( + expect.objectContaining({ + headers: expect.objectContaining({ a: 'one', z: 'Z' }) + }) + ); + + await expect((await pReq2).json()).resolves.toStrictEqual( + expect.objectContaining({ headers: expect.objectContaining({ b: '2' }) }) + ); + + await expect((await pReq22).json()).resolves.toStrictEqual( + expect.objectContaining({ + headers: expect.objectContaining({ b: 'two', z: 'Z' }) + }) + ); + + await expect((await pReq3).json()).resolves.toStrictEqual( + expect.objectContaining({ headers: expect.objectContaining({ c: '3' }) }) + ); + + await expect((await pReq33).json()).resolves.toStrictEqual( + expect.objectContaining({ + headers: expect.objectContaining({ c: 'three', z: 'Z' }) + }) + ); + }); +}); + +describe('RequestQueue::maxRequestsPerInterval', () => { + it('determines the maximum number of requests processed per interval', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 1 + }); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pReq1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(1), sentinel.reject(1)); + const pReq2 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(2), sentinel.reject(2)); + const pReq3 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(3), sentinel.reject(3)); + const pReq4 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(4), sentinel.reject(4)); + const pReq5 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(5), sentinel.reject(5)); + const pReq6 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(6), sentinel.reject(6)); + + setThreshold(1); + + queue.beginProcessingRequestQueue(); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq1).resolves.toBeInstanceOf(Response); + + setThreshold(3); + + queue.maxRequestsPerInterval = 2; + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq2).resolves.toBeInstanceOf(Response); + await expect(pReq3).resolves.toBeInstanceOf(Response); + + setThreshold(6); + + queue.maxRequestsPerInterval = 3; + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq4).resolves.toBeInstanceOf(Response); + await expect(pReq5).resolves.toBeInstanceOf(Response); + await expect(pReq6).resolves.toBeInstanceOf(Response); + }); + + it('can be changed in the midst of interval processing', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 2 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pReq1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(1), sentinel.reject(1)); + + const pReq2 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(2), sentinel.reject(2)); + + const pReq3 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(3), sentinel.reject(3)); + + const pReq4 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(4), sentinel.reject(4)); + + setThreshold(1); + + jest.advanceTimersByTime(queue.intervalPeriodMs); + queue.maxRequestsPerInterval = 1; + + await expect(pReq1).resolves.toBeInstanceOf(Response); + + queue.delayRequestProcessingByMs(1000); + jest.advanceTimersByTime(queue.intervalPeriodMs); + queue.maxRequestsPerInterval = 3; + + setThreshold(4); + + await expect(pReq2).resolves.toBeInstanceOf(Response); + await expect(pReq3).resolves.toBeInstanceOf(Response); + await expect(pReq4).resolves.toBeInstanceOf(Response); + }); + + it('keeps processing even if queue is smaller than maxRequestsPerInterval', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 1000, + maxRequestsPerInterval: 100 + }); + + queue.beginProcessingRequestQueue(); + + const [setThreshold, sentinel] = promiseSettledSentinel(); + + const pReq1 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(1), sentinel.reject(1)); + + setThreshold(1); + jest.advanceTimersByTime(queue.intervalPeriodMs); + await expect(pReq1).resolves.toBeInstanceOf(Response); + + // ? Some time passes... + jest.advanceTimersByTime(5 * queue.intervalPeriodMs); + + const pReq2 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(2), sentinel.reject(2)); + + const pReq3 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(3), sentinel.reject(3)); + + const pReq4 = queue + .addRequestToQueue('https://fake-url') + .then(sentinel.resolve(4), sentinel.reject(4)); + + setThreshold(4); + jest.advanceTimersByTime(queue.intervalPeriodMs); + + await expect(pReq2).resolves.toBeInstanceOf(Response); + await expect(pReq3).resolves.toBeInstanceOf(Response); + await expect(pReq4).resolves.toBeInstanceOf(Response); + }); +}); + +describe('RequestQueue::intervalPeriodMs', () => { + it('determines the delay period between intervals', async () => { + expect.hasAssertions(); + + const queue = new RequestQueue({ + intervalPeriodMs: 10000, + maxRequestsPerInterval: 1 + }); + + queue.delayRequestProcessingByMs(1000); + queue.beginProcessingRequestQueue(); + + expect(mockSetTimeout).toBeCalledTimes(0); + + const [, sentinel] = promiseSettledSentinel(); + + void queue.addRequestToQueue('https://fake-url').then( + sentinel.resolve(1), + // ? Since this request will get sent, the halt below will abort it + sentinel.resolve(0) + ); + + jest.advanceTimersByTime(10000); + expect(mockSetTimeout).toBeCalledTimes(1); + + void queue.addRequestToQueue('https://fake-url'); + + // ? Wait for the current interval microtask to finish before continuing + await Promise.resolve().then(() => queue.delayRequestProcessingByMs(1000)); + + jest.advanceTimersByTime(2500); + expect(mockSetTimeout).toBeCalledTimes(1); + jest.advanceTimersByTime(2500); + expect(mockSetTimeout).toBeCalledTimes(1); + jest.advanceTimersByTime(5000); + expect(mockSetTimeout).toBeCalledTimes(2); + + queue.immediatelyStopProcessingRequestQueue(); + + queue.delayRequestProcessingByMs(1000); + queue.intervalPeriodMs = 500; + + queue.beginProcessingRequestQueue(); + + void queue.addRequestToQueue('https://fake-url'); + + jest.advanceTimersByTime(250); + expect(mockSetTimeout).toBeCalledTimes(2); + jest.advanceTimersByTime(250); + expect(mockSetTimeout).toBeCalledTimes(3); + }); +}); diff --git a/lint-staged.config.js b/lint-staged.config.js new file mode 100644 index 0000000..a022a6a --- /dev/null +++ b/lint-staged.config.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = { + '*.md': 'remark -o --use reference-links --use gfm --use frontmatter', + 'package.json': 'sort-package-json', + '*': 'prettier --write --ignore-unknown' +}; diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..3d646cf --- /dev/null +++ b/next.config.js @@ -0,0 +1,49 @@ +'use strict'; + +const withBundleAnalyzer = require('@next/bundle-analyzer'); +const { verifyEnvironment } = require('./expect-env'); + +verifyEnvironment(); + +module.exports = () => { + return withBundleAnalyzer({ + enabled: process.env.ANALYZE === 'true' + })({ + // ? Renames the build dir "build" instead of ".next" + distDir: 'build', + + // ? Select some environment variables defined in .env to push to the + // ? client. + // !! DO NOT PUT ANY SECRET ENVIRONMENT VARIABLES HERE !! + env: { + RESULTS_PER_PAGE: process.env.RESULTS_PER_PAGE, + IGNORE_RATE_LIMITS: process.env.IGNORE_RATE_LIMITS, + LOCKOUT_ALL_CLIENTS: process.env.LOCKOUT_ALL_CLIENTS, + DISALLOWED_METHODS: process.env.DISALLOWED_METHODS, + MAX_CONTENT_LENGTH_BYTES: process.env.MAX_CONTENT_LENGTH_BYTES + }, + + eslint: { + // ! This prevents production builds from failing in the presence of + // ! ESLint errors; linting is handled during CL/CI rather than at deploy + // ! time. + ignoreDuringBuilds: true + }, + + typescript: { + // ! This prevents production builds from failing in the presence of + // ! TypeScript errors, e.g. when modules from dev deps cannot be found; + // ! linting is handled during CL/CI rather than at deploy time. + ignoreBuildErrors: true + }, + + async rewrites() { + return [ + { + source: '/:path*', + destination: '/api/:path*' + } + ]; + } + }); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a678a91 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,37149 @@ +{ + "name": "blogpress.api.hscc.bdpa.org", + "version": "1.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "blogpress.api.hscc.bdpa.org", + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.18.10", + "@babel/plugin-proposal-export-default-from": "^7.18.10", + "@babel/plugin-proposal-function-bind": "^7.18.9", + "@babel/plugin-transform-react-jsx-source": "^7.18.6", + "@babel/preset-env": "^7.18.10", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@next/bundle-analyzer": "^12.2.4", + "@types/bytes": "^3.1.1", + "@types/cors": "^2.8.12", + "@types/debug": "^4.1.7", + "@types/node": "^18.6.5", + "@types/react": "^18.0.17", + "@types/request-ip": "^0.0.37", + "@xunnamius/next-types": "^1.0.9", + "@xunnamius/types": "^1.3.0", + "babel-plugin-explicit-exports-references": "^1.0.2", + "babel-plugin-transform-default-named-imports": "^1.2.2", + "bytes": "^3.1.2", + "clone-deep": "^4.0.1", + "content-type": "^1.0.4", + "cors": "^2.8.5", + "find-up": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-server-side": "^1.0.2", + "mongodb": "^4.8.1", + "named-app-errors": "^4.0.0", + "next": "^12.2.4", + "node-fetch": "cjs", + "raw-body": "^2.5.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-use": "^17.4.0", + "request-ip": "3.3.0", + "swr": "^1.3.0", + "toss-expression": "^0.1.1", + "typescript": "^4.7.4" + }, + "devDependencies": { + "@babel/cli": "^7.18.10", + "@babel/eslint-parser": "^7.18.9", + "@commitlint/cli": "^17.0.3", + "@commitlint/config-conventional": "^17.0.3", + "@next/eslint-plugin-next": "^12.2.4", + "@semantic-release/changelog": "^6.0.1", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.3.0", + "@types/clone-deep": "^4.0.1", + "@types/confusing-browser-globals": "^1.0.0", + "@types/content-type": "^1.1.5", + "@types/inquirer": "^9.0.0", + "@types/jest": "^28.1.6", + "@types/jsonfile": "^6.1.0", + "@types/node-fetch": "^2.5.12", + "@types/semver": "^7.3.10", + "@types/tar-stream": "^2.2.2", + "@types/test-listen": "^1.1.0", + "@types/webpack": "^5.28.0", + "@typescript-eslint/eslint-plugin": "^5.33.0", + "@typescript-eslint/parser": "^5.33.0", + "@xunnamius/conventional-changelog-projector": "^1.1.1", + "@xunnamius/jest-types": "^1.1.3", + "auto-bind": "^4.0.0", + "babel-jest": "^28.1.3", + "babel-loader": "^8.2.5", + "chalk": "^4.1.2", + "confusing-browser-globals": "^1.0.11", + "conventional-changelog-cli": "^2.2.2", + "dotenv": "^16.0.1", + "eslint": "^8.21.0", + "eslint-config-next": "^12.2.4", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-import-resolver-typescript": "^3.4.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jest": "^26.8.2", + "eslint-plugin-jest-dom": "^4.0.2", + "eslint-plugin-react": "^7.30.1", + "execa": "^5.1.1", + "html-entities": "^2.3.3", + "http-terminator": "^3.2.0", + "husky": "^8.0.1", + "inquirer": "^8.2.4", + "jest": "^28.1.3", + "jest-circus": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "jest-extended": "^3.0.2", + "jest-silent-reporter": "^0.5.0", + "jsonfile": "^6.1.0", + "lint-staged": "^13.0.3", + "mongodb-memory-server": "^8.8.0", + "msw": "^0.44.2", + "next-test-api-route-handler": "^3.1.7", + "prettier": "^2.7.1", + "random-case": "^1.0.0", + "remark-cli": "^11.0.0", + "remark-frontmatter": "^4.0.1", + "remark-gfm": "^3.0.1", + "remark-lint-final-newline": "^2.1.1", + "remark-lint-hard-break-spaces": "^3.1.1", + "remark-lint-no-auto-link-without-protocol": "^3.1.1", + "remark-lint-no-blockquote-without-marker": "^5.1.1", + "remark-lint-no-duplicate-definitions": "^3.1.1", + "remark-lint-no-heading-content-indent": "^4.1.1", + "remark-lint-no-inline-padding": "^4.1.1", + "remark-lint-no-undefined-references": "^4.1.1", + "remark-lint-no-unused-definitions": "^3.1.1", + "remark-lint-ordered-list-marker-style": "^3.1.1", + "remark-reference-links": "^6.0.1", + "remark-validate-links": "^12.0.0", + "semantic-release": "https://xunn.at/semantic-release-atam", + "simple-git": "^3.12.0", + "sort-package-json": "https://xunn.at/sort-package-json", + "spellchecker": "^3.7.1", + "test-listen": "^1.1.0", + "text-extensions": "^2.4.0", + "type-fest": "^2.18.0", + "typedoc": "^0.23.10", + "typedoc-plugin-markdown": "^3.13.4", + "unfetch": "^4.2.0", + "unique-filename": "^1.1.1", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0", + "webpack-node-externals": "^3.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/cli": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.18.10.tgz", + "integrity": "sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.8", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "dev": true, + "dependencies": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", + "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", + "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "dependencies": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.10.tgz", + "integrity": "sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-default-from": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz", + "integrity": "sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-default-from": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-function-bind": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.18.9.tgz", + "integrity": "sha512-9RfxqKkRBCCT0xoBl9AqieCMscJmSAL9HYixGMWH549jUpT9csWWK/HEYZEx9t9iW/PRSXgX95x9bDlgtAJGFA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-function-bind": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-default-from": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz", + "integrity": "sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-function-bind": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.18.6.tgz", + "integrity": "sha512-wZN0Aq/AScknI9mKGcR3TpHdASMufFGaeJgc1rhPmLtZ/PniwjePSh8cfh8tXMB3U4kh/3cRKrLjDtedejg8jQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz", + "integrity": "sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz", + "integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz", + "integrity": "sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz", + "integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", + "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.0.3.tgz", + "integrity": "sha512-oAo2vi5d8QZnAbtU5+0cR2j+A7PO8zuccux65R/EycwvsZrDVyW518FFrnJK2UQxbRtHFFIG+NjQ6vOiJV0Q8A==", + "dev": true, + "dependencies": { + "@commitlint/format": "^17.0.0", + "@commitlint/lint": "^17.0.3", + "@commitlint/load": "^17.0.3", + "@commitlint/read": "^17.0.0", + "@commitlint/types": "^17.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.0.3.tgz", + "integrity": "sha512-HCnzTm5ATwwwzNVq5Y57poS0a1oOOcd5pc1MmBpLbGmSysc4i7F/++JuwtdFPu16sgM3H9J/j2zznRLOSGVO2A==", + "dev": true, + "dependencies": { + "conventional-changelog-conventionalcommits": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.0.3.tgz", + "integrity": "sha512-3tLRPQJKapksGE7Kee9axv+9z5I2GDHitDH4q63q7NmNA0wkB+DAorJ0RHz2/K00Zb1/MVdHzhCga34FJvDihQ==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.0.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/ensure": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.0.0.tgz", + "integrity": "sha512-M2hkJnNXvEni59S0QPOnqCKIK52G1XyXBGw51mvh7OXDudCmZ9tZiIPpU882p475Mhx48Ien1MbWjCP1zlyC0A==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.0.0", + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz", + "integrity": "sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/format": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.0.0.tgz", + "integrity": "sha512-MZzJv7rBp/r6ZQJDEodoZvdRM0vXu1PfQvMTNWFb8jFraxnISMTnPBWMMjr2G/puoMashwaNM//fl7j8gGV5lA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.0.0", + "chalk": "^4.1.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.0.3.tgz", + "integrity": "sha512-/wgCXAvPtFTQZxsVxj7owLeRf5wwzcXLaYmrZPR4a87iD4sCvUIRl1/ogYrtOyUmHwWfQsvjqIB4mWE/SqWSnA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.0.0", + "semver": "7.3.7" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/lint": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.0.3.tgz", + "integrity": "sha512-2o1fk7JUdxBUgszyt41sHC/8Nd5PXNpkmuOo9jvGIjDHzOwXyV0PSdbEVTH3xGz9NEmjohFHr5l+N+T9fcxong==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^17.0.3", + "@commitlint/parse": "^17.0.0", + "@commitlint/rules": "^17.0.0", + "@commitlint/types": "^17.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/load": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.0.3.tgz", + "integrity": "sha512-3Dhvr7GcKbKa/ey4QJ5MZH3+J7QFlARohUow6hftQyNjzoXXROm+RwpBes4dDFrXG1xDw9QPXA7uzrOShCd4bw==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^17.0.3", + "@commitlint/execute-rule": "^17.0.0", + "@commitlint/resolve-extends": "^17.0.3", + "@commitlint/types": "^17.0.0", + "@types/node": ">=12", + "chalk": "^4.1.0", + "cosmiconfig": "^7.0.0", + "cosmiconfig-typescript-loader": "^2.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "typescript": "^4.6.4" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/message": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.0.0.tgz", + "integrity": "sha512-LpcwYtN+lBlfZijHUdVr8aNFTVpHjuHI52BnfoV01TF7iSLnia0jttzpLkrLmI8HNQz6Vhr9UrxDWtKZiMGsBw==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/parse": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.0.0.tgz", + "integrity": "sha512-cKcpfTIQYDG1ywTIr5AG0RAiLBr1gudqEsmAGCTtj8ffDChbBRxm6xXs2nv7GvmJN7msOt7vOKleLvcMmRa1+A==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.0.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.2.2" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/read": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.0.0.tgz", + "integrity": "sha512-zkuOdZayKX3J6F6mPnVMzohK3OBrsEdOByIqp4zQjA9VLw1hMsDEFQ18rKgUc2adkZar+4S01QrFreDCfZgbxA==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^17.0.0", + "@commitlint/types": "^17.0.0", + "fs-extra": "^10.0.0", + "git-raw-commits": "^2.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.0.3.tgz", + "integrity": "sha512-H/RFMvrcBeJCMdnVC4i8I94108UDccIHrTke2tyQEg9nXQnR5/Hd6MhyNWkREvcrxh9Y+33JLb+PiPiaBxCtBA==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^17.0.3", + "@commitlint/types": "^17.0.0", + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/rules": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.0.0.tgz", + "integrity": "sha512-45nIy3dERKXWpnwX9HeBzK5SepHwlDxdGBfmedXhL30fmFCkJOdxHyOJsh0+B0RaVsLGT01NELpfzJUmtpDwdQ==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^17.0.0", + "@commitlint/message": "^17.0.0", + "@commitlint/to-lines": "^17.0.0", + "@commitlint/types": "^17.0.0", + "execa": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.0.0.tgz", + "integrity": "sha512-nEi4YEz04Rf2upFbpnEorG8iymyH7o9jYIVFBG1QdzebbIFET3ir+8kQvCZuBE5pKCtViE4XBUsRZz139uFrRQ==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/top-level": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.0.0.tgz", + "integrity": "sha512-dZrEP1PBJvodNWYPOYiLWf6XZergdksKQaT6i1KSROLdjf5Ai0brLOv5/P+CPxBeoj3vBxK4Ax8H1Pg9t7sHIQ==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.0.0.tgz", + "integrity": "sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "dependencies": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "dependencies": { + "jest-get-type": "^28.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, + "node_modules/@mswjs/cookies": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", + "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "dev": true, + "dependencies": { + "@types/set-cookie-parser": "^2.4.0", + "set-cookie-parser": "^2.4.6" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.3.tgz", + "integrity": "sha512-jBRFPeHBPqKv3od8KPjmrvt4b/+e1DorizFDYJ8NQCrjFT9YGnxA8ojGi0MIo64x/JgdjYkhP8bG9EY4BGPoqg==", + "dev": true, + "dependencies": { + "@open-draft/until": "^1.0.3", + "@types/debug": "^4.1.7", + "@xmldom/xmldom": "^0.7.5", + "debug": "^4.3.3", + "headers-polyfill": "^3.0.4", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.2.4", + "web-encoding": "^1.1.5" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@next/bundle-analyzer": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-12.2.4.tgz", + "integrity": "sha512-7kyv8UBSq85wODeqemHJrvc1eg7He+oyA04XTKL8gUvE50uKlIpCh5PXP4ko4yphhWADGq8LeFOZXLIfbc7Fdg==", + "dependencies": { + "webpack-bundle-analyzer": "4.3.0" + } + }, + "node_modules/@next/env": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.2.4.tgz", + "integrity": "sha512-/gApFXWk5CCLFQJL5IYJXxPQuG5tz5nPX4l27A9Zm/+wJxiwFrRSP54AopDxIv4JRp/rGwcgk/lZS/0Clw8jYA==" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.4.tgz", + "integrity": "sha512-ChDkUIkJeYWKRx+FdF+EhUgvKtK1wF+Xew4Os7ef3iAjMch5GGBiezw2zGXTa/C0E6potz4j11EpX89mngffug==", + "dev": true, + "dependencies": { + "glob": "7.1.7" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.4.tgz", + "integrity": "sha512-P4YSFNpmXXSnn3P1qsOAqz+MX3On9fHrlc8ovb/CFJJoU+YLCR53iCEwfw39e0IZEgDA7ttgr108plF8mxaX0g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.4.tgz", + "integrity": "sha512-4o2n14E18O+8xHlf6dgJsWPXN9gmSmfIe2Z0EqKDIPBBkFt/2CyrH0+vwHnL2l7xkDHhOGfZYcYIWVUR5aNu0A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.4.tgz", + "integrity": "sha512-DcUO6MGBL9E3jj5o86MUnTOy4WawIJJhyCcFYO4f51sbl7+uPIYIx40eo98A6NwJEXazCqq1hLeqOaNTAIvDiQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.4.tgz", + "integrity": "sha512-IUlFMqeLjdIzDorrGC2Dt+2Ae3DbKQbRzCzmDq4/CP1+jJGeDXo/2AHnlE+WYnwQAC4KtAz6pbVnd3KstZWsVA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-freebsd-x64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.4.tgz", + "integrity": "sha512-475vwyWcjnyDVDWLgAATP0HI8W1rwByc+uXk1B6KkAVFhkoDgH387LW0uNqxavK+VxCzj3avQXX/58XDvxtSlg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.4.tgz", + "integrity": "sha512-qZW+L3iG3XSGtlOPmD5RRWXyk6ZNdscLV0BQjuDvP+exTg+uixqHXOHz0/GVATIJEBQOF0Kew7jAXVXEP+iRTQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.4.tgz", + "integrity": "sha512-fEPRjItWYaKyyG9N+2HIA59OBHIhk7WC+Rh+LwXsh0pQe870Ykpek3KQs0umjsrEGe57NyMomq3f80/N8taDvA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.4.tgz", + "integrity": "sha512-rnCTzXII0EBCcFn9P5s/Dho2kPUMSX/bP0iOAj8wEI/IxUEfEElbin89zJoNW30cycHu19xY8YP4K2+hzciPzQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.4.tgz", + "integrity": "sha512-PhXX6NSuIuhHInxPY2VkG2Bl7VllsD3Cjx+pQcS1wTym7Zt7UoLvn05PkRrkiyIkvR+UXnqPUM3TYiSbnemXEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.4.tgz", + "integrity": "sha512-GmC/QROiUZpFirHRfPQqMyCXZ+5+ndbBZrMvL74HtQB/CKXB8K1VM+rvy9Gp/5OaU8Rxp48IcX79NOfI2LiXlA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.4.tgz", + "integrity": "sha512-9XKoCXbNZuaMRPtcKQz3+hgVpkMosaLlcxHFXT8/j4w61k7/qvEbrkMDS9WHNrD/xVcLycwhPRgXcns2K1BdBQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.4.tgz", + "integrity": "sha512-hEyRieZKH9iw4AzvXaQ+Fyb98k0G/o9QcRGxA1/O/O/elf1+Qvuwb15phT8GbVtIeNziy66XTPOhKKfdr8KyUg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.4.tgz", + "integrity": "sha512-5Pl1tdMJWLy4rvzU1ecx0nHWgDPqoYuvYoXE/5X0Clu9si/yOuBIj573F2kOTY7mu0LX2wgCJVSnyK0abHBxIw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/config": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/config/-/config-4.2.0.tgz", + "integrity": "sha512-imWNz5dNWb2u+y41jyxL2WB389tkhu3a01Rchn16O/ur6GrnKySgOqdNG3N/9Z+mqxdISMEGKXI/POCauzz0dA==", + "dev": true, + "dependencies": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/config/node_modules/ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/config/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.3.tgz", + "integrity": "sha512-X6suAun5QyupNM8iHkNPh0AHdRC2rb1W+MTdMvvA/2ixgmqZwlq5cGUBgmKHUHT2LgrkKJMAXbfAoTxOigpK8Q==", + "dev": true, + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", + "dev": true + }, + "node_modules/@octokit/auth-token": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.0.tgz", + "integrity": "sha512-MDNFUBcJIptB9At7HiV7VCvU3NcL4GnfCQaP8C5lrxWrRPMJBnemYtehaKSOlaM7AYxeRyj9etenu8LVpSpVaQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.4.tgz", + "integrity": "sha512-sUpR/hc4Gc7K34o60bWC7WUH6Q7T6ftZ2dUmepSyJr9PRF76/qqkWjE2SOEzCqLA5W83SaISymwKtxks+96hPQ==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/endpoint": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.0.tgz", + "integrity": "sha512-Kz/mIkOTjs9rV50hf/JK9pIDl4aGwAtT8pry6Rpy+hVXkAPhXanNQRxMoq6AeRgDCZR6t/A1zKniY2V1YhrzlQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/graphql": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.0.tgz", + "integrity": "sha512-1ZZ8tX4lUEcLPvHagfIVu5S2xpHYXAmgN0+95eAOPoaVPzCfUXJtA5vASafcpWcO86ze0Pzn30TAx72aB2aguQ==", + "dev": true, + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.10.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz", + "integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.0.0.tgz", + "integrity": "sha512-fvw0Q5IXnn60D32sKeLIxgXCEZ7BTSAjJd8cFAE6QU5qUp0xo7LjFUjjX1J5D7HgN355CN4EXE4+Q1/96JaNUA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.39.0" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.1.2.tgz", + "integrity": "sha512-sAfSKtLHNq0UQ2iFuI41I6m5SK6bnKFRJ5kUjDRVbmQXiRVi4aQiIcgG4cM7bt+bhSiWL4HwnTxDkWFlKeKClA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.40.0", + "deprecation": "^2.3.1" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.0.tgz", + "integrity": "sha512-7IAmHnaezZrgUqtRShMlByJK33MT9ZDnMRgZjnRrRV9a/jzzFwKGz0vxhFU6i7VMLraYcQ1qmcAOin37Kryq+Q==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.0.tgz", + "integrity": "sha512-WBtpzm9lR8z4IHIMtOqr6XwfkGvMOOILNLxsWvDwtzm/n7f5AWuqJTXQXdDtOvPfTDrH4TPhEvW2qMlR4JFA2w==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/rest": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", + "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", + "dev": true, + "dependencies": { + "@octokit/core": "^4.0.0", + "@octokit/plugin-paginate-rest": "^3.0.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/types": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz", + "integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^12.10.0" + } + }, + "node_modules/@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, + "node_modules/@pkgr/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-7dIJ9CRVzBnqyEl7diUHPUFJf/oty2SeoVzcMocc5PeOUDK9KGzvgIBjGRRzzlRDaOjh3ADwH0WeibQvi3ls2Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==", + "dev": true + }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.1.tgz", + "integrity": "sha512-FT+tAGdWHr0RCM3EpWegWnvXJ05LQtBkQUaQRIExONoXjVjLuOILNm4DEKNaV+GAQyJjbLRVs57ti//GypH6PA==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/changelog/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", + "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "import-from": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/exec": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz", + "integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "parse-json": "^5.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/github": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.0.5.tgz", + "integrity": "sha512-9pGxRM3gv1hgoZ/muyd4pWnykdIUVfCiev6MXE9lOyGQof4FQy95GFE26nDcifs9ZG7bBzV8ue87bo/y1zVf0g==", + "dev": true, + "dependencies": { + "@octokit/rest": "^19.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^10.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^3.0.0", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", + "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", + "dev": true + }, + "node_modules/@semantic-release/npm": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.1.tgz", + "integrity": "sha512-I5nVZklxBzfMFwemhRNbSrkiN/dsH3c7K9+KSk6jUnq0rdLFUuJt7EBsysq4Ir3moajQgFkfEryEHPqiKJj20g==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^6.0.0", + "npm": "^8.3.0", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "engines": { + "node": ">=16 || ^14.17" + }, + "peerDependencies": { + "semantic-release": ">=19.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/@semantic-release/npm/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/@semantic-release/npm/node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@semantic-release/npm/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", + "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "get-stream": "^6.0.0", + "import-from": "^4.0.0", + "into-stream": "^6.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.20", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz", + "integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.3.tgz", + "integrity": "sha512-6JrF+fdUK2zbGpJIlN7G3v966PQjyx/dPt1T9km2wj+EUBqgrxCk3uX4Kct16MIm9gGxfKRcfax2hVf5jvlTzA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz", + "integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==" + }, + "node_modules/@types/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-bdkCSkyVHsgl3Goe1y16T9k6JuQx7SiDREkq728QjKmTZkGJZuS8R3gGcnGzVuGBP0mssKrzM/GlMOQxtip9cg==", + "dev": true + }, + "node_modules/@types/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/confusing-browser-globals": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/confusing-browser-globals/-/confusing-browser-globals-1.0.0.tgz", + "integrity": "sha512-2RQF2SJW3pAQgkHtlIefyxiRkdlBwXxqNiiDDoPyaR9R0SqIujHoP6ceLSAE95EKd7CCLV3F/n4ohOAMi5edpQ==", + "dev": true + }, + "node_modules/@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/inquirer": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.0.tgz", + "integrity": "sha512-4sncHTq3AWbLWZOs83DQlrKxhdehs2AW79Dp1amoGGc6B+oJal7fQJa/EO1Taa5CQLSywEMAgw2SFB1fZIX3PQ==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "node_modules/@types/is-empty": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/is-empty/-/is-empty-1.2.1.tgz", + "integrity": "sha512-a3xgqnFTuNJDm1fjsTjHocYJ40Cz3t8utYpi5GNaxzrJC2HSD08ym+whIL7fNqiqBCdM9bcqD1H/tORWAFXoZw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "28.1.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.6.tgz", + "integrity": "sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "node_modules/@types/js-levenshtein": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz", + "integrity": "sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==", + "dev": true + }, + "node_modules/@types/jsdom": { + "version": "16.2.15", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.15.tgz", + "integrity": "sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/parse5": "^6.0.3", + "@types/tough-cookie": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-zQPywzif9EycCkvECjYT9dbbttT0dkk657zcLb/803ZOXHsBA963jzEPF/Jnh1zOdBbgFJvUE8kcvZverAoK1w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "18.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz", + "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.4.tgz", + "integrity": "sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/react": { + "version": "18.0.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", + "integrity": "sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", + "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/request-ip": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@types/request-ip/-/request-ip-0.0.37.tgz", + "integrity": "sha512-uw6/i3rQnpznxD7LtLaeuZytLhKZK6bRoTS6XVJlwxIOoOpEBU7bgKoVXDNtOg4Xl6riUKHa9bjMVrL6ESqYlQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/semver": { + "version": "7.3.10", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", + "integrity": "sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==", + "dev": true + }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz", + "integrity": "sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", + "dev": true + }, + "node_modules/@types/tar-stream": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-2.2.2.tgz", + "integrity": "sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/test-listen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/test-listen/-/test-listen-1.1.0.tgz", + "integrity": "sha512-y6ZfbSzYHniCeY6ZAzsQjSAdJInNVoEz4Uhsb81W+RCoNYA59yoG/+XbqPqCPj2KCU3Wa6RFWSozutkGIHIsNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dev": true, + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/text-table": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/text-table/-/text-table-0.2.2.tgz", + "integrity": "sha512-dGoI5Af7To0R2XE8wJuc6vwlavWARsCh3UKJPjWs1YEqGUqfgBI/j/4GX0yf19/DsDPPf0YAXWAp8psNeIehLg==", + "dev": true + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true + }, + "node_modules/@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "node_modules/@types/webpack": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", + "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz", + "integrity": "sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/type-utils": "5.33.0", + "@typescript-eslint/utils": "5.33.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.33.0.tgz", + "integrity": "sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/typescript-estree": "5.33.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz", + "integrity": "sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/visitor-keys": "5.33.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz", + "integrity": "sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.33.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.33.0.tgz", + "integrity": "sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz", + "integrity": "sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/visitor-keys": "5.33.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.33.0.tgz", + "integrity": "sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/typescript-estree": "5.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz", + "integrity": "sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.33.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz", + "integrity": "sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@xunnamius/conventional-changelog-projector": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@xunnamius/conventional-changelog-projector/-/conventional-changelog-projector-1.1.1.tgz", + "integrity": "sha512-YFhr0ChPrvoCg1E2lxdZoWti+MzYnl9jXEg6qtUsia75/P1ugeOpyZaqG9kEBTGV/tx7v1YFCu5zz68Ppj1z8g==", + "deprecated": "this package has been superseded by @projector-js/config-conventional-changelog", + "dev": true, + "dependencies": { + "assign-deep": "^1.0.1", + "debug": "^4.3.2", + "is-plain-object": "^5.0.0", + "semver": "^7.3.5", + "toss-expression": "^0.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@xunnamius/conventional-changelog-projector/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@xunnamius/jest-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@xunnamius/jest-types/-/jest-types-1.1.3.tgz", + "integrity": "sha512-htMyGTIO5tWcJKCksqCsvMZDQShdyqJPLiIl/TdgVdxf4ZD3Zu9Zv3pb0z7DNQsc3oAL4lgYgmdNL9f6XR6Ufw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@types/jest": ">=27", + "@xunnamius/types": ">=1" + } + }, + "node_modules/@xunnamius/next-types": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@xunnamius/next-types/-/next-types-1.0.9.tgz", + "integrity": "sha512-6g3MYhaxy5+Cqm3La7XphqntcAsUrqGm28AUctndcilHgk3Tbtf/J984QiHH7X1upjZLur9jo3Lg0DsCQqG1ig==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "next": ">=9" + } + }, + "node_modules/@xunnamius/types": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@xunnamius/types/-/types-1.3.0.tgz", + "integrity": "sha512-RAWA75sRcOsZFROjNPz5+WPeSwJj/T751iNUeT+uVmV4f9tlN+cgiV+A6yUxUPEL5NR6pTzTv8ZwZ/HU78+TcQ==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "type-fest": ">=2.8" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dev": true, + "optional": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-deep": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/assign-deep/-/assign-deep-1.0.1.tgz", + "integrity": "sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA==", + "dev": true, + "dependencies": { + "assign-symbols": "^2.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/assign-symbols": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-2.0.2.tgz", + "integrity": "sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-mutex": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", + "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/auto-bind": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", + "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "node_modules/babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-explicit-exports-references": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-explicit-exports-references/-/babel-plugin-explicit-exports-references-1.0.2.tgz", + "integrity": "sha512-z+weAyF11Mr1azXIR5pfhAeXeK8ZvacXSKgPLGdwBoR7efyqnUxYvlGUny7eHZxAO/Q1C2O1+xO9lwgGPDaBlw==", + "dependencies": { + "@babel/core": "^7.13.15", + "@babel/template": "^7.12.13", + "@babel/types": "^7.13.14", + "debug": "^4.3.1" + }, + "engines": { + "node": ">= 12.x" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-transform-default-named-imports": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-default-named-imports/-/babel-plugin-transform-default-named-imports-1.2.2.tgz", + "integrity": "sha512-xLfgaSMqNEgGhcCIXvSqdWjbcucLPDSNUmVHBYyLLWENLDJ3uOOBRFWyHCItzT727OQeMbpML8DN8ntUS9loSw==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/types": "^7.16.0", + "webpack-node-module-types": "^1.2.1" + }, + "engines": { + "node": ">= 12.x" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz", + "integrity": "sha512-uqrgcjyOaZsHfz7ea8zLRCLe1u+QGUSzMZmvXqO24CDW7DWoW1qiN9folSwa7hSneTSgM2ykDIzF5kcQQ8cwNw==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001370", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz", + "integrity": "sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", + "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-cli": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.2.2.tgz", + "integrity": "sha512-8grMV5Jo8S0kP3yoMeJxV2P5R6VJOqK72IiSV9t/4H5r/HiRqEBQ83bYGuz4Yzfdj4bjaAEhZN/FFbsFXr5bOA==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog": "^3.1.24", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "tempfile": "^3.0.0" + }, + "bin": { + "conventional-changelog": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", + "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", + "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^4.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "dev": true, + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog/node_modules/conventional-changelog-conventionalcommits": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz", + "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==", + "dependencies": { + "browserslist": "^4.21.2", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.0.tgz", + "integrity": "sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", + "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7", + "ts-node": "^10.8.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "typescript": ">=3" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "dependencies": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "dev": true + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.202", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.202.tgz", + "integrity": "sha512-JYsK2ex9lmQD27kj19fhXYxzFJ/phLAkLKHv49A5UY6kMRV2xED3qMMLg/voW/+0AR6wMiI+VxlmK9NDtdxlPA==" + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-ci": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz", + "integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "fromentries": "^1.3.2", + "java-properties": "^1.0.0" + }, + "engines": { + "node": ">=10.17" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.2.4.tgz", + "integrity": "sha512-r3keSLY1Z+rN+ASN8nmWwZ+AsMl6IrPGRWgbQhKHcop4/fk1hJGxE9Xf/mYMkV07+1Q/catchw25lu525HFy5Q==", + "dev": true, + "dependencies": { + "@next/eslint-plugin-next": "12.2.4", + "@rushstack/eslint-patch": "^1.1.3", + "@typescript-eslint/parser": "^5.21.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.5.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-alias": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz", + "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==", + "dev": true, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "eslint-plugin-import": ">=1.4.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.4.0.tgz", + "integrity": "sha512-rBCgiEovwX/HQ8ESWV+XIWZaFiRtDeAXNZdcTATB8UbMuadc9qfGOlIP+vy+c7nsgfEBN4NTwy5qunGNptDP0Q==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.9.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.1" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/eslint-plugin-jest": { + "version": "26.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.8.2.tgz", + "integrity": "sha512-67oh0FKaku9y48OpLzL3uK9ckrgLb83Sp5gxxTbtOGDw9lq6D8jw/Psj/9CipkbK406I2M7mvx1q+pv/MdbvxA==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jest-dom": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest-dom/-/eslint-plugin-jest-dom-4.0.2.tgz", + "integrity": "sha512-Jo51Atwyo2TdcUncjmU+UQeSTKh3sc2LF/M5i/R3nTU0Djw9V65KGJisdm/RtuKhy2KH/r7eQ1n6kwYFPNdHlA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.3", + "@testing-library/dom": "^8.11.1", + "requireindex": "^1.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "eslint": "^6.8.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz", + "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-json-stringify": { + "version": "2.7.13", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz", + "integrity": "sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==", + "dev": true, + "dependencies": { + "ajv": "^6.11.0", + "deepmerge": "^4.2.2", + "rfdc": "^1.2.0", + "string-similarity": "^4.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dev": true, + "dependencies": { + "boolean": "^3.1.4" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz", + "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "dev": true, + "dependencies": { + "semver-regex": "^3.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/get-pkg-repo/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/get-pkg-repo/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", + "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/git-hooks-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", + "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", + "dev": true, + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", + "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", + "dev": true, + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + } + }, + "node_modules/git-log-parser/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/git-log-parser/node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/git-log-parser/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/git-log-parser/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "dependencies": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/github-slugger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphql": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", + "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/headers-polyfill": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.0.10.tgz", + "integrity": "sha512-lOhQU7iG3AMcjmb8NIWCa+KwfJw5bY44BoWPtrj5A4iDbSD3ylGf5QcYr0ZyQnhkKQ2GgWNLdF2rfrXtXlF3nQ==", + "dev": true + }, + "node_modules/hook-std": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", + "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-terminator": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/http-terminator/-/http-terminator-3.2.0.tgz", + "integrity": "sha512-JLjck1EzPaWjsmIf8bziM3p9fgR1Y3JoUKAkyYEbZmFrIvJM6I8vVJfBGWlEtV9IWOvzNnaTtjuwZeBY2kwB4g==", + "dev": true, + "dependencies": { + "delay": "^5.0.0", + "p-wait-for": "^3.2.0", + "roarr": "^7.0.4", + "type-fest": "^2.3.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", + "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true, + "engines": { + "node": ">=12.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-2.0.3.tgz", + "integrity": "sha512-fpAppnBpZ3ymQ/dPP97TNsco1HB5+V9SYJ3chY50PP8xn4U/w+Y6ovWBmTImB/prmGsTjzPh8pQYY+EVBlr9mw==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-style-prefixer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz", + "integrity": "sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ==", + "dependencies": { + "css-in-js-utils": "^2.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/inquirer/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dev": true, + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-empty": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", + "integrity": "sha512-F2FnH/otLNJv0J6wc73A5Xo7oHLNnqplYqZhUu01tD54DIPvxIRSTSLkrUB/M0nHO4vo1O9PDfN4KoTxCzLh/w==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-node-process": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.0.1.tgz", + "integrity": "sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-server-side": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-server-side/-/is-server-side-1.0.2.tgz", + "integrity": "sha512-AazkGxiRd+xwIv0OYcvxHIITZccolSEPqVDOxmmApqtEGYX4G+7sucsVUs9CAwcVW+RIRsRNvqr3u7L8vlHdHg==", + "deprecated": "this package has been superseded by the @xunnamius/next-is-server-side package" + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-text-path/node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-circus/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-config/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-environment-jsdom": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz", + "integrity": "sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/jsdom": "^16.2.4", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3", + "jsdom": "^19.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-extended": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.2.tgz", + "integrity": "sha512-LnVZvwWLRV9AL8J7f4frKu0KHuTrbIFK15IqrvSwbFCYxalkuC5l7HfcofsksePrvlEJ2WAcfYNnu1+bEGvInA==", + "dev": true, + "dependencies": { + "jest-diff": "^28.0.0", + "jest-get-type": "^28.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "jest": ">=27.2.5" + } + }, + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-silent-reporter": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jest-silent-reporter/-/jest-silent-reporter-0.5.0.tgz", + "integrity": "sha512-epdLt8Oj0a1AyRiR6F8zx/1SVT1Mi7VU3y4wB2uOBHs/ohIquC7v2eeja7UN54uRPyHInIKWdL+RdG228n5pJQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-util": "^26.0.0" + } + }, + "node_modules/jest-silent-reporter/node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-silent-reporter/node_modules/@types/yargs": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-silent-reporter/node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz", + "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dev": true, + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levenshtein-edit-distance": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/levenshtein-edit-distance/-/levenshtein-edit-distance-1.0.0.tgz", + "integrity": "sha512-gpgBvPn7IFIAL32f0o6Nsh2g+5uOvkt4eK9epTfgE4YVxBxwVhJ/p1888lMm/u8mXdu1ETLSi6zeEmkBI+0F3w==", + "dev": true, + "bin": { + "levenshtein-edit-distance": "cli.js" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", + "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.17", + "commander": "^9.3.0", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.5", + "listr2": "^4.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-5.0.0.tgz", + "integrity": "sha512-jTz8tvC0BTMtof27lTSV5SAOnCRT0Z++k+S3QeQ5CrF8ZAS5L2nhi3euf4ZhJyDkds+nOQGyPcFqdQZ9s8ELkg==", + "dev": true, + "dependencies": { + "@npmcli/config": "^4.0.0", + "import-meta-resolve": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/longest-streak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.1.tgz", + "integrity": "sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz", + "integrity": "sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/marked-terminal": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.1.1.tgz", + "integrity": "sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g==", + "dev": true, + "dependencies": { + "ansi-escapes": "^5.0.0", + "cardinal": "^2.1.1", + "chalk": "^5.0.0", + "cli-table3": "^0.6.1", + "node-emoji": "^1.11.0", + "supports-hyperlinks": "^2.2.0" + }, + "engines": { + "node": ">=14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/marked-terminal/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", + "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==", + "dev": true, + "bin": { + "md5-file": "cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.1.tgz", + "integrity": "sha512-SobxkQXFAdd4b5WmEakmkVoh18icjQRxGy5OWTCzgsLRm1Fu/KCtwD1HIQSsmq5ZRjVH0Ehwg6/Fn3xIUk+nKw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", + "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.0.tgz", + "integrity": "sha512-7itKvp0arEVNpCktOET/eLFAYaZ+0cNjVtFtIPxgQ5tV+3i+D4SDDTjTzPWl44LT59PC+xdx+glNTawBdF98Mw==", + "dev": true, + "dependencies": { + "micromark-extension-frontmatter": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-gfm-autolink-literal": "^1.0.0", + "mdast-util-gfm-footnote": "^1.0.0", + "mdast-util-gfm-strikethrough": "^1.0.0", + "mdast-util-gfm-table": "^1.0.0", + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.2.tgz", + "integrity": "sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "ccount": "^2.0.0", + "mdast-util-find-and-replace": "^2.0.0", + "micromark-util-character": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.1.tgz", + "integrity": "sha512-p+PrYlkw9DeCRkTVw1duWqPRHX6Ywh2BNKJQcZbCwAuP/59B0Lk9kakuAd7KbQprVO4GzdW8eS5++A9PUSqIyw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-util-normalize-identifier": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.1.tgz", + "integrity": "sha512-zKJbEPe+JP6EUv0mZ0tQUyLQOC+FADt0bARldONot/nefuISkaZFlmVK4tU6JgfyZGrky02m/I6PmehgAgZgqg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", + "dev": true, + "dependencies": { + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.1.tgz", + "integrity": "sha512-KZ4KLmPdABXOsfnM6JHUIjxEvcx2ulk656Z/4Balw071/5qgnhz+H1uGtf2zIGnrnvDC8xR4Fj9uKbjAFGNIeA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-heading-style": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-heading-style/-/mdast-util-heading-style-2.0.0.tgz", + "integrity": "sha512-q9+WW2hJduW51LgV2r/fcU5wIt2GLFf0yYHxyi0f2aaxnC63ErBSOAJlhP6nbQ6yeG5rTCozbwOi4QNDPKV0zw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz", + "integrity": "sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", + "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/meow/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", + "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", + "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.0.0.tgz", + "integrity": "sha512-EXjmRnupoX6yYuUJSQhrQ9ggK0iQtQlpi6xeJzVD5xscyAI+giqco5fdymayZhJMbIFecjnE2yz85S9NzIgQpg==", + "dev": true, + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.1.tgz", + "integrity": "sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA==", + "dev": true, + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^1.0.0", + "micromark-extension-gfm-footnote": "^1.0.0", + "micromark-extension-gfm-strikethrough": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.0", + "micromark-extension-gfm-tagfilter": "^1.0.0", + "micromark-extension-gfm-task-list-item": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.3.tgz", + "integrity": "sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg==", + "dev": true, + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", + "dev": true, + "dependencies": { + "micromark-core-commonmark": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.4.tgz", + "integrity": "sha512-/vjHU/lalmjZCT5xt7CcHVJGq8sYRm80z24qAKXzaHzem/xsDYb2yLL+NNVbYvmpLx3O7SYPuGL5pzusL9CLIQ==", + "dev": true, + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz", + "integrity": "sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==", + "dev": true, + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.1.tgz", + "integrity": "sha512-Ty6psLAcAjboRa/UKUbbUcwjVAv5plxmpUTy2XC/3nJFL37eHej8jrHrRzkqcpipJliuBH30DTs7+3wqNcQUVA==", + "dev": true, + "dependencies": { + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.3.tgz", + "integrity": "sha512-PpysK2S1Q/5VXi72IIapbi/jliaiOFzv7THH4amwXeYXLq3l1uo8/2Be0Ac1rEwK20MQEsGH2ltAZLNY2KI/0Q==", + "dev": true, + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", + "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", + "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", + "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", + "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", + "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", + "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", + "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", + "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", + "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", + "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", + "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", + "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", + "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", + "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", + "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", + "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", + "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", + "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mongodb": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.1.tgz", + "integrity": "sha512-/NyiM3Ox9AwP5zrfT9TXjRKDJbXlLaUDQ9Rg//2lbg8D2A8GXV0VidYYnA/gfdK6uwbnL4FnAflH7FbGw3TS7w==", + "dependencies": { + "bson": "^4.6.5", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.5.2", + "socks": "^2.6.2" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "saslprep": "^1.0.3" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.3.tgz", + "integrity": "sha512-f+/WsED+xF4B74l3k9V/XkTVj5/fxFH2o5ToKXd8Iyi5UhM+sO9u0Ape17Mvl/GkZaFtM0HQnzAG5OTmhKw+tQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-memory-server": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-8.8.0.tgz", + "integrity": "sha512-1cy/N4RC7mH/T8Go6J/vbbOWg5d2YR3DZCnhrRJkhQMUQmEIdU62jqLTepViW7A2LKKweopkfb4PiKAFJV/Z1A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "mongodb-memory-server-core": "8.8.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/mongodb-memory-server-core": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-8.8.0.tgz", + "integrity": "sha512-pguLQes27cFhiqs/rRqINst1y2zCMGVNrVEREbvFtKaMTQUh40TU1XeOrVesqu1qYFGHYntYCOzlJueUtl62qQ==", + "dev": true, + "dependencies": { + "@types/tmp": "^0.2.3", + "async-mutex": "^0.3.2", + "camelcase": "^6.3.0", + "debug": "^4.3.4", + "find-cache-dir": "^3.3.2", + "get-port": "^5.1.1", + "https-proxy-agent": "^5.0.1", + "md5-file": "^5.0.0", + "mongodb": "~4.7.0", + "new-find-package-json": "^2.0.0", + "semver": "^7.3.7", + "tar-stream": "^2.1.4", + "tmp": "^0.2.1", + "tslib": "^2.4.0", + "uuid": "^8.3.1", + "yauzl": "^2.10.0" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.7.0.tgz", + "integrity": "sha512-HhVar6hsUeMAVlIbwQwWtV36iyjKd9qdhY+s4wcU8K6TOj4Q331iiMy+FoPuxEntDIijTYWivwFJkLv8q/ZgvA==", + "dev": true, + "dependencies": { + "bson": "^4.6.3", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.5.2", + "socks": "^2.6.2" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "saslprep": "^1.0.3" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/msw": { + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-0.44.2.tgz", + "integrity": "sha512-u8wjzzcMWouoZtuIShCwx4M3wFF5sBAV1f8K4a0WX8kiihFjzl89IKE1VYmTclLyMIwpOq8qQ1HTpuh2BFX/3A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@mswjs/cookies": "^0.2.2", + "@mswjs/interceptors": "^0.17.2", + "@open-draft/until": "^1.0.3", + "@types/cookie": "^0.4.1", + "@types/js-levenshtein": "^1.1.1", + "chalk": "4.1.1", + "chokidar": "^3.4.2", + "cookie": "^0.4.2", + "graphql": "^16.3.0", + "headers-polyfill": "^3.0.4", + "inquirer": "^8.2.0", + "is-node-process": "^1.0.1", + "js-levenshtein": "^1.1.6", + "node-fetch": "^2.6.7", + "outvariant": "^1.3.0", + "path-to-regexp": "^6.2.0", + "statuses": "^2.0.0", + "strict-event-emitter": "^0.2.0", + "type-fest": "^1.2.2", + "yargs": "^17.3.1" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.2.x <= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/named-app-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/named-app-errors/-/named-app-errors-4.0.0.tgz", + "integrity": "sha512-S8X3M3R9Ps1D0Cy8t/McPVxih9bX39QrX5wf2s3V1u6yubqTOlUYgLEcHQYgQxYmXRvqEwIUSGqenUBNIHlrmw==" + }, + "node_modules/nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "dev": true + }, + "node_modules/nano-css": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.5.tgz", + "integrity": "sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==", + "dependencies": { + "css-tree": "^1.1.2", + "csstype": "^3.0.6", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^6.0.0", + "rtl-css-js": "^1.14.0", + "sourcemap-codec": "^1.4.8", + "stacktrace-js": "^2.0.2", + "stylis": "^4.0.6" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true + }, + "node_modules/new-find-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", + "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-12.2.4.tgz", + "integrity": "sha512-b1xlxEozmAWokAXzXsi5vlmU/IfJcFNIJA8dpU5UdkFbyDPio8wwb8mAQ/Y7rGtfTgG/t/u49BiyEA+xAgFvow==", + "dependencies": { + "@next/env": "12.2.4", + "@swc/helpers": "0.4.3", + "caniuse-lite": "^1.0.30001332", + "postcss": "8.4.14", + "styled-jsx": "5.0.2", + "use-sync-external-store": "1.2.0" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=12.22.0" + }, + "optionalDependencies": { + "@next/swc-android-arm-eabi": "12.2.4", + "@next/swc-android-arm64": "12.2.4", + "@next/swc-darwin-arm64": "12.2.4", + "@next/swc-darwin-x64": "12.2.4", + "@next/swc-freebsd-x64": "12.2.4", + "@next/swc-linux-arm-gnueabihf": "12.2.4", + "@next/swc-linux-arm64-gnu": "12.2.4", + "@next/swc-linux-arm64-musl": "12.2.4", + "@next/swc-linux-x64-gnu": "12.2.4", + "@next/swc-linux-x64-musl": "12.2.4", + "@next/swc-win32-arm64-msvc": "12.2.4", + "@next/swc-win32-ia32-msvc": "12.2.4", + "@next/swc-win32-x64-msvc": "12.2.4" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^6.0.0 || ^7.0.0", + "react": "^17.0.2 || ^18.0.0-0", + "react-dom": "^17.0.2 || ^18.0.0-0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-test-api-route-handler": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/next-test-api-route-handler/-/next-test-api-route-handler-3.1.7.tgz", + "integrity": "sha512-ds4VH7TOMRY8iT0vAnOi88uXsIGk9r6Yf/VrZPGHuG1ceRuTHIN3pyzKaPtQO2MisHH4z9ZDXmGrh3NJgq56ag==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "next": ">=9" + } + }, + "node_modules/next-test-api-route-handler/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-8.15.0.tgz", + "integrity": "sha512-sFXrMiO07eDWUb/e5ni2yNvtz2hePKqSyukUxYcQv0QHjyXCe+zKP7af/bISjcvsgRBWGyivk5V3KCZ0vg8J3Q==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "archy", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^5.0.4", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/config": "^4.2.0", + "@npmcli/fs": "^2.1.0", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.1.7", + "abbrev": "~1.1.1", + "archy": "~1.0.0", + "cacache": "^16.1.1", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.2", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.12", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^5.0.0", + "ini": "^3.0.0", + "init-package-json": "^3.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^6.0.2", + "libnpmdiff": "^4.0.2", + "libnpmexec": "^4.0.2", + "libnpmfund": "^3.0.1", + "libnpmhook": "^8.0.2", + "libnpmorg": "^4.0.2", + "libnpmpack": "^4.0.2", + "libnpmpublish": "^6.0.2", + "libnpmsearch": "^5.0.2", + "libnpmteam": "^4.0.2", + "libnpmversion": "^3.0.1", + "make-fetch-happen": "^10.2.0", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^9.0.0", + "nopt": "^5.0.0", + "npm-audit-report": "^3.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.1.0", + "npm-pick-manifest": "^7.0.1", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.3.0", + "npm-user-validate": "^1.0.1", + "npmlog": "^6.0.2", + "opener": "^1.5.2", + "p-map": "^4.0.0", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.2", + "proc-log": "^2.0.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^5.0.1", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^2.0.0", + "validate-npm-package-name": "^4.0.0", + "which": "^2.0.2", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.0", + "cacache": "^16.0.6", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/ci-detect": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/move-file": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "4.1.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm/node_modules/agentkeepalive": { + "version": "4.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "16.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/dezalgo": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "8.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/ini": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/ip": { + "version": "1.1.8", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.9.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "5.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^2.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^5.0.1", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1", + "tar": "^6.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "4.0.8", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.0.0", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/run-script": "^4.1.3", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^9.0.1", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "proc-log": "^2.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "8.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "4.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^4.1.3", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "6.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "5.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "4.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "3.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/run-script": "^4.1.3", + "json-parse-even-better-errors": "^2.3.1", + "proc-log": "^2.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "7.12.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "10.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "3.3.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^1.1.2", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "13.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/opener": { + "version": "1.5.2", + "dev": true, + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "13.6.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.3.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.11", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.1.11", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/outvariant": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.3.0.tgz", + "integrity": "sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ==", + "dev": true + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-filter/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "dev": true, + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/propose": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/propose/-/propose-0.0.5.tgz", + "integrity": "sha512-Jary1vb+ap2DIwOGfyiadcK4x1Iu3pzpkDBy8tljFPmQvnc9ES3m1PMZOMiWOG50cfoAyYNtGeBzrp+Rlh4G9A==", + "dev": true, + "dependencies": { + "levenshtein-edit-distance": "^1.0.0" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/random-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-case/-/random-case-1.0.0.tgz", + "integrity": "sha512-bMlDjeg8GB38Ju0hK8eII3qPQucgFm+sFWCmucwnHlgeCqm99uhmwR3d+dU3sJV2Dn+bp7XnFUygUidrRbM4Vg==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "peerDependencies": { + "react": "*", + "tslib": "*" + } + }, + "node_modules/react-use": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.4.0.tgz", + "integrity": "sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.3.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "dev": true, + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remark": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz", + "integrity": "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "remark-parse": "^10.0.0", + "remark-stringify": "^10.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-11.0.0.tgz", + "integrity": "sha512-8JEWwArXquRq1/In4Ftz7gSG9Scwb1ijT2/dEuBETW9omqhmMRxcfjZ3iKqrak3BnCJeZSXCdWEmPhFKC8+RUQ==", + "dev": true, + "dependencies": { + "remark": "^14.0.0", + "unified-args": "^10.0.0" + }, + "bin": { + "remark": "cli.js" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", + "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-gfm": "^2.0.0", + "micromark-extension-gfm": "^2.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-final-newline": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-final-newline/-/remark-lint-final-newline-2.1.1.tgz", + "integrity": "sha512-cgKYaI7ujUse/kV4KajLv2j1kmi1CxpAu+w7wIU0/Faihhb3sZAf4a5ACf2Wu8NoTSIr1Q//3hDysG507PIoDg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-hard-break-spaces": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-hard-break-spaces/-/remark-lint-hard-break-spaces-3.1.1.tgz", + "integrity": "sha512-UfwFvESpX32qwyHJeluuUuRPWmxJDTkmjnWv2r49G9fC4Jrzm4crdJMs3sWsrGiQ3mSex6bgp/8rqDgtBng2IA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-auto-link-without-protocol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-auto-link-without-protocol/-/remark-lint-no-auto-link-without-protocol-3.1.1.tgz", + "integrity": "sha512-lCjBuoSUWjN1kO0J7vqQgn7HUF/WeOHOqc3oiq9LMRXIovKWqPCBi77o8Npv8KfV+JXeRl+hrupfbhJXlLturA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-blockquote-without-marker": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-blockquote-without-marker/-/remark-lint-no-blockquote-without-marker-5.1.1.tgz", + "integrity": "sha512-7jL7eKS25kKRhQ7SKKB5eRfNleDMWKWAmZ5Y/votJdDoM+6qsopLLumPWaSzP0onyV3dyHRhPfBtqelt3hvcyA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile-location": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-duplicate-definitions": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-duplicate-definitions/-/remark-lint-no-duplicate-definitions-3.1.1.tgz", + "integrity": "sha512-9p+nBz8VvV+t4g/ALNLVN8naV+ffAzC4ADyg9QivzmKwLjyF93Avt4HYNlb2GZ+aoXRQSVG1wjjWFeDC9c7Tdg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-heading-content-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-heading-content-indent/-/remark-lint-no-heading-content-indent-4.1.1.tgz", + "integrity": "sha512-W4zF7MA72IDC5JB0qzciwsnioL5XlnoE0r1F7sDS0I5CJfQtHYOLlxb3UAIlgRCkBokPWCp0E4o1fsY/gQUKVg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-heading-style": "^2.0.0", + "pluralize": "^8.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-inline-padding": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-inline-padding/-/remark-lint-no-inline-padding-4.1.1.tgz", + "integrity": "sha512-++IMm6ohOPKNOrybqjP9eiclEtVX/Rd2HpF2UD9icrC1X5nvrI6tlfN55tePaFvWAB7pe6MW4LzNEMnWse61Lw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-undefined-references": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-undefined-references/-/remark-lint-no-undefined-references-4.1.1.tgz", + "integrity": "sha512-J20rKfTGflLiTI3T5JlLZSmINk6aDGmZi1y70lpU69LDfAyHAKgDK6sSW9XDeFmCPPdm8Ybxe5Gf2a70k+GcVQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile-location": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-no-unused-definitions": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-unused-definitions/-/remark-lint-no-unused-definitions-3.1.1.tgz", + "integrity": "sha512-/GtyBukhAxi5MEX/g/m+FzDEflSbTe2/cpe2H+tJZyDmiLhjGXRdwWnPRDp+mB9g1iIZgVRCk7T4v90RbQX/mw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-lint-ordered-list-marker-style": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-ordered-list-marker-style/-/remark-lint-ordered-list-marker-style-3.1.1.tgz", + "integrity": "sha512-IWcWaJoaSb4yoSOuvDbj9B2uXp9kSj58DqtrMKo8MoRShmbj1onVfulTxoTLeLtI11NvW+mj3jPSpqjMjls+5Q==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", + "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reference-links": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-6.0.1.tgz", + "integrity": "sha512-34wY2C6HXSuKVTRtyJJwefkUD8zBOZOSHFZ4aSTnU2F656gr9WeuQ2dL6IJDK3NPd2F6xKF2t4XXcQY9MygAXg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz", + "integrity": "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-validate-links": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/remark-validate-links/-/remark-validate-links-12.0.0.tgz", + "integrity": "sha512-mBNfsGBPO3A4CYnHDd3fpnr+A2Ep71moc0UlxC6lhZSJslMAU3UZypKK3DLlxUPoLy9TlJaEVEnfy6qpGQU7sg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "github-slugger": "^1.0.0", + "hosted-git-info": "^5.0.0", + "mdast-util-to-string": "^3.0.0", + "propose": "0.0.5", + "to-vfile": "^7.0.0", + "trough": "^2.0.0", + "unified": "^10.0.0", + "unified-engine": "^10.0.1", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-validate-links/node_modules/hosted-git-info": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.0.0.tgz", + "integrity": "sha512-rRnjWu0Bxj+nIfUOkz0695C0H6tRrN5iYIzYejb0tDEefe2AekHu/U5Kn9pEie5vsJqpNQU02az7TGSH3qpz4Q==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/remark-validate-links/node_modules/lru-cache": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", + "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-7.11.0.tgz", + "integrity": "sha512-DKiMaEYHoOZ0JyD4Ohr5KRnqybQ162s3ZL/WNO9oy6EUszYvpp0eLYJErc/U4NI96HYnHsbROhFaH4LYuJPnDg==", + "dev": true, + "dependencies": { + "boolean": "^3.1.4", + "fast-json-stringify": "^2.7.10", + "fast-printf": "^1.6.9", + "fast-safe-stringify": "^2.1.1", + "globalthis": "^1.0.2", + "semver-compare": "^1.0.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/rtl-css-js": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.0.tgz", + "integrity": "sha512-Oc7PnzwIEU4M0K1J4h/7qUUaljXhQ0kCObRsZjxs2HjkpKsnoTMvSmvJ4sqgJZd0zBoEfAyTdnK/jMIYvrjySQ==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release": { + "version": "19.0.3", + "resolved": "https://xunn.at/semantic-release-atam", + "integrity": "sha512-vNB9WbhgxFqkPcZr6z8s2NcBb2g5d+cw3ReZWUx/lCdp1HQX6r33BJbejxIpojRz7i/5ukUubJ9NhFjX6sqwKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^9.0.2", + "@semantic-release/error": "^3.0.0", + "@semantic-release/github": "^8.0.0", + "@semantic-release/npm": "^9.0.0", + "@semantic-release/release-notes-generator": "^10.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^5.0.0", + "figures": "^3.0.0", + "find-versions": "^4.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^4.0.0", + "lodash": "^4.17.21", + "marked": "^4.0.10", + "marked-terminal": "^5.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^16.2.0" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=16 || ^14.17" + } + }, + "node_modules/semantic-release/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/semantic-release/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/semantic-release/node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/semantic-release/node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semantic-release/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semantic-release/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", + "dev": true + }, + "node_modules/set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "engines": { + "node": ">=6.9" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/simple-git": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.12.0.tgz", + "integrity": "sha512-cy1RSRFHGZSrlYa3MnUuNVOXLUdifEZD2X8+AZjg8mKCdRvtCFSga6acq5N2g0ggb8lH3jBi369MrFZ+Y6sfsA==", + "dev": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "node_modules/sort-package-json": { + "version": "2.3.0", + "resolved": "https://xunn.at/sort-package-json", + "integrity": "sha512-efTk1wTodMGOvLTM+HX2HveURBKttzewX+v/q0t0fHNecJLnbf2SIbYhQ16Z/r/+Em/+6Sphusi2QGTgWxzrEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "bin": { + "sort-package-json": "cli.js" + } + }, + "node_modules/sort-package-json/node_modules/detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spellchecker": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/spellchecker/-/spellchecker-3.7.1.tgz", + "integrity": "sha512-j36QRZrekxPXy58fo2B/Le3GzHryLv9Zq2Hqz907+JmUBCP35tJlwwhCo4n1lwisBDK40IFHqEHPUe5gwUkpwA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "any-promise": "^1.3.0", + "nan": "^2.14.0" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-combiner2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-combiner2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.4.tgz", + "integrity": "sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ==", + "dev": true, + "dependencies": { + "events": "^3.3.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-similarity": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", + "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.2.tgz", + "integrity": "sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==", + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylis": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz", + "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz", + "integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==", + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/synckit": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.1.tgz", + "integrity": "sha512-rJEeygO5PNmcZICmrgnbOd2usi5zWE1ESc0Gn5tTmJlongoU8zCTwMFQtar2UgMSiR68vK9afPQ+uVs2lURSIA==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempfile": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-3.0.0.tgz", + "integrity": "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==", + "dev": true, + "dependencies": { + "temp-dir": "^2.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempfile/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, + "dependencies": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-listen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-listen/-/test-listen-1.1.0.tgz", + "integrity": "sha512-OyEVi981C1sb9NX1xayfgZls3p8QTDRwp06EcgxSgd1kktaENBW8dO15i8v/7Fi15j0IYQctJzk5J+hyEBId2w==", + "dev": true + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-vfile": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-7.2.3.tgz", + "integrity": "sha512-QO0A9aE6Z/YkmQadJ0syxpmNXtcQiu0qAtCKYKD5cS3EfgfFTAXfgLX6AOaBrSfWSek5nfsMf3gBZ9KGVFcLuw==", + "dev": true, + "dependencies": { + "is-buffer": "^2.0.0", + "vfile": "^5.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toss-expression": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/toss-expression/-/toss-expression-0.1.1.tgz", + "integrity": "sha512-gcLSA2aZOjZgGkODu/Ac+ke1UiY5zcrfsajrEUra7AZzotbNEUOGHSKfhR/4l2Jf6gjy8PRxGy/G5/I4lm9aXQ==" + }, + "node_modules/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==", + "dev": true + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz", + "integrity": "sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typedoc": { + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", + "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.0.18", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.13.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.4.tgz", + "integrity": "sha512-E/EBBmu6ARtnbswZGtBVBB/BfukZiGMOlqPc0RXCI/NFitONBahFqbCAF5fKQlijlcfipJj5pw5AMFH3NytrAw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.23.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.3.tgz", + "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified-args": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/unified-args/-/unified-args-10.0.0.tgz", + "integrity": "sha512-PqsqxwkXpGSLiMkbjNnKU33Ffm6gso6rAvz1TlBGzMBx3gpx7ewIhViBX8HEWmy0v7pebA5PM6RkRWWaYmtfYw==", + "dev": true, + "dependencies": { + "@types/text-table": "^0.2.0", + "camelcase": "^7.0.0", + "chalk": "^5.0.0", + "chokidar": "^3.0.0", + "fault": "^2.0.0", + "json5": "^2.0.0", + "minimist": "^1.0.0", + "text-table": "^0.2.0", + "unified-engine": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified-args/node_modules/camelcase": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", + "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified-args/node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/unified-engine": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/unified-engine/-/unified-engine-10.0.1.tgz", + "integrity": "sha512-lsj7VC8kNWhK87rGBhidklk4llgrEdJoOZHoQFbTZQ/fA22JqowUPM10bEf05eSZOR6UnUSrZ/mPWHrQsHGm7g==", + "dev": true, + "dependencies": { + "@types/concat-stream": "^2.0.0", + "@types/debug": "^4.0.0", + "@types/is-empty": "^1.0.0", + "@types/node": "^18.0.0", + "@types/unist": "^2.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.0.0", + "fault": "^2.0.0", + "glob": "^8.0.0", + "ignore": "^5.0.0", + "is-buffer": "^2.0.0", + "is-empty": "^1.0.0", + "is-plain-obj": "^4.0.0", + "load-plugin": "^5.0.0", + "parse-json": "^6.0.0", + "to-vfile": "^7.0.0", + "trough": "^2.0.0", + "unist-util-inspect": "^7.0.0", + "vfile-message": "^3.0.0", + "vfile-reporter": "^7.0.0", + "vfile-statistics": "^2.0.0", + "yaml": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified-engine/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/unified-engine/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/unified-engine/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified-engine/node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/unified-engine/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/unified-engine/node_modules/parse-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-6.0.2.tgz", + "integrity": "sha512-SA5aMiaIjXkAiBrW/yPgLgQAQg42f7K3ACO+2l/zOvtQBwX58DMUsFJXelW2fx3yMBmWOVkR6j1MGsdSbCA4UA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^2.3.1", + "lines-and-columns": "^2.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified-engine/node_modules/yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/unified-lint-rule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/unified-lint-rule/-/unified-lint-rule-2.1.1.tgz", + "integrity": "sha512-vsLHyLZFstqtGse2gvrGwasOmH8M2y+r2kQMoDSWzSqUkQx2MjHjvZuGSv5FUaiv4RQO1bHRajy7lSGp7XWq5A==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "trough": "^2.0.0", + "unified": "^10.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-generated": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.0.tgz", + "integrity": "sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-inspect": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/unist-util-inspect/-/unist-util-inspect-7.0.1.tgz", + "integrity": "sha512-gEPeSrsYXus8012VJ00p9uZC8D0iogtLLiHlBgvS61hU22KNKduQhMKezJm83viHlLf3TYS2y9SDEFglWPDMKw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz", + "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz", + "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz", + "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", + "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.0.1.tgz", + "integrity": "sha512-JDxPlTbZrZCQXogGheBHjbRWjESSPEak770XwWPfw5mTc1v1nWGLB/apzZxsx8a0SJVfF8HK8ql8RD308vXRUw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-reporter": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.4.tgz", + "integrity": "sha512-4cWalUnLrEnbeUQ+hARG5YZtaHieVK3Jp4iG5HslttkVl+MHunSGNAIrODOTLbtjWsNZJRMCkL66AhvZAYuJ9A==", + "dev": true, + "dependencies": { + "@types/supports-color": "^8.0.0", + "string-width": "^5.0.0", + "supports-color": "^9.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-sort": "^3.0.0", + "vfile-statistics": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-reporter/node_modules/supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/vfile-sort": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-3.0.0.tgz", + "integrity": "sha512-fJNctnuMi3l4ikTVcKpxTbzHeCgvDhnI44amA3NVDvA6rTC6oKCFpCVyT5n2fFMr3ebfr+WVQZedOCd73rzSxg==", + "dev": true, + "dependencies": { + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-statistics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-2.0.0.tgz", + "integrity": "sha512-foOWtcnJhKN9M2+20AOTlWi2dxNfAoeNIoxD5GXcO182UJyId4QrXa41fWrgcfV3FWTjdEDy3I4cpLVcQscIMA==", + "dev": true, + "dependencies": { + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/walk-up-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", + "dev": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dev": true, + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz", + "integrity": "sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==", + "dependencies": { + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^6.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-node-module-types": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/webpack-node-module-types/-/webpack-node-module-types-1.2.2.tgz", + "integrity": "sha512-cn1R6CS8UjjtDx/B/hbq7PrNoOXn8cWNrvnpMWC1+Jwv0XiVx+qnuBYInDLqAvoXN3Qo6wYmsnrRLzoo0wlwAg==", + "engines": { + "node": ">= 12.x" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz", + "integrity": "sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/cli": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.18.10.tgz", + "integrity": "sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.8", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==" + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "dev": true, + "requires": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", + "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", + "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "requires": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.10.tgz", + "integrity": "sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-default-from": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz", + "integrity": "sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-default-from": "^7.18.6" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-function-bind": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.18.9.tgz", + "integrity": "sha512-9RfxqKkRBCCT0xoBl9AqieCMscJmSAL9HYixGMWH549jUpT9csWWK/HEYZEx9t9iW/PRSXgX95x9bDlgtAJGFA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-function-bind": "^7.18.6" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-default-from": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz", + "integrity": "sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-function-bind": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.18.6.tgz", + "integrity": "sha512-wZN0Aq/AScknI9mKGcR3TpHdASMufFGaeJgc1rhPmLtZ/PniwjePSh8cfh8tXMB3U4kh/3cRKrLjDtedejg8jQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz", + "integrity": "sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz", + "integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz", + "integrity": "sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-typescript": "^7.18.6" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + } + }, + "@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + } + }, + "@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz", + "integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==", + "dev": true, + "requires": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", + "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, + "@commitlint/cli": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.0.3.tgz", + "integrity": "sha512-oAo2vi5d8QZnAbtU5+0cR2j+A7PO8zuccux65R/EycwvsZrDVyW518FFrnJK2UQxbRtHFFIG+NjQ6vOiJV0Q8A==", + "dev": true, + "requires": { + "@commitlint/format": "^17.0.0", + "@commitlint/lint": "^17.0.3", + "@commitlint/load": "^17.0.3", + "@commitlint/read": "^17.0.0", + "@commitlint/types": "^17.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + } + }, + "@commitlint/config-conventional": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.0.3.tgz", + "integrity": "sha512-HCnzTm5ATwwwzNVq5Y57poS0a1oOOcd5pc1MmBpLbGmSysc4i7F/++JuwtdFPu16sgM3H9J/j2zznRLOSGVO2A==", + "dev": true, + "requires": { + "conventional-changelog-conventionalcommits": "^5.0.0" + } + }, + "@commitlint/config-validator": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.0.3.tgz", + "integrity": "sha512-3tLRPQJKapksGE7Kee9axv+9z5I2GDHitDH4q63q7NmNA0wkB+DAorJ0RHz2/K00Zb1/MVdHzhCga34FJvDihQ==", + "dev": true, + "requires": { + "@commitlint/types": "^17.0.0", + "ajv": "^8.11.0" + } + }, + "@commitlint/ensure": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.0.0.tgz", + "integrity": "sha512-M2hkJnNXvEni59S0QPOnqCKIK52G1XyXBGw51mvh7OXDudCmZ9tZiIPpU882p475Mhx48Ien1MbWjCP1zlyC0A==", + "dev": true, + "requires": { + "@commitlint/types": "^17.0.0", + "lodash": "^4.17.19" + } + }, + "@commitlint/execute-rule": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz", + "integrity": "sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ==", + "dev": true + }, + "@commitlint/format": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.0.0.tgz", + "integrity": "sha512-MZzJv7rBp/r6ZQJDEodoZvdRM0vXu1PfQvMTNWFb8jFraxnISMTnPBWMMjr2G/puoMashwaNM//fl7j8gGV5lA==", + "dev": true, + "requires": { + "@commitlint/types": "^17.0.0", + "chalk": "^4.1.0" + } + }, + "@commitlint/is-ignored": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.0.3.tgz", + "integrity": "sha512-/wgCXAvPtFTQZxsVxj7owLeRf5wwzcXLaYmrZPR4a87iD4sCvUIRl1/ogYrtOyUmHwWfQsvjqIB4mWE/SqWSnA==", + "dev": true, + "requires": { + "@commitlint/types": "^17.0.0", + "semver": "7.3.7" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@commitlint/lint": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.0.3.tgz", + "integrity": "sha512-2o1fk7JUdxBUgszyt41sHC/8Nd5PXNpkmuOo9jvGIjDHzOwXyV0PSdbEVTH3xGz9NEmjohFHr5l+N+T9fcxong==", + "dev": true, + "requires": { + "@commitlint/is-ignored": "^17.0.3", + "@commitlint/parse": "^17.0.0", + "@commitlint/rules": "^17.0.0", + "@commitlint/types": "^17.0.0" + } + }, + "@commitlint/load": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.0.3.tgz", + "integrity": "sha512-3Dhvr7GcKbKa/ey4QJ5MZH3+J7QFlARohUow6hftQyNjzoXXROm+RwpBes4dDFrXG1xDw9QPXA7uzrOShCd4bw==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^17.0.3", + "@commitlint/execute-rule": "^17.0.0", + "@commitlint/resolve-extends": "^17.0.3", + "@commitlint/types": "^17.0.0", + "@types/node": ">=12", + "chalk": "^4.1.0", + "cosmiconfig": "^7.0.0", + "cosmiconfig-typescript-loader": "^2.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "typescript": "^4.6.4" + } + }, + "@commitlint/message": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.0.0.tgz", + "integrity": "sha512-LpcwYtN+lBlfZijHUdVr8aNFTVpHjuHI52BnfoV01TF7iSLnia0jttzpLkrLmI8HNQz6Vhr9UrxDWtKZiMGsBw==", + "dev": true + }, + "@commitlint/parse": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.0.0.tgz", + "integrity": "sha512-cKcpfTIQYDG1ywTIr5AG0RAiLBr1gudqEsmAGCTtj8ffDChbBRxm6xXs2nv7GvmJN7msOt7vOKleLvcMmRa1+A==", + "dev": true, + "requires": { + "@commitlint/types": "^17.0.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.2.2" + } + }, + "@commitlint/read": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.0.0.tgz", + "integrity": "sha512-zkuOdZayKX3J6F6mPnVMzohK3OBrsEdOByIqp4zQjA9VLw1hMsDEFQ18rKgUc2adkZar+4S01QrFreDCfZgbxA==", + "dev": true, + "requires": { + "@commitlint/top-level": "^17.0.0", + "@commitlint/types": "^17.0.0", + "fs-extra": "^10.0.0", + "git-raw-commits": "^2.0.0" + } + }, + "@commitlint/resolve-extends": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.0.3.tgz", + "integrity": "sha512-H/RFMvrcBeJCMdnVC4i8I94108UDccIHrTke2tyQEg9nXQnR5/Hd6MhyNWkREvcrxh9Y+33JLb+PiPiaBxCtBA==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^17.0.3", + "@commitlint/types": "^17.0.0", + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + } + }, + "@commitlint/rules": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.0.0.tgz", + "integrity": "sha512-45nIy3dERKXWpnwX9HeBzK5SepHwlDxdGBfmedXhL30fmFCkJOdxHyOJsh0+B0RaVsLGT01NELpfzJUmtpDwdQ==", + "dev": true, + "requires": { + "@commitlint/ensure": "^17.0.0", + "@commitlint/message": "^17.0.0", + "@commitlint/to-lines": "^17.0.0", + "@commitlint/types": "^17.0.0", + "execa": "^5.0.0" + } + }, + "@commitlint/to-lines": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.0.0.tgz", + "integrity": "sha512-nEi4YEz04Rf2upFbpnEorG8iymyH7o9jYIVFBG1QdzebbIFET3ir+8kQvCZuBE5pKCtViE4XBUsRZz139uFrRQ==", + "dev": true + }, + "@commitlint/top-level": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.0.0.tgz", + "integrity": "sha512-dZrEP1PBJvodNWYPOYiLWf6XZergdksKQaT6i1KSROLdjf5Ai0brLOv5/P+CPxBeoj3vBxK4Ax8H1Pg9t7sHIQ==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + } + }, + "@commitlint/types": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.0.0.tgz", + "integrity": "sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ==", + "dev": true, + "requires": { + "chalk": "^4.1.0" + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + } + }, + "@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "requires": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + } + }, + "@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "requires": { + "jest-get-type": "^28.0.2" + } + }, + "@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + } + }, + "@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "requires": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, + "@mswjs/cookies": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", + "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "dev": true, + "requires": { + "@types/set-cookie-parser": "^2.4.0", + "set-cookie-parser": "^2.4.6" + } + }, + "@mswjs/interceptors": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.3.tgz", + "integrity": "sha512-jBRFPeHBPqKv3od8KPjmrvt4b/+e1DorizFDYJ8NQCrjFT9YGnxA8ojGi0MIo64x/JgdjYkhP8bG9EY4BGPoqg==", + "dev": true, + "requires": { + "@open-draft/until": "^1.0.3", + "@types/debug": "^4.1.7", + "@xmldom/xmldom": "^0.7.5", + "debug": "^4.3.3", + "headers-polyfill": "^3.0.4", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.2.4", + "web-encoding": "^1.1.5" + } + }, + "@next/bundle-analyzer": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-12.2.4.tgz", + "integrity": "sha512-7kyv8UBSq85wODeqemHJrvc1eg7He+oyA04XTKL8gUvE50uKlIpCh5PXP4ko4yphhWADGq8LeFOZXLIfbc7Fdg==", + "requires": { + "webpack-bundle-analyzer": "4.3.0" + } + }, + "@next/env": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.2.4.tgz", + "integrity": "sha512-/gApFXWk5CCLFQJL5IYJXxPQuG5tz5nPX4l27A9Zm/+wJxiwFrRSP54AopDxIv4JRp/rGwcgk/lZS/0Clw8jYA==" + }, + "@next/eslint-plugin-next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.4.tgz", + "integrity": "sha512-ChDkUIkJeYWKRx+FdF+EhUgvKtK1wF+Xew4Os7ef3iAjMch5GGBiezw2zGXTa/C0E6potz4j11EpX89mngffug==", + "dev": true, + "requires": { + "glob": "7.1.7" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "@next/swc-android-arm-eabi": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.4.tgz", + "integrity": "sha512-P4YSFNpmXXSnn3P1qsOAqz+MX3On9fHrlc8ovb/CFJJoU+YLCR53iCEwfw39e0IZEgDA7ttgr108plF8mxaX0g==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.4.tgz", + "integrity": "sha512-4o2n14E18O+8xHlf6dgJsWPXN9gmSmfIe2Z0EqKDIPBBkFt/2CyrH0+vwHnL2l7xkDHhOGfZYcYIWVUR5aNu0A==", + "optional": true + }, + "@next/swc-darwin-arm64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.4.tgz", + "integrity": "sha512-DcUO6MGBL9E3jj5o86MUnTOy4WawIJJhyCcFYO4f51sbl7+uPIYIx40eo98A6NwJEXazCqq1hLeqOaNTAIvDiQ==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.4.tgz", + "integrity": "sha512-IUlFMqeLjdIzDorrGC2Dt+2Ae3DbKQbRzCzmDq4/CP1+jJGeDXo/2AHnlE+WYnwQAC4KtAz6pbVnd3KstZWsVA==", + "optional": true + }, + "@next/swc-freebsd-x64": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.4.tgz", + "integrity": "sha512-475vwyWcjnyDVDWLgAATP0HI8W1rwByc+uXk1B6KkAVFhkoDgH387LW0uNqxavK+VxCzj3avQXX/58XDvxtSlg==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.4.tgz", + "integrity": "sha512-qZW+L3iG3XSGtlOPmD5RRWXyk6ZNdscLV0BQjuDvP+exTg+uixqHXOHz0/GVATIJEBQOF0Kew7jAXVXEP+iRTQ==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.4.tgz", + "integrity": "sha512-fEPRjItWYaKyyG9N+2HIA59OBHIhk7WC+Rh+LwXsh0pQe870Ykpek3KQs0umjsrEGe57NyMomq3f80/N8taDvA==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.4.tgz", + "integrity": "sha512-rnCTzXII0EBCcFn9P5s/Dho2kPUMSX/bP0iOAj8wEI/IxUEfEElbin89zJoNW30cycHu19xY8YP4K2+hzciPzQ==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.4.tgz", + "integrity": "sha512-PhXX6NSuIuhHInxPY2VkG2Bl7VllsD3Cjx+pQcS1wTym7Zt7UoLvn05PkRrkiyIkvR+UXnqPUM3TYiSbnemXEw==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.4.tgz", + "integrity": "sha512-GmC/QROiUZpFirHRfPQqMyCXZ+5+ndbBZrMvL74HtQB/CKXB8K1VM+rvy9Gp/5OaU8Rxp48IcX79NOfI2LiXlA==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.4.tgz", + "integrity": "sha512-9XKoCXbNZuaMRPtcKQz3+hgVpkMosaLlcxHFXT8/j4w61k7/qvEbrkMDS9WHNrD/xVcLycwhPRgXcns2K1BdBQ==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.4.tgz", + "integrity": "sha512-hEyRieZKH9iw4AzvXaQ+Fyb98k0G/o9QcRGxA1/O/O/elf1+Qvuwb15phT8GbVtIeNziy66XTPOhKKfdr8KyUg==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.4.tgz", + "integrity": "sha512-5Pl1tdMJWLy4rvzU1ecx0nHWgDPqoYuvYoXE/5X0Clu9si/yOuBIj573F2kOTY7mu0LX2wgCJVSnyK0abHBxIw==", + "optional": true + }, + "@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/config": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/config/-/config-4.2.0.tgz", + "integrity": "sha512-imWNz5dNWb2u+y41jyxL2WB389tkhu3a01Rchn16O/ur6GrnKySgOqdNG3N/9Z+mqxdISMEGKXI/POCauzz0dA==", + "dev": true, + "requires": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "dependencies": { + "ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@npmcli/map-workspaces": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.3.tgz", + "integrity": "sha512-X6suAun5QyupNM8iHkNPh0AHdRC2rb1W+MTdMvvA/2ixgmqZwlq5cGUBgmKHUHT2LgrkKJMAXbfAoTxOigpK8Q==", + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", + "dev": true + }, + "@octokit/auth-token": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.0.tgz", + "integrity": "sha512-MDNFUBcJIptB9At7HiV7VCvU3NcL4GnfCQaP8C5lrxWrRPMJBnemYtehaKSOlaM7AYxeRyj9etenu8LVpSpVaQ==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.4.tgz", + "integrity": "sha512-sUpR/hc4Gc7K34o60bWC7WUH6Q7T6ftZ2dUmepSyJr9PRF76/qqkWjE2SOEzCqLA5W83SaISymwKtxks+96hPQ==", + "dev": true, + "requires": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.0.tgz", + "integrity": "sha512-Kz/mIkOTjs9rV50hf/JK9pIDl4aGwAtT8pry6Rpy+hVXkAPhXanNQRxMoq6AeRgDCZR6t/A1zKniY2V1YhrzlQ==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.0.tgz", + "integrity": "sha512-1ZZ8tX4lUEcLPvHagfIVu5S2xpHYXAmgN0+95eAOPoaVPzCfUXJtA5vASafcpWcO86ze0Pzn30TAx72aB2aguQ==", + "dev": true, + "requires": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.10.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz", + "integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ==", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.0.0.tgz", + "integrity": "sha512-fvw0Q5IXnn60D32sKeLIxgXCEZ7BTSAjJd8cFAE6QU5qUp0xo7LjFUjjX1J5D7HgN355CN4EXE4+Q1/96JaNUA==", + "dev": true, + "requires": { + "@octokit/types": "^6.39.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.1.2.tgz", + "integrity": "sha512-sAfSKtLHNq0UQ2iFuI41I6m5SK6bnKFRJ5kUjDRVbmQXiRVi4aQiIcgG4cM7bt+bhSiWL4HwnTxDkWFlKeKClA==", + "dev": true, + "requires": { + "@octokit/types": "^6.40.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.0.tgz", + "integrity": "sha512-7IAmHnaezZrgUqtRShMlByJK33MT9ZDnMRgZjnRrRV9a/jzzFwKGz0vxhFU6i7VMLraYcQ1qmcAOin37Kryq+Q==", + "dev": true, + "requires": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.0.tgz", + "integrity": "sha512-WBtpzm9lR8z4IHIMtOqr6XwfkGvMOOILNLxsWvDwtzm/n7f5AWuqJTXQXdDtOvPfTDrH4TPhEvW2qMlR4JFA2w==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", + "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", + "dev": true, + "requires": { + "@octokit/core": "^4.0.0", + "@octokit/plugin-paginate-rest": "^3.0.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + } + }, + "@octokit/types": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz", + "integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==", + "dev": true, + "requires": { + "@octokit/openapi-types": "^12.10.0" + } + }, + "@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, + "@pkgr/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-7dIJ9CRVzBnqyEl7diUHPUFJf/oty2SeoVzcMocc5PeOUDK9KGzvgIBjGRRzzlRDaOjh3ADwH0WeibQvi3ls2Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + }, + "@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==", + "dev": true + }, + "@semantic-release/changelog": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.1.tgz", + "integrity": "sha512-FT+tAGdWHr0RCM3EpWegWnvXJ05LQtBkQUaQRIExONoXjVjLuOILNm4DEKNaV+GAQyJjbLRVs57ti//GypH6PA==", + "dev": true, + "requires": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + } + } + }, + "@semantic-release/commit-analyzer": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", + "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "import-from": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + } + }, + "@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true + }, + "@semantic-release/exec": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz", + "integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==", + "dev": true, + "requires": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "parse-json": "^5.0.0" + } + }, + "@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "requires": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + } + }, + "@semantic-release/github": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.0.5.tgz", + "integrity": "sha512-9pGxRM3gv1hgoZ/muyd4pWnykdIUVfCiev6MXE9lOyGQof4FQy95GFE26nDcifs9ZG7bBzV8ue87bo/y1zVf0g==", + "dev": true, + "requires": { + "@octokit/rest": "^19.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^10.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^3.0.0", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + }, + "dependencies": { + "@semantic-release/error": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", + "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", + "dev": true + } + } + }, + "@semantic-release/npm": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.1.tgz", + "integrity": "sha512-I5nVZklxBzfMFwemhRNbSrkiN/dsH3c7K9+KSk6jUnq0rdLFUuJt7EBsysq4Ir3moajQgFkfEryEHPqiKJj20g==", + "dev": true, + "requires": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^6.0.0", + "npm": "^8.3.0", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "@semantic-release/release-notes-generator": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", + "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "get-stream": "^6.0.0", + "import-from": "^4.0.0", + "into-stream": "^6.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "@sinclair/typebox": { + "version": "0.24.20", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz", + "integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@swc/helpers": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.3.tgz", + "integrity": "sha512-6JrF+fdUK2zbGpJIlN7G3v966PQjyx/dPt1T9km2wj+EUBqgrxCk3uX4Kct16MIm9gGxfKRcfax2hVf5jvlTzA==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@testing-library/dom": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz", + "integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + } + }, + "@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@testing-library/react": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==" + }, + "@types/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-bdkCSkyVHsgl3Goe1y16T9k6JuQx7SiDREkq728QjKmTZkGJZuS8R3gGcnGzVuGBP0mssKrzM/GlMOQxtip9cg==", + "dev": true + }, + "@types/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/confusing-browser-globals": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/confusing-browser-globals/-/confusing-browser-globals-1.0.0.tgz", + "integrity": "sha512-2RQF2SJW3pAQgkHtlIefyxiRkdlBwXxqNiiDDoPyaR9R0SqIujHoP6ceLSAE95EKd7CCLV3F/n4ohOAMi5edpQ==", + "dev": true + }, + "@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "requires": { + "@types/ms": "*" + } + }, + "@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/inquirer": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.0.tgz", + "integrity": "sha512-4sncHTq3AWbLWZOs83DQlrKxhdehs2AW79Dp1amoGGc6B+oJal7fQJa/EO1Taa5CQLSywEMAgw2SFB1fZIX3PQ==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "@types/is-empty": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/is-empty/-/is-empty-1.2.1.tgz", + "integrity": "sha512-a3xgqnFTuNJDm1fjsTjHocYJ40Cz3t8utYpi5GNaxzrJC2HSD08ym+whIL7fNqiqBCdM9bcqD1H/tORWAFXoZw==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "28.1.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.6.tgz", + "integrity": "sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ==", + "dev": true, + "requires": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "@types/js-levenshtein": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz", + "integrity": "sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==", + "dev": true + }, + "@types/jsdom": { + "version": "16.2.15", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.15.tgz", + "integrity": "sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/parse5": "^6.0.3", + "@types/tough-cookie": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-zQPywzif9EycCkvECjYT9dbbttT0dkk657zcLb/803ZOXHsBA963jzEPF/Jnh1zOdBbgFJvUE8kcvZverAoK1w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mdast": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "@types/node": { + "version": "18.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz", + "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + }, + "@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, + "@types/prettier": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.4.tgz", + "integrity": "sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/react": { + "version": "18.0.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", + "integrity": "sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", + "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/request-ip": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@types/request-ip/-/request-ip-0.0.37.tgz", + "integrity": "sha512-uw6/i3rQnpznxD7LtLaeuZytLhKZK6bRoTS6XVJlwxIOoOpEBU7bgKoVXDNtOg4Xl6riUKHa9bjMVrL6ESqYlQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/semver": { + "version": "7.3.10", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", + "integrity": "sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==", + "dev": true + }, + "@types/set-cookie-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz", + "integrity": "sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", + "dev": true + }, + "@types/tar-stream": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-2.2.2.tgz", + "integrity": "sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/test-listen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/test-listen/-/test-listen-1.1.0.tgz", + "integrity": "sha512-y6ZfbSzYHniCeY6ZAzsQjSAdJInNVoEz4Uhsb81W+RCoNYA59yoG/+XbqPqCPj2KCU3Wa6RFWSozutkGIHIsNQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dev": true, + "requires": { + "@types/jest": "*" + } + }, + "@types/text-table": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/text-table/-/text-table-0.2.2.tgz", + "integrity": "sha512-dGoI5Af7To0R2XE8wJuc6vwlavWARsCh3UKJPjWs1YEqGUqfgBI/j/4GX0yf19/DsDPPf0YAXWAp8psNeIehLg==", + "dev": true + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true + }, + "@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "@types/webpack": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", + "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", + "dev": true, + "requires": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz", + "integrity": "sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/type-utils": "5.33.0", + "@typescript-eslint/utils": "5.33.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.33.0.tgz", + "integrity": "sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/typescript-estree": "5.33.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz", + "integrity": "sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/visitor-keys": "5.33.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz", + "integrity": "sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.33.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.33.0.tgz", + "integrity": "sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz", + "integrity": "sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/visitor-keys": "5.33.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.33.0.tgz", + "integrity": "sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.33.0", + "@typescript-eslint/types": "5.33.0", + "@typescript-eslint/typescript-estree": "5.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz", + "integrity": "sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.33.0", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "requires": {} + }, + "@xmldom/xmldom": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz", + "integrity": "sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==", + "dev": true + }, + "@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@xunnamius/conventional-changelog-projector": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@xunnamius/conventional-changelog-projector/-/conventional-changelog-projector-1.1.1.tgz", + "integrity": "sha512-YFhr0ChPrvoCg1E2lxdZoWti+MzYnl9jXEg6qtUsia75/P1ugeOpyZaqG9kEBTGV/tx7v1YFCu5zz68Ppj1z8g==", + "dev": true, + "requires": { + "assign-deep": "^1.0.1", + "debug": "^4.3.2", + "is-plain-object": "^5.0.0", + "semver": "^7.3.5", + "toss-expression": "^0.1.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@xunnamius/jest-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@xunnamius/jest-types/-/jest-types-1.1.3.tgz", + "integrity": "sha512-htMyGTIO5tWcJKCksqCsvMZDQShdyqJPLiIl/TdgVdxf4ZD3Zu9Zv3pb0z7DNQsc3oAL4lgYgmdNL9f6XR6Ufw==", + "dev": true, + "requires": {} + }, + "@xunnamius/next-types": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@xunnamius/next-types/-/next-types-1.0.9.tgz", + "integrity": "sha512-6g3MYhaxy5+Cqm3La7XphqntcAsUrqGm28AUctndcilHgk3Tbtf/J984QiHH7X1upjZLur9jo3Lg0DsCQqG1ig==", + "requires": {} + }, + "@xunnamius/types": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@xunnamius/types/-/types-1.3.0.tgz", + "integrity": "sha512-RAWA75sRcOsZFROjNPz5+WPeSwJj/T751iNUeT+uVmV4f9tlN+cgiV+A6yUxUPEL5NR6pTzTv8ZwZ/HU78+TcQ==", + "requires": {} + }, + "@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dev": true, + "optional": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true + }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "assign-deep": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/assign-deep/-/assign-deep-1.0.1.tgz", + "integrity": "sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA==", + "dev": true, + "requires": { + "assign-symbols": "^2.0.2" + } + }, + "assign-symbols": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-2.0.2.tgz", + "integrity": "sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async-mutex": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", + "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", + "dev": true, + "requires": { + "tslib": "^2.3.1" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "auto-bind": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "axe-core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", + "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "dev": true + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, + "requires": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-explicit-exports-references": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-explicit-exports-references/-/babel-plugin-explicit-exports-references-1.0.2.tgz", + "integrity": "sha512-z+weAyF11Mr1azXIR5pfhAeXeK8ZvacXSKgPLGdwBoR7efyqnUxYvlGUny7eHZxAO/Q1C2O1+xO9lwgGPDaBlw==", + "requires": { + "@babel/core": "^7.13.15", + "@babel/template": "^7.12.13", + "@babel/types": "^7.13.14", + "debug": "^4.3.1" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + } + }, + "babel-plugin-transform-default-named-imports": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-default-named-imports/-/babel-plugin-transform-default-named-imports-1.2.2.tgz", + "integrity": "sha512-xLfgaSMqNEgGhcCIXvSqdWjbcucLPDSNUmVHBYyLLWENLDJ3uOOBRFWyHCItzT727OQeMbpML8DN8ntUS9loSw==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/types": "^7.16.0", + "webpack-node-module-types": "^1.2.1" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true + }, + "bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "bson": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz", + "integrity": "sha512-uqrgcjyOaZsHfz7ea8zLRCLe1u+QGUSzMZmvXqO24CDW7DWoW1qiN9folSwa7hSneTSgM2ykDIzF5kcQQ8cwNw==", + "requires": { + "buffer": "^5.6.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001370", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz", + "integrity": "sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g==" + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true + }, + "cli-table3": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "conventional-changelog": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", + "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" + }, + "dependencies": { + "conventional-changelog-conventionalcommits": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + } + } + }, + "conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-cli": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.2.2.tgz", + "integrity": "sha512-8grMV5Jo8S0kP3yoMeJxV2P5R6VJOqK72IiSV9t/4H5r/HiRqEBQ83bYGuz4Yzfdj4bjaAEhZN/FFbsFXr5bOA==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog": "^3.1.24", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "tempfile": "^3.0.0" + } + }, + "conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", + "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", + "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^4.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^4.0.0" + } + }, + "conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "dev": true, + "requires": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + } + }, + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "requires": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + } + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js-compat": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz", + "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==", + "requires": { + "browserslist": "^4.21.2", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-js-pure": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.0.tgz", + "integrity": "sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cosmiconfig-typescript-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", + "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", + "dev": true, + "requires": { + "cosmiconfig": "^7", + "ts-node": "^10.8.1" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "requires": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + } + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "requires": { + "character-entities": "^2.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true + }, + "detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "dev": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", + "dev": true + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.202", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.202.tgz", + "integrity": "sha512-JYsK2ex9lmQD27kj19fhXYxzFJ/phLAkLKHv49A5UY6kMRV2xED3qMMLg/voW/+0AR6wMiI+VxlmK9NDtdxlPA==" + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "env-ci": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz", + "integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "fromentries": "^1.3.2", + "java-properties": "^1.0.0" + } + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.2.4.tgz", + "integrity": "sha512-r3keSLY1Z+rN+ASN8nmWwZ+AsMl6IrPGRWgbQhKHcop4/fk1hJGxE9Xf/mYMkV07+1Q/catchw25lu525HFy5Q==", + "dev": true, + "requires": { + "@next/eslint-plugin-next": "12.2.4", + "@rushstack/eslint-patch": "^1.1.3", + "@typescript-eslint/parser": "^5.21.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.5.0" + }, + "dependencies": { + "eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + } + } + } + }, + "eslint-import-resolver-alias": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz", + "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.4.0.tgz", + "integrity": "sha512-rBCgiEovwX/HQ8ESWV+XIWZaFiRtDeAXNZdcTATB8UbMuadc9qfGOlIP+vy+c7nsgfEBN4NTwy5qunGNptDP0Q==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.9.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.1" + }, + "dependencies": { + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "eslint-plugin-jest": { + "version": "26.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.8.2.tgz", + "integrity": "sha512-67oh0FKaku9y48OpLzL3uK9ckrgLb83Sp5gxxTbtOGDw9lq6D8jw/Psj/9CipkbK406I2M7mvx1q+pv/MdbvxA==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^5.10.0" + } + }, + "eslint-plugin-jest-dom": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest-dom/-/eslint-plugin-jest-dom-4.0.2.tgz", + "integrity": "sha512-Jo51Atwyo2TdcUncjmU+UQeSTKh3sc2LF/M5i/R3nTU0Djw9V65KGJisdm/RtuKhy2KH/r7eQ1n6kwYFPNdHlA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.16.3", + "@testing-library/dom": "^8.11.1", + "requireindex": "^1.2.0" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + } + } + }, + "eslint-plugin-react": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz", + "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==", + "dev": true, + "requires": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "requires": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-json-stringify": { + "version": "2.7.13", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz", + "integrity": "sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==", + "dev": true, + "requires": { + "ajv": "^6.11.0", + "deepmerge": "^4.2.2", + "rfdc": "^1.2.0", + "string-similarity": "^4.0.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dev": true, + "requires": { + "boolean": "^3.1.4" + } + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "fastest-levenshtein": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz", + "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA==", + "dev": true + }, + "fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "requires": { + "format": "^0.2.0" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "dev": true, + "requires": { + "semver-regex": "^3.1.2" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, + "requires": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-tsconfig": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", + "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==", + "dev": true + }, + "git-hooks-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", + "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", + "dev": true + }, + "git-log-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", + "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", + "dev": true, + "requires": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "requires": { + "through2": "~2.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "requires": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "requires": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + } + }, + "git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "requires": { + "meow": "^8.0.0", + "semver": "^6.0.0" + } + }, + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "requires": { + "ini": "^1.3.2" + } + }, + "github-slugger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphql": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", + "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", + "dev": true + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "headers-polyfill": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.0.10.tgz", + "integrity": "sha512-lOhQU7iG3AMcjmb8NIWCa+KwfJw5bY44BoWPtrj5A4iDbSD3ylGf5QcYr0ZyQnhkKQ2GgWNLdF2rfrXtXlF3nQ==", + "dev": true + }, + "hook-std": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", + "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http-terminator": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/http-terminator/-/http-terminator-3.2.0.tgz", + "integrity": "sha512-JLjck1EzPaWjsmIf8bziM3p9fgR1Y3JoUKAkyYEbZmFrIvJM6I8vVJfBGWlEtV9IWOvzNnaTtjuwZeBY2kwB4g==", + "dev": true, + "requires": { + "delay": "^5.0.0", + "p-wait-for": "^3.2.0", + "roarr": "^7.0.4", + "type-fest": "^2.3.3" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "husky": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", + "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", + "dev": true + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "import-meta-resolve": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-2.0.3.tgz", + "integrity": "sha512-fpAppnBpZ3ymQ/dPP97TNsco1HB5+V9SYJ3chY50PP8xn4U/w+Y6ovWBmTImB/prmGsTjzPh8pQYY+EVBlr9mw==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "inline-style-prefixer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz", + "integrity": "sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ==", + "requires": { + "css-in-js-utils": "^2.0.0" + } + }, + "inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dev": true, + "requires": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + } + }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + } + } + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-empty": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", + "integrity": "sha512-F2FnH/otLNJv0J6wc73A5Xo7oHLNnqplYqZhUu01tD54DIPvxIRSTSLkrUB/M0nHO4vo1O9PDfN4KoTxCzLh/w==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-node-process": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.0.1.tgz", + "integrity": "sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-server-side": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-server-side/-/is-server-side-1.0.2.tgz", + "integrity": "sha512-AazkGxiRd+xwIv0OYcvxHIITZccolSEPqVDOxmmApqtEGYX4G+7sucsVUs9CAwcVW+RIRsRNvqr3u7L8vlHdHg==" + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + }, + "dependencies": { + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + } + } + }, + "is-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + } + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true + }, + "jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, + "requires": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + } + }, + "jest-changed-files": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "requires": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-environment-jsdom": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz", + "integrity": "sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/jsdom": "^16.2.4", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3", + "jsdom": "^19.0.0" + } + }, + "jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "jest-extended": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.2.tgz", + "integrity": "sha512-LnVZvwWLRV9AL8J7f4frKu0KHuTrbIFK15IqrvSwbFCYxalkuC5l7HfcofsksePrvlEJ2WAcfYNnu1+bEGvInA==", + "dev": true, + "requires": { + "jest-diff": "^28.0.0", + "jest-get-type": "^28.0.0" + } + }, + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true + }, + "jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "requires": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true + }, + "jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, + "requires": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + } + }, + "jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "jest-silent-reporter": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jest-silent-reporter/-/jest-silent-reporter-0.5.0.tgz", + "integrity": "sha512-epdLt8Oj0a1AyRiR6F8zx/1SVT1Mi7VU3y4wB2uOBHs/ohIquC7v2eeja7UN54uRPyHInIKWdL+RdG228n5pJQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-util": "^26.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } + } + }, + "jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + }, + "jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "jsx-ast-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz", + "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dev": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenshtein-edit-distance": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/levenshtein-edit-distance/-/levenshtein-edit-distance-1.0.0.tgz", + "integrity": "sha512-gpgBvPn7IFIAL32f0o6Nsh2g+5uOvkt4eK9epTfgE4YVxBxwVhJ/p1888lMm/u8mXdu1ETLSi6zeEmkBI+0F3w==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lint-staged": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", + "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.17", + "commander": "^9.3.0", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.5", + "listr2": "^4.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.1" + }, + "dependencies": { + "commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true + }, + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "load-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-5.0.0.tgz", + "integrity": "sha512-jTz8tvC0BTMtof27lTSV5SAOnCRT0Z++k+S3QeQ5CrF8ZAS5L2nhi3euf4ZhJyDkds+nOQGyPcFqdQZ9s8ELkg==", + "dev": true, + "requires": { + "@npmcli/config": "^4.0.0", + "import-meta-resolve": "^2.0.0" + } + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "longest-streak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.1.tgz", + "integrity": "sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "markdown-table": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz", + "integrity": "sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==", + "dev": true + }, + "marked": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", + "dev": true + }, + "marked-terminal": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.1.1.tgz", + "integrity": "sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g==", + "dev": true, + "requires": { + "ansi-escapes": "^5.0.0", + "cardinal": "^2.1.1", + "chalk": "^5.0.0", + "cli-table3": "^0.6.1", + "node-emoji": "^1.11.0", + "supports-hyperlinks": "^2.2.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "requires": { + "type-fest": "^1.0.2" + } + }, + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true + }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } + }, + "md5-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", + "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==", + "dev": true + }, + "mdast-util-find-and-replace": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.1.tgz", + "integrity": "sha512-SobxkQXFAdd4b5WmEakmkVoh18icjQRxGy5OWTCzgsLRm1Fu/KCtwD1HIQSsmq5ZRjVH0Ehwg6/Fn3xIUk+nKw==", + "dev": true, + "requires": { + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true + } + } + }, + "mdast-util-from-markdown": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", + "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-frontmatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.0.tgz", + "integrity": "sha512-7itKvp0arEVNpCktOET/eLFAYaZ+0cNjVtFtIPxgQ5tV+3i+D4SDDTjTzPWl44LT59PC+xdx+glNTawBdF98Mw==", + "dev": true, + "requires": { + "micromark-extension-frontmatter": "^1.0.0" + } + }, + "mdast-util-gfm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", + "dev": true, + "requires": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-gfm-autolink-literal": "^1.0.0", + "mdast-util-gfm-footnote": "^1.0.0", + "mdast-util-gfm-strikethrough": "^1.0.0", + "mdast-util-gfm-table": "^1.0.0", + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + } + }, + "mdast-util-gfm-autolink-literal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.2.tgz", + "integrity": "sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "ccount": "^2.0.0", + "mdast-util-find-and-replace": "^2.0.0", + "micromark-util-character": "^1.0.0" + } + }, + "mdast-util-gfm-footnote": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.1.tgz", + "integrity": "sha512-p+PrYlkw9DeCRkTVw1duWqPRHX6Ywh2BNKJQcZbCwAuP/59B0Lk9kakuAd7KbQprVO4GzdW8eS5++A9PUSqIyw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-util-normalize-identifier": "^1.0.0" + } + }, + "mdast-util-gfm-strikethrough": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.1.tgz", + "integrity": "sha512-zKJbEPe+JP6EUv0mZ0tQUyLQOC+FADt0bARldONot/nefuISkaZFlmVK4tU6JgfyZGrky02m/I6PmehgAgZgqg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-gfm-table": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", + "dev": true, + "requires": { + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-gfm-task-list-item": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.1.tgz", + "integrity": "sha512-KZ4KLmPdABXOsfnM6JHUIjxEvcx2ulk656Z/4Balw071/5qgnhz+H1uGtf2zIGnrnvDC8xR4Fj9uKbjAFGNIeA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-heading-style": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-heading-style/-/mdast-util-heading-style-2.0.0.tgz", + "integrity": "sha512-q9+WW2hJduW51LgV2r/fcU5wIt2GLFf0yYHxyi0f2aaxnC63ErBSOAJlhP6nbQ6yeG5rTCozbwOi4QNDPKV0zw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz", + "integrity": "sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", + "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", + "dev": true + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromark": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", + "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", + "dev": true, + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", + "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-extension-frontmatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.0.0.tgz", + "integrity": "sha512-EXjmRnupoX6yYuUJSQhrQ9ggK0iQtQlpi6xeJzVD5xscyAI+giqco5fdymayZhJMbIFecjnE2yz85S9NzIgQpg==", + "dev": true, + "requires": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-extension-gfm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.1.tgz", + "integrity": "sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA==", + "dev": true, + "requires": { + "micromark-extension-gfm-autolink-literal": "^1.0.0", + "micromark-extension-gfm-footnote": "^1.0.0", + "micromark-extension-gfm-strikethrough": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.0", + "micromark-extension-gfm-tagfilter": "^1.0.0", + "micromark-extension-gfm-task-list-item": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-gfm-autolink-literal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.3.tgz", + "integrity": "sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-footnote": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", + "dev": true, + "requires": { + "micromark-core-commonmark": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-strikethrough": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.4.tgz", + "integrity": "sha512-/vjHU/lalmjZCT5xt7CcHVJGq8sYRm80z24qAKXzaHzem/xsDYb2yLL+NNVbYvmpLx3O7SYPuGL5pzusL9CLIQ==", + "dev": true, + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-table": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz", + "integrity": "sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-tagfilter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.1.tgz", + "integrity": "sha512-Ty6psLAcAjboRa/UKUbbUcwjVAv5plxmpUTy2XC/3nJFL37eHej8jrHrRzkqcpipJliuBH30DTs7+3wqNcQUVA==", + "dev": true, + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-gfm-task-list-item": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.3.tgz", + "integrity": "sha512-PpysK2S1Q/5VXi72IIapbi/jliaiOFzv7THH4amwXeYXLq3l1uo8/2Be0Ac1rEwK20MQEsGH2ltAZLNY2KI/0Q==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", + "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", + "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-space": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", + "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", + "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", + "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", + "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", + "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", + "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", + "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", + "dev": true, + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", + "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", + "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", + "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", + "dev": true + }, + "micromark-util-html-tag-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", + "dev": true + }, + "micromark-util-normalize-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", + "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", + "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", + "dev": true, + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", + "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", + "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", + "dev": true, + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", + "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", + "dev": true + }, + "micromark-util-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", + "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "mongodb": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.1.tgz", + "integrity": "sha512-/NyiM3Ox9AwP5zrfT9TXjRKDJbXlLaUDQ9Rg//2lbg8D2A8GXV0VidYYnA/gfdK6uwbnL4FnAflH7FbGw3TS7w==", + "requires": { + "bson": "^4.6.5", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.5.2", + "saslprep": "^1.0.3", + "socks": "^2.6.2" + } + }, + "mongodb-connection-string-url": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.3.tgz", + "integrity": "sha512-f+/WsED+xF4B74l3k9V/XkTVj5/fxFH2o5ToKXd8Iyi5UhM+sO9u0Ape17Mvl/GkZaFtM0HQnzAG5OTmhKw+tQ==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "mongodb-memory-server": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-8.8.0.tgz", + "integrity": "sha512-1cy/N4RC7mH/T8Go6J/vbbOWg5d2YR3DZCnhrRJkhQMUQmEIdU62jqLTepViW7A2LKKweopkfb4PiKAFJV/Z1A==", + "dev": true, + "requires": { + "mongodb-memory-server-core": "8.8.0", + "tslib": "^2.4.0" + } + }, + "mongodb-memory-server-core": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-8.8.0.tgz", + "integrity": "sha512-pguLQes27cFhiqs/rRqINst1y2zCMGVNrVEREbvFtKaMTQUh40TU1XeOrVesqu1qYFGHYntYCOzlJueUtl62qQ==", + "dev": true, + "requires": { + "@types/tmp": "^0.2.3", + "async-mutex": "^0.3.2", + "camelcase": "^6.3.0", + "debug": "^4.3.4", + "find-cache-dir": "^3.3.2", + "get-port": "^5.1.1", + "https-proxy-agent": "^5.0.1", + "md5-file": "^5.0.0", + "mongodb": "~4.7.0", + "new-find-package-json": "^2.0.0", + "semver": "^7.3.7", + "tar-stream": "^2.1.4", + "tmp": "^0.2.1", + "tslib": "^2.4.0", + "uuid": "^8.3.1", + "yauzl": "^2.10.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "mongodb": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.7.0.tgz", + "integrity": "sha512-HhVar6hsUeMAVlIbwQwWtV36iyjKd9qdhY+s4wcU8K6TOj4Q331iiMy+FoPuxEntDIijTYWivwFJkLv8q/ZgvA==", + "dev": true, + "requires": { + "bson": "^4.6.3", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.5.2", + "saslprep": "^1.0.3", + "socks": "^2.6.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "msw": { + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-0.44.2.tgz", + "integrity": "sha512-u8wjzzcMWouoZtuIShCwx4M3wFF5sBAV1f8K4a0WX8kiihFjzl89IKE1VYmTclLyMIwpOq8qQ1HTpuh2BFX/3A==", + "dev": true, + "requires": { + "@mswjs/cookies": "^0.2.2", + "@mswjs/interceptors": "^0.17.2", + "@open-draft/until": "^1.0.3", + "@types/cookie": "^0.4.1", + "@types/js-levenshtein": "^1.1.1", + "chalk": "4.1.1", + "chokidar": "^3.4.2", + "cookie": "^0.4.2", + "graphql": "^16.3.0", + "headers-polyfill": "^3.0.4", + "inquirer": "^8.2.0", + "is-node-process": "^1.0.1", + "js-levenshtein": "^1.1.6", + "node-fetch": "^2.6.7", + "outvariant": "^1.3.0", + "path-to-regexp": "^6.2.0", + "statuses": "^2.0.0", + "strict-event-emitter": "^0.2.0", + "type-fest": "^1.2.2", + "yargs": "^17.3.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "named-app-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/named-app-errors/-/named-app-errors-4.0.0.tgz", + "integrity": "sha512-S8X3M3R9Ps1D0Cy8t/McPVxih9bX39QrX5wf2s3V1u6yubqTOlUYgLEcHQYgQxYmXRvqEwIUSGqenUBNIHlrmw==" + }, + "nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "dev": true + }, + "nano-css": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.5.tgz", + "integrity": "sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==", + "requires": { + "css-tree": "^1.1.2", + "csstype": "^3.0.6", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^6.0.0", + "rtl-css-js": "^1.14.0", + "sourcemap-codec": "^1.4.8", + "stacktrace-js": "^2.0.2", + "stylis": "^4.0.6" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true + }, + "new-find-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", + "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "next": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-12.2.4.tgz", + "integrity": "sha512-b1xlxEozmAWokAXzXsi5vlmU/IfJcFNIJA8dpU5UdkFbyDPio8wwb8mAQ/Y7rGtfTgG/t/u49BiyEA+xAgFvow==", + "requires": { + "@next/env": "12.2.4", + "@next/swc-android-arm-eabi": "12.2.4", + "@next/swc-android-arm64": "12.2.4", + "@next/swc-darwin-arm64": "12.2.4", + "@next/swc-darwin-x64": "12.2.4", + "@next/swc-freebsd-x64": "12.2.4", + "@next/swc-linux-arm-gnueabihf": "12.2.4", + "@next/swc-linux-arm64-gnu": "12.2.4", + "@next/swc-linux-arm64-musl": "12.2.4", + "@next/swc-linux-x64-gnu": "12.2.4", + "@next/swc-linux-x64-musl": "12.2.4", + "@next/swc-win32-arm64-msvc": "12.2.4", + "@next/swc-win32-ia32-msvc": "12.2.4", + "@next/swc-win32-x64-msvc": "12.2.4", + "@swc/helpers": "0.4.3", + "caniuse-lite": "^1.0.30001332", + "postcss": "8.4.14", + "styled-jsx": "5.0.2", + "use-sync-external-store": "1.2.0" + } + }, + "next-test-api-route-handler": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/next-test-api-route-handler/-/next-test-api-route-handler-3.1.7.tgz", + "integrity": "sha512-ds4VH7TOMRY8iT0vAnOi88uXsIGk9r6Yf/VrZPGHuG1ceRuTHIN3pyzKaPtQO2MisHH4z9ZDXmGrh3NJgq56ag==", + "dev": true, + "requires": { + "cookie": "^0.5.0", + "node-fetch": "^2.6.7" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + } + } + }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-8.15.0.tgz", + "integrity": "sha512-sFXrMiO07eDWUb/e5ni2yNvtz2hePKqSyukUxYcQv0QHjyXCe+zKP7af/bISjcvsgRBWGyivk5V3KCZ0vg8J3Q==", + "dev": true, + "requires": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^5.0.4", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/config": "^4.2.0", + "@npmcli/fs": "^2.1.0", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.1.7", + "abbrev": "~1.1.1", + "archy": "~1.0.0", + "cacache": "^16.1.1", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.2", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.12", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^5.0.0", + "ini": "^3.0.0", + "init-package-json": "^3.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^6.0.2", + "libnpmdiff": "^4.0.2", + "libnpmexec": "^4.0.2", + "libnpmfund": "^3.0.1", + "libnpmhook": "^8.0.2", + "libnpmorg": "^4.0.2", + "libnpmpack": "^4.0.2", + "libnpmpublish": "^6.0.2", + "libnpmsearch": "^5.0.2", + "libnpmteam": "^4.0.2", + "libnpmversion": "^3.0.1", + "make-fetch-happen": "^10.2.0", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^9.0.0", + "nopt": "^5.0.0", + "npm-audit-report": "^3.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.1.0", + "npm-pick-manifest": "^7.0.1", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.3.0", + "npm-user-validate": "^1.0.1", + "npmlog": "^6.0.2", + "opener": "^1.5.2", + "p-map": "^4.0.0", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.2", + "proc-log": "^2.0.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^5.0.1", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^2.0.0", + "validate-npm-package-name": "^4.0.0", + "which": "^2.0.2", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "@colors/colors": { + "version": "1.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "@gar/promisify": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "@isaacs/string-locale-compare": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "@npmcli/arborist": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "requires": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.0", + "cacache": "^16.0.6", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "4.2.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/fs": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + } + }, + "@npmcli/metavuln-calculator": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "@npmcli/package-json": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "@npmcli/promise-spawn": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "4.1.7", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aproba": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "bin-links": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "builtins": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "cacache": { + "version": "16.1.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + } + }, + "chalk": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, + "cli-table3": { + "version": "0.6.2", + "bundled": true, + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "color-support": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "columnify": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "debug": { + "version": "4.3.4", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "depd": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "bundled": true, + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "4.0.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "glob": { + "version": "8.0.3", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "bundled": true, + "dev": true + }, + "has": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore-walk": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + } + }, + "ip": { + "version": "1.1.8", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true, + "dev": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "just-diff": { + "version": "5.0.3", + "bundled": true, + "dev": true + }, + "just-diff-apply": { + "version": "5.3.1", + "bundled": true, + "dev": true + }, + "libnpmaccess": { + "version": "6.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + } + }, + "libnpmdiff": { + "version": "4.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/disparity-colors": "^2.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^5.0.1", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1", + "tar": "^6.1.0" + } + }, + "libnpmexec": { + "version": "4.0.8", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^5.0.0", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/run-script": "^4.1.3", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^9.0.1", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "proc-log": "^2.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + } + }, + "libnpmfund": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^5.0.0" + } + }, + "libnpmhook": { + "version": "8.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + } + }, + "libnpmorg": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + } + }, + "libnpmpack": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^4.1.3", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1" + } + }, + "libnpmpublish": { + "version": "6.0.4", + "bundled": true, + "dev": true, + "requires": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + } + }, + "libnpmsearch": { + "version": "5.0.3", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^13.0.0" + } + }, + "libnpmteam": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + } + }, + "libnpmversion": { + "version": "3.0.6", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^3.0.0", + "@npmcli/run-script": "^4.1.3", + "json-parse-even-better-errors": "^2.3.1", + "proc-log": "^2.0.0", + "semver": "^7.3.7" + } + }, + "lru-cache": { + "version": "7.12.0", + "bundled": true, + "dev": true + }, + "make-fetch-happen": { + "version": "10.2.0", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "3.3.4", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "bundled": true, + "dev": true + }, + "node-gyp": { + "version": "9.0.0", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "nopt": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "npm-audit-report": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "9.1.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + } + }, + "npm-packlist": { + "version": "5.1.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^1.1.2", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "7.0.1", + "bundled": true, + "dev": true, + "requires": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + } + }, + "npm-profile": { + "version": "6.2.0", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0" + } + }, + "npm-registry-fetch": { + "version": "13.3.0", + "bundled": true, + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "6.0.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "13.6.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + } + }, + "parse-conflict-json": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "proc-log": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "read-package-json": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "7.3.7", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "7.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "bundled": true, + "dev": true + }, + "ssri": { + "version": "9.0.1", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.11", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "treeverse": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + } + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "outvariant": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.3.0.tgz", + "integrity": "sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ==", + "dev": true + }, + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true + }, + "p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "requires": { + "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true + }, + "p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "dev": true, + "requires": { + "p-timeout": "^3.0.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "propose": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/propose/-/propose-0.0.5.tgz", + "integrity": "sha512-Jary1vb+ap2DIwOGfyiadcK4x1Iu3pzpkDBy8tljFPmQvnc9ES3m1PMZOMiWOG50cfoAyYNtGeBzrp+Rlh4G9A==", + "dev": true, + "requires": { + "levenshtein-edit-distance": "^1.0.0" + } + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "random-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-case/-/random-case-1.0.0.tgz", + "integrity": "sha512-bMlDjeg8GB38Ju0hK8eII3qPQucgFm+sFWCmucwnHlgeCqm99uhmwR3d+dU3sJV2Dn+bp7XnFUygUidrRbM4Vg==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "requires": {} + }, + "react-use": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.4.0.tgz", + "integrity": "sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==", + "requires": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.3.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "dev": true, + "requires": { + "rc": "1.2.8" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "remark": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz", + "integrity": "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "remark-parse": "^10.0.0", + "remark-stringify": "^10.0.0", + "unified": "^10.0.0" + } + }, + "remark-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-11.0.0.tgz", + "integrity": "sha512-8JEWwArXquRq1/In4Ftz7gSG9Scwb1ijT2/dEuBETW9omqhmMRxcfjZ3iKqrak3BnCJeZSXCdWEmPhFKC8+RUQ==", + "dev": true, + "requires": { + "remark": "^14.0.0", + "unified-args": "^10.0.0" + } + }, + "remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-gfm": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", + "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-gfm": "^2.0.0", + "micromark-extension-gfm": "^2.0.0", + "unified": "^10.0.0" + } + }, + "remark-lint-final-newline": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-final-newline/-/remark-lint-final-newline-2.1.1.tgz", + "integrity": "sha512-cgKYaI7ujUse/kV4KajLv2j1kmi1CxpAu+w7wIU0/Faihhb3sZAf4a5ACf2Wu8NoTSIr1Q//3hDysG507PIoDg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0" + } + }, + "remark-lint-hard-break-spaces": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-hard-break-spaces/-/remark-lint-hard-break-spaces-3.1.1.tgz", + "integrity": "sha512-UfwFvESpX32qwyHJeluuUuRPWmxJDTkmjnWv2r49G9fC4Jrzm4crdJMs3sWsrGiQ3mSex6bgp/8rqDgtBng2IA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-no-auto-link-without-protocol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-auto-link-without-protocol/-/remark-lint-no-auto-link-without-protocol-3.1.1.tgz", + "integrity": "sha512-lCjBuoSUWjN1kO0J7vqQgn7HUF/WeOHOqc3oiq9LMRXIovKWqPCBi77o8Npv8KfV+JXeRl+hrupfbhJXlLturA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-no-blockquote-without-marker": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-blockquote-without-marker/-/remark-lint-no-blockquote-without-marker-5.1.1.tgz", + "integrity": "sha512-7jL7eKS25kKRhQ7SKKB5eRfNleDMWKWAmZ5Y/votJdDoM+6qsopLLumPWaSzP0onyV3dyHRhPfBtqelt3hvcyA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile-location": "^4.0.0" + } + }, + "remark-lint-no-duplicate-definitions": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-duplicate-definitions/-/remark-lint-no-duplicate-definitions-3.1.1.tgz", + "integrity": "sha512-9p+nBz8VvV+t4g/ALNLVN8naV+ffAzC4ADyg9QivzmKwLjyF93Avt4HYNlb2GZ+aoXRQSVG1wjjWFeDC9c7Tdg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-no-heading-content-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-heading-content-indent/-/remark-lint-no-heading-content-indent-4.1.1.tgz", + "integrity": "sha512-W4zF7MA72IDC5JB0qzciwsnioL5XlnoE0r1F7sDS0I5CJfQtHYOLlxb3UAIlgRCkBokPWCp0E4o1fsY/gQUKVg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-heading-style": "^2.0.0", + "pluralize": "^8.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-no-inline-padding": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-inline-padding/-/remark-lint-no-inline-padding-4.1.1.tgz", + "integrity": "sha512-++IMm6ohOPKNOrybqjP9eiclEtVX/Rd2HpF2UD9icrC1X5nvrI6tlfN55tePaFvWAB7pe6MW4LzNEMnWse61Lw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-no-undefined-references": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-undefined-references/-/remark-lint-no-undefined-references-4.1.1.tgz", + "integrity": "sha512-J20rKfTGflLiTI3T5JlLZSmINk6aDGmZi1y70lpU69LDfAyHAKgDK6sSW9XDeFmCPPdm8Ybxe5Gf2a70k+GcVQ==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile-location": "^4.0.0" + } + }, + "remark-lint-no-unused-definitions": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-unused-definitions/-/remark-lint-no-unused-definitions-3.1.1.tgz", + "integrity": "sha512-/GtyBukhAxi5MEX/g/m+FzDEflSbTe2/cpe2H+tJZyDmiLhjGXRdwWnPRDp+mB9g1iIZgVRCk7T4v90RbQX/mw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-lint-ordered-list-marker-style": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-lint-ordered-list-marker-style/-/remark-lint-ordered-list-marker-style-3.1.1.tgz", + "integrity": "sha512-IWcWaJoaSb4yoSOuvDbj9B2uXp9kSj58DqtrMKo8MoRShmbj1onVfulTxoTLeLtI11NvW+mj3jPSpqjMjls+5Q==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^2.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-parse": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", + "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-reference-links": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-6.0.1.tgz", + "integrity": "sha512-34wY2C6HXSuKVTRtyJJwefkUD8zBOZOSHFZ4aSTnU2F656gr9WeuQ2dL6IJDK3NPd2F6xKF2t4XXcQY9MygAXg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-stringify": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz", + "integrity": "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-validate-links": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/remark-validate-links/-/remark-validate-links-12.0.0.tgz", + "integrity": "sha512-mBNfsGBPO3A4CYnHDd3fpnr+A2Ep71moc0UlxC6lhZSJslMAU3UZypKK3DLlxUPoLy9TlJaEVEnfy6qpGQU7sg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "github-slugger": "^1.0.0", + "hosted-git-info": "^5.0.0", + "mdast-util-to-string": "^3.0.0", + "propose": "0.0.5", + "to-vfile": "^7.0.0", + "trough": "^2.0.0", + "unified": "^10.0.0", + "unified-engine": "^10.0.1", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.0.0.tgz", + "integrity": "sha512-rRnjWu0Bxj+nIfUOkz0695C0H6tRrN5iYIzYejb0tDEefe2AekHu/U5Kn9pEie5vsJqpNQU02az7TGSH3qpz4Q==", + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + } + }, + "lru-cache": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", + "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", + "dev": true + } + } + }, + "request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "requires": { + "global-dirs": "^0.1.1" + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "roarr": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-7.11.0.tgz", + "integrity": "sha512-DKiMaEYHoOZ0JyD4Ohr5KRnqybQ162s3ZL/WNO9oy6EUszYvpp0eLYJErc/U4NI96HYnHsbROhFaH4LYuJPnDg==", + "dev": true, + "requires": { + "boolean": "^3.1.4", + "fast-json-stringify": "^2.7.10", + "fast-printf": "^1.6.9", + "fast-safe-stringify": "^2.1.1", + "globalthis": "^1.0.2", + "semver-compare": "^1.0.0" + } + }, + "rtl-css-js": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.0.tgz", + "integrity": "sha512-Oc7PnzwIEU4M0K1J4h/7qUUaljXhQ0kCObRsZjxs2HjkpKsnoTMvSmvJ4sqgJZd0zBoEfAyTdnK/jMIYvrjySQ==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "requires": { + "mri": "^1.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==" + }, + "semantic-release": { + "version": "https://xunn.at/semantic-release-atam", + "integrity": "sha512-vNB9WbhgxFqkPcZr6z8s2NcBb2g5d+cw3ReZWUx/lCdp1HQX6r33BJbejxIpojRz7i/5ukUubJ9NhFjX6sqwKQ==", + "dev": true, + "requires": { + "@semantic-release/commit-analyzer": "^9.0.2", + "@semantic-release/error": "^3.0.0", + "@semantic-release/github": "^8.0.0", + "@semantic-release/npm": "^9.0.0", + "@semantic-release/release-notes-generator": "^10.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^5.0.0", + "figures": "^3.0.0", + "find-versions": "^4.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^4.0.0", + "lodash": "^4.17.21", + "marked": "^4.0.10", + "marked-terminal": "^5.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^16.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + } + }, + "semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", + "dev": true + }, + "set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "requires": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "simple-git": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.12.0.tgz", + "integrity": "sha512-cy1RSRFHGZSrlYa3MnUuNVOXLUdifEZD2X8+AZjg8mKCdRvtCFSga6acq5N2g0ggb8lH3jBi369MrFZ+Y6sfsA==", + "dev": true, + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + } + }, + "sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true + } + } + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "sort-package-json": { + "version": "https://xunn.at/sort-package-json", + "integrity": "sha512-efTk1wTodMGOvLTM+HX2HveURBKttzewX+v/q0t0fHNecJLnbf2SIbYhQ16Z/r/+Em/+6Sphusi2QGTgWxzrEQ==", + "dev": true, + "requires": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "dependencies": { + "detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "spellchecker": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/spellchecker/-/spellchecker-3.7.1.tgz", + "integrity": "sha512-j36QRZrekxPXy58fo2B/Le3GzHryLv9Zq2Hqz907+JmUBCP35tJlwwhCo4n1lwisBDK40IFHqEHPUe5gwUkpwA==", + "dev": true, + "requires": { + "any-promise": "^1.3.0", + "nan": "^2.14.0" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==" + } + } + }, + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "requires": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "strict-event-emitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.4.tgz", + "integrity": "sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ==", + "dev": true, + "requires": { + "events": "^3.3.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-similarity": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", + "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "styled-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.2.tgz", + "integrity": "sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==", + "requires": {} + }, + "stylis": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz", + "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "swr": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz", + "integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==", + "requires": {} + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "synckit": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.1.tgz", + "integrity": "sha512-rJEeygO5PNmcZICmrgnbOd2usi5zWE1ESc0Gn5tTmJlongoU8zCTwMFQtar2UgMSiR68vK9afPQ+uVs2lURSIA==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.0", + "tslib": "^2.4.0" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "tempfile": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-3.0.0.tgz", + "integrity": "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==", + "dev": true, + "requires": { + "temp-dir": "^2.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, + "requires": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "test-listen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-listen/-/test-listen-1.1.0.tgz", + "integrity": "sha512-OyEVi981C1sb9NX1xayfgZls3p8QTDRwp06EcgxSgd1kktaENBW8dO15i8v/7Fi15j0IYQctJzk5J+hyEBId2w==", + "dev": true + }, + "text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "to-vfile": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-7.2.3.tgz", + "integrity": "sha512-QO0A9aE6Z/YkmQadJ0syxpmNXtcQiu0qAtCKYKD5cS3EfgfFTAXfgLX6AOaBrSfWSek5nfsMf3gBZ9KGVFcLuw==", + "dev": true, + "requires": { + "is-buffer": "^2.0.0", + "vfile": "^5.1.0" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "toss-expression": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/toss-expression/-/toss-expression-0.1.1.tgz", + "integrity": "sha512-gcLSA2aZOjZgGkODu/Ac+ke1UiY5zcrfsajrEUra7AZzotbNEUOGHSKfhR/4l2Jf6gjy8PRxGy/G5/I4lm9aXQ==" + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "dev": true + }, + "ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + } + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz", + "integrity": "sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "typedoc": { + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", + "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.0.18", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "typedoc-plugin-markdown": { + "version": "3.13.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.4.tgz", + "integrity": "sha512-E/EBBmu6ARtnbswZGtBVBB/BfukZiGMOlqPc0RXCI/NFitONBahFqbCAF5fKQlijlcfipJj5pw5AMFH3NytrAw==", + "dev": true, + "requires": { + "handlebars": "^4.7.7" + } + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==" + }, + "uglify-js": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.3.tgz", + "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", + "dev": true, + "optional": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + } + } + }, + "unified-args": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/unified-args/-/unified-args-10.0.0.tgz", + "integrity": "sha512-PqsqxwkXpGSLiMkbjNnKU33Ffm6gso6rAvz1TlBGzMBx3gpx7ewIhViBX8HEWmy0v7pebA5PM6RkRWWaYmtfYw==", + "dev": true, + "requires": { + "@types/text-table": "^0.2.0", + "camelcase": "^7.0.0", + "chalk": "^5.0.0", + "chokidar": "^3.0.0", + "fault": "^2.0.0", + "json5": "^2.0.0", + "minimist": "^1.0.0", + "text-table": "^0.2.0", + "unified-engine": "^10.0.0" + }, + "dependencies": { + "camelcase": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", + "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", + "dev": true + }, + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true + } + } + }, + "unified-engine": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/unified-engine/-/unified-engine-10.0.1.tgz", + "integrity": "sha512-lsj7VC8kNWhK87rGBhidklk4llgrEdJoOZHoQFbTZQ/fA22JqowUPM10bEf05eSZOR6UnUSrZ/mPWHrQsHGm7g==", + "dev": true, + "requires": { + "@types/concat-stream": "^2.0.0", + "@types/debug": "^4.0.0", + "@types/is-empty": "^1.0.0", + "@types/node": "^18.0.0", + "@types/unist": "^2.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.0.0", + "fault": "^2.0.0", + "glob": "^8.0.0", + "ignore": "^5.0.0", + "is-buffer": "^2.0.0", + "is-empty": "^1.0.0", + "is-plain-obj": "^4.0.0", + "load-plugin": "^5.0.0", + "parse-json": "^6.0.0", + "to-vfile": "^7.0.0", + "trough": "^2.0.0", + "unist-util-inspect": "^7.0.0", + "vfile-message": "^3.0.0", + "vfile-reporter": "^7.0.0", + "vfile-statistics": "^2.0.0", + "yaml": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + }, + "lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "parse-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-6.0.2.tgz", + "integrity": "sha512-SA5aMiaIjXkAiBrW/yPgLgQAQg42f7K3ACO+2l/zOvtQBwX58DMUsFJXelW2fx3yMBmWOVkR6j1MGsdSbCA4UA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^2.3.1", + "lines-and-columns": "^2.0.2" + } + }, + "yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true + } + } + }, + "unified-lint-rule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/unified-lint-rule/-/unified-lint-rule-2.1.1.tgz", + "integrity": "sha512-vsLHyLZFstqtGse2gvrGwasOmH8M2y+r2kQMoDSWzSqUkQx2MjHjvZuGSv5FUaiv4RQO1bHRajy7lSGp7XWq5A==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "trough": "^2.0.0", + "unified": "^10.0.0", + "vfile": "^5.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "unist-util-generated": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.0.tgz", + "integrity": "sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw==", + "dev": true + }, + "unist-util-inspect": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/unist-util-inspect/-/unist-util-inspect-7.0.1.tgz", + "integrity": "sha512-gEPeSrsYXus8012VJ00p9uZC8D0iogtLLiHlBgvS61hU22KNKduQhMKezJm83viHlLf3TYS2y9SDEFglWPDMKw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-is": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz", + "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==", + "dev": true + }, + "unist-util-position": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz", + "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + } + }, + "unist-util-visit-parents": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz", + "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + }, + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true + } + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vfile": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", + "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + } + }, + "vfile-location": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.0.1.tgz", + "integrity": "sha512-JDxPlTbZrZCQXogGheBHjbRWjESSPEak770XwWPfw5mTc1v1nWGLB/apzZxsx8a0SJVfF8HK8ql8RD308vXRUw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + } + }, + "vfile-message": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + }, + "vfile-reporter": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.4.tgz", + "integrity": "sha512-4cWalUnLrEnbeUQ+hARG5YZtaHieVK3Jp4iG5HslttkVl+MHunSGNAIrODOTLbtjWsNZJRMCkL66AhvZAYuJ9A==", + "dev": true, + "requires": { + "@types/supports-color": "^8.0.0", + "string-width": "^5.0.0", + "supports-color": "^9.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-sort": "^3.0.0", + "vfile-statistics": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true + } + } + }, + "vfile-sort": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-3.0.0.tgz", + "integrity": "sha512-fJNctnuMi3l4ikTVcKpxTbzHeCgvDhnI44amA3NVDvA6rTC6oKCFpCVyT5n2fFMr3ebfr+WVQZedOCd73rzSxg==", + "dev": true, + "requires": { + "vfile-message": "^3.0.0" + } + }, + "vfile-statistics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-2.0.0.tgz", + "integrity": "sha512-foOWtcnJhKN9M2+20AOTlWi2dxNfAoeNIoxD5GXcO182UJyId4QrXa41fWrgcfV3FWTjdEDy3I4cpLVcQscIMA==", + "dev": true, + "requires": { + "vfile-message": "^3.0.0" + } + }, + "vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", + "dev": true + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dev": true, + "requires": { + "@zxing/text-encoding": "0.9.0", + "util": "^0.12.3" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz", + "integrity": "sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==", + "requires": { + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^6.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + } + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true + }, + "webpack-node-module-types": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/webpack-node-module-types/-/webpack-node-module-types-1.2.2.tgz", + "integrity": "sha512-cn1R6CS8UjjtDx/B/hbq7PrNoOXn8cWNrvnpMWC1+Jwv0XiVx+qnuBYInDLqAvoXN3Qo6wYmsnrRLzoo0wlwAg==" + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zwitch": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz", + "integrity": "sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b9ffc5f --- /dev/null +++ b/package.json @@ -0,0 +1,177 @@ +{ + "name": "blogpress.api.hscc.bdpa.org", + "version": "1.2.0", + "private": true, + "description": "https://blogpress.api.hscc.bdpa.org", + "homepage": "https://blogpress.api.hscc.bdpa.org", + "repository": { + "type": "git", + "url": "https://github.com/nhscc/blogpress.api.hscc.bdpa.org", + "lens": "next" + }, + "license": "MIT", + "author": "Xunnamius", + "type": "commonjs", + "scripts": { + "__test-repeat-all": "echo 'Repeating test suite [initializing]...'; (i=0; while [ \"$((( i += 1 ) <= 100 ))\" -ne 0 ]; do sleep 0.1 && echo \"\\r\\033[1A\\033[0KRepeating test suite [run $i/100]...\" && JEST_SILENT_REPORTER_SHOW_WARNINGS=true NODE_ENV=test npx jest --reporters=jest-silent-reporter || exit; done) && echo \"All tests passed! Congrats!\"", + "__test-repeat-unit": "echo 'Repeating test suite [initializing]...'; (i=0; while [ \"$((( i += 1 ) <= 100 ))\" -ne 0 ]; do sleep 0.1 && echo \"\\r\\033[1A\\033[0KRepeating test suite [run $i/100]...\" && JEST_SILENT_REPORTER_SHOW_WARNINGS=true NODE_ENV=test npx jest --reporters=jest-silent-reporter --testPathIgnorePatterns '/(integration|e2e).*?\\.test\\.tsx?' '/dist/' || exit; done) && echo \"All tests passed! Congrats!\"", + "build": "npm run build-dist --", + "build-changelog": "conventional-changelog --outfile CHANGELOG.md --config conventional.config.js --release-count 0 --skip-unstable && (if [ \"$CHANGELOG_SKIP_TITLE\" != 'true' ]; then { node -e 'console.log(require(\"./conventional.config.js\").changelogTitle)'; cat CHANGELOG.md; } > CHANGELOG.md.ignore && mv CHANGELOG.md.ignore CHANGELOG.md; fi) && remark -o --use reference-links --use gfm --use frontmatter CHANGELOG.md && prettier --write CHANGELOG.md", + "build-dist": "if [ -r ./next.config.js ]; then next build; else NODE_ENV=production tsc --project tsconfig.types.json && NODE_ENV=production webpack --config-name lib && if [ -r ./src/cli.ts ]; then NODE_ENV=production webpack --config-name cli && chmod +x ./dist/cli.js; fi && NODE_ENV=esm babel src --extensions .ts --out-dir dist/esm --out-file-extension .mjs; fi", + "build-docs": "if [ -r ./next.config.js ]; then typedoc --plugin typedoc-plugin-markdown --out docs --readme none lib src test types external-scripts --exclude '**/*.test.*' --exclude external-scripts/bin; else ENTRY=`node -e 'console.log((x => typeof x==\"string\"?x:x.default)(require(\"./package.json\").exports[\".\"]).replace(/\\.\\/dist\\/(.+)\\.[a-zA-Z0-9]+$/, \"./src/$1.ts\"))'` && echo 'Entry file:' \"$ENTRY\" && typedoc --plugin typedoc-plugin-markdown --out docs --readme none $ENTRY && find docs -name '*.md' -exec sed -i -e 's/Project: //g' {} + && sed -i -e 1,4d docs/README.md; fi && find docs -name '*.md' -exec sed -i -e 's/`__namedParameters`/`\\(destructured\\)`/g' {} + && find docs -name '*.md' -exec sed -i -E 's/`__namedParameters\\.([^`]+)`/`\\({ \\1 }\\)`/g' {} +", + "build-externals": "NODE_ENV=external webpack --config-name externals", + "build-stats": "NODE_ENV=production webpack --json > bundle-stats.ignore.json", + "clean": "git ls-files --exclude-standard --ignored --others --directory | grep -vE '^((\\.(env|vscode|husky))|next-env\\.d\\.ts|node_modules)($|\\/)' | xargs -p rm -rf", + "dev": "next -p `npx -q acquire-port`", + "format": "sort-package-json && remark -o --use reference-links --use gfm --use frontmatter '{{,.}*.md,!(node_modules)/**/{,.}*.md,.*/**/{,.}*.md}' && prettier --write '{{,.}*.md,!(node_modules)/**/{,.}*.md,.*/**/{,.}*.md}' && if [ -z \"$ALLOW_DISABLED_LINKS\" ] && grep -qR --exclude-dir=node_modules --include='*.md' '\\\\\\[[^\\]*\\]\\\\' .; then echo '---\nWARNING: disabled links were found in the following files:'; grep -R --color=always --exclude-dir=node_modules --include='*.md' '\\\\\\[[^\\]*\\]\\\\' .; echo '(to ignore this error, run this command again with ALLOW_DISABLED_LINKS=1)'; exit 1; fi", + "lint": "stdbuf -i0 -o0 -e0 tsc --project tsconfig.lint.json; X=$?; stdbuf -i0 -o0 -e0 eslint --parser-options=project:tsconfig.lint.json src; Y=$?; remark --quiet --use gfm --use frontmatter --use lint-final-newline --use lint-no-auto-link-without-protocol --use lint-no-blockquote-without-marker --use lint-ordered-list-marker-style --use lint-hard-break-spaces --use lint-no-duplicate-definitions --use lint-no-heading-content-indent --use lint-no-inline-padding --use lint-no-undefined-references --use lint-no-unused-definitions --use validate-links '{{,.}*.md,!(node_modules)/**/{,.}*.md,.*/**/{,.}*.md}'; Z=$?; [ $X -eq 0 ] && [ $Y -eq 0 ] && [ $Z -eq 0 ]", + "list-tasks": "node -e 'console.log(Object.keys(require(\"./package.json\").scripts).join(\"\\n\"))'", + "prepare": "if [ -z \"$CI\" ] && ([ -z \"$NODE_ENV\" ] || [ \"$NODE_ENV\" = \"development\" ]); then npx --no husky install; else echo 'skipped installing husky git hooks'; fi", + "start": "next start", + "test": "npm run test-unit --", + "test-all": "NODE_ENV=test jest $JEST_CLI --coverage", + "test-e2e": "NODE_ENV=test jest $JEST_CLI '/e2e.*?\\.test\\.tsx?' --testPathIgnorePatterns '/dist/'", + "test-integration": "NODE_ENV=test jest $JEST_CLI '/integration.*?\\.test\\.tsx?' --testPathIgnorePatterns '/dist/'", + "test-integration-client": "NODE_ENV=test jest $JEST_CLI '/integration-client.*?\\.test\\.tsx?' --testPathIgnorePatterns '/dist/'", + "test-integration-compile": "NODE_ENV=test jest $JEST_CLI '/integration-compile.*?\\.test\\.tsx?' --testPathIgnorePatterns '/dist/'", + "test-integration-node": "NODE_ENV=test jest $JEST_CLI '/integration-node.*?\\.test\\.tsx?' --testPathIgnorePatterns '/dist/'", + "test-repeat-all": "npm run __test-repeat-all --silent", + "test-repeat-unit": "npm run __test-repeat-unit --silent", + "test-unit": "NODE_ENV=test jest $JEST_CLI --coverage --testPathIgnorePatterns '/(integration|e2e).*?\\.test\\.tsx?' '/dist/'" + }, + "config": { + "mongodbMemoryServer": { + "version": "5.0.9" + } + }, + "dependencies": { + "@babel/core": "^7.18.10", + "@babel/plugin-proposal-export-default-from": "^7.18.10", + "@babel/plugin-proposal-function-bind": "^7.18.9", + "@babel/plugin-transform-react-jsx-source": "^7.18.6", + "@babel/preset-env": "^7.18.10", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@next/bundle-analyzer": "^12.2.4", + "@types/bytes": "^3.1.1", + "@types/cors": "^2.8.12", + "@types/debug": "^4.1.7", + "@types/node": "^18.6.5", + "@types/react": "^18.0.17", + "@types/request-ip": "^0.0.37", + "@xunnamius/next-types": "^1.0.9", + "@xunnamius/types": "^1.3.0", + "babel-plugin-explicit-exports-references": "^1.0.2", + "babel-plugin-transform-default-named-imports": "^1.2.2", + "bytes": "^3.1.2", + "clone-deep": "^4.0.1", + "content-type": "^1.0.4", + "cors": "^2.8.5", + "find-up": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-server-side": "^1.0.2", + "mongodb": "^4.8.1", + "named-app-errors": "^4.0.0", + "next": "^12.2.4", + "node-fetch": "cjs", + "raw-body": "^2.5.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-use": "^17.4.0", + "request-ip": "3.3.0", + "swr": "^1.3.0", + "toss-expression": "^0.1.1", + "typescript": "^4.7.4" + }, + "devDependencies": { + "@babel/cli": "^7.18.10", + "@babel/eslint-parser": "^7.18.9", + "@commitlint/cli": "^17.0.3", + "@commitlint/config-conventional": "^17.0.3", + "@next/eslint-plugin-next": "^12.2.4", + "@semantic-release/changelog": "^6.0.1", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.3.0", + "@types/clone-deep": "^4.0.1", + "@types/confusing-browser-globals": "^1.0.0", + "@types/content-type": "^1.1.5", + "@types/inquirer": "^9.0.0", + "@types/jest": "^28.1.6", + "@types/jsonfile": "^6.1.0", + "@types/node-fetch": "^2.5.12", + "@types/semver": "^7.3.10", + "@types/tar-stream": "^2.2.2", + "@types/test-listen": "^1.1.0", + "@types/webpack": "^5.28.0", + "@typescript-eslint/eslint-plugin": "^5.33.0", + "@typescript-eslint/parser": "^5.33.0", + "@xunnamius/conventional-changelog-projector": "^1.1.1", + "@xunnamius/jest-types": "^1.1.3", + "auto-bind": "^4.0.0", + "babel-jest": "^28.1.3", + "babel-loader": "^8.2.5", + "chalk": "^4.1.2", + "confusing-browser-globals": "^1.0.11", + "conventional-changelog-cli": "^2.2.2", + "dotenv": "^16.0.1", + "eslint": "^8.21.0", + "eslint-config-next": "^12.2.4", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-import-resolver-typescript": "^3.4.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jest": "^26.8.2", + "eslint-plugin-jest-dom": "^4.0.2", + "eslint-plugin-react": "^7.30.1", + "execa": "^5.1.1", + "html-entities": "^2.3.3", + "http-terminator": "^3.2.0", + "husky": "^8.0.1", + "inquirer": "^8.2.4", + "jest": "^28.1.3", + "jest-circus": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "jest-extended": "^3.0.2", + "jest-silent-reporter": "^0.5.0", + "jsonfile": "^6.1.0", + "lint-staged": "^13.0.3", + "mongodb-memory-server": "^8.8.0", + "msw": "^0.44.2", + "next-test-api-route-handler": "^3.1.7", + "prettier": "^2.7.1", + "random-case": "^1.0.0", + "remark-cli": "^11.0.0", + "remark-frontmatter": "^4.0.1", + "remark-gfm": "^3.0.1", + "remark-lint-final-newline": "^2.1.1", + "remark-lint-hard-break-spaces": "^3.1.1", + "remark-lint-no-auto-link-without-protocol": "^3.1.1", + "remark-lint-no-blockquote-without-marker": "^5.1.1", + "remark-lint-no-duplicate-definitions": "^3.1.1", + "remark-lint-no-heading-content-indent": "^4.1.1", + "remark-lint-no-inline-padding": "^4.1.1", + "remark-lint-no-undefined-references": "^4.1.1", + "remark-lint-no-unused-definitions": "^3.1.1", + "remark-lint-ordered-list-marker-style": "^3.1.1", + "remark-reference-links": "^6.0.1", + "remark-validate-links": "^12.0.0", + "semantic-release": "https://xunn.at/semantic-release-atam", + "simple-git": "^3.12.0", + "sort-package-json": "https://xunn.at/sort-package-json", + "spellchecker": "^3.7.1", + "test-listen": "^1.1.0", + "text-extensions": "^2.4.0", + "type-fest": "^2.18.0", + "typedoc": "^0.23.10", + "typedoc-plugin-markdown": "^3.13.4", + "unfetch": "^4.2.0", + "unique-filename": "^1.1.1", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0", + "webpack-node-externals": "^3.0.0" + }, + "engines": { + "node": ">=16" + } +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..ba7ab22 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + endOfLine: 'lf', + printWidth: 80, + proseWrap: 'always', + semi: true, + singleQuote: true, + tabWidth: 2, + trailingComma: 'none', + overrides: [ + { + files: '**/*.@(ts|?(@(c|m))js)?(x)', + options: { + parser: 'babel-ts', + printWidth: 86 + } + } + ] +}; diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/release.config.js b/release.config.js new file mode 100644 index 0000000..7b06067 --- /dev/null +++ b/release.config.js @@ -0,0 +1,99 @@ +'use strict'; + +const debug = require('debug')( + `${require('./package.json').name}:semantic-release-config` +); + +// TODO: turn this into @xunnamius/semantic-release-projector-config + +const updateChangelog = + process.env.UPDATE_CHANGELOG === 'true' || + // ? Legacy + process.env.SHOULD_UPDATE_CHANGELOG === 'true'; + +debug(`will update changelog: ${updateChangelog ? 'yes' : 'no'}`); + +const { + changelogTitle, + parserOpts, + writerOpts +} = require('./conventional.config.js'); + +module.exports = { + branches: [ + '+([0-9])?(.{+([0-9]),x}).x', + 'main', + { + name: 'canary', + channel: 'canary', + prerelease: true + } + ], + plugins: [ + [ + '@semantic-release/commit-analyzer', + { + parserOpts, + releaseRules: [ + // ? releaseRules are checked first; if none match, defaults are + // ? checked next. + + // ! These two lines must always appear first and in order: + { breaking: true, release: 'major' }, + { revert: true, release: 'patch' }, + + // * Custom release rules, if any, may appear next: + { type: 'build', release: 'patch' } + ] + } + ], + [ + '@semantic-release/release-notes-generator', + { + parserOpts, + writerOpts + } + ], + ...(updateChangelog + ? [ + [ + '@semantic-release/exec', + { + prepareCmd: 'CHANGELOG_SKIP_TITLE=true npm run build-changelog' + } + ], + ['@semantic-release/changelog', { changelogTitle }], + [ + '@semantic-release/exec', + { + prepareCmd: + 'remark -o --use reference-links --use gfm --use frontmatter CHANGELOG.md' + } + ], + [ + '@semantic-release/exec', + { + prepareCmd: 'npx prettier --write CHANGELOG.md' + } + ] + ] + : []), + ['@semantic-release/npm'], + [ + '@semantic-release/git', + { + assets: [ + 'package.json', + 'package-lock.json', + 'npm-shrinkwrap.json', + 'CHANGELOG.md', + 'docs' + ], + message: 'release: ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}' + } + ], + ['@semantic-release/github'] + ] +}; + +debug('exports: %O', module.exports); diff --git a/spellcheck-commit.js b/spellcheck-commit.js new file mode 100644 index 0000000..646bfce --- /dev/null +++ b/spellcheck-commit.js @@ -0,0 +1,116 @@ +'use strict'; +/* eslint-disable no-console */ + +const spellcheck = require('spellchecker'); +const pkg = require('./package.json'); +const read = require('fs').promises.readFile; +const execa = require('execa'); + +const debug = require('debug')(`${require('./package.json').name}:spellcheck-commit`); + +const tryToRead = async (path) => { + try { + debug(`attempting to read ${path}`); + const data = await read(path); + debug(`successfully read ${path}`); + return data; + } catch (ignored) { + debug(`failed to read ${path}`); + return ''; + } +}; + +const asJson = (str) => { + try { + const json = JSON.parse(str.toString('utf-8')); + debug('json parse successful!'); + return [ + ...(json?.['cSpell.words'] || []), + ...(json?.['cSpell.userWords'] || []), + ...(json?.['cSpell.ignoreWords'] || []) + ]; + } catch (ignored) { + debug('json parse failed!'); + return []; + } +}; + +const asText = (str) => str.toString('utf-8').split('\n'); +const isPascalCase = (w) => /^([A-Z]{2,}.+|[A-Z][a-z]+[A-Z].*)$/.test(w); +const isCamelCase = (w) => /^[a-z]+[A-Z]+.*$/.test(w); +const isAllCaps = (w) => /^[^a-z]+$/.test(w); + +const splitOutWords = (phrase) => + [...phrase.split(/[^a-zA-Z]+/g), phrase].filter(Boolean); + +const keys = (obj) => (obj ? Object.keys(obj).map(splitOutWords) : []); + +(async () => { + const lastCommitMsg = (await read('./.git/COMMIT_EDITMSG')).toString('utf-8'); + const homeDir = require('os').homedir(); + + debug(`lastCommitMsg: ${lastCommitMsg}`); + debug(`homeDir: ${homeDir}`); + + const ignoreWords = Array.from( + new Set( + [ + ...(await Promise.all([ + tryToRead('./.spellcheckignore').then(asText), + tryToRead(`${homeDir}/.config/_spellcheckignore`).then(asText), + tryToRead('./.vscode/settings.json').then(asJson), + tryToRead(`${homeDir}/.config/Code/User/settings.json`).then(asJson) + ])), + ...require('text-extensions'), + // ? Popular contractions + ...['ve', 're', 's', 'll', 't', 'd', 'o', 'ol'], + ...keys(pkg.dependencies), + ...keys(pkg.devDependencies), + ...keys(pkg.scripts), + ...splitOutWords( + (await execa('git', ['log', '--format="%B"', 'HEAD~1'])).stdout + ).slice(0, -1) + ] + .flat() + .filter(Boolean) + .map((word) => splitOutWords(word.trim().toLowerCase())) + .flat() + ) + ); + + const typos = Array.from( + new Set( + spellcheck + .checkSpelling(lastCommitMsg) + .map((typoLocation) => + lastCommitMsg.slice(typoLocation.start, typoLocation.end).trim().split("'") + ) + .flat() + .filter((w) => !isAllCaps(w) && !isCamelCase(w) && !isPascalCase(w)) + .map((w) => w.toLowerCase()) + .filter((typo) => !ignoreWords.includes(typo)) + ) + ); + + debug('typos: %O', typos); + + if (typos.length) { + console.warn('WARNING: there may be misspelled words in your commit message!'); + console.warn( + 'Commit messages can be fixed before push with `git commit -S --amend`' + ); + console.warn('---'); + + for (const typo of typos.slice(0, 5)) { + const corrections = spellcheck.getCorrectionsForMisspelling(typo); + const suggestion = corrections.length + ? ` (did you mean ${corrections.slice(0, 5).join(', ')}?)` + : ''; + + console.warn(`${typo}${suggestion}`); + } + + typos.length > 5 && console.warn(`${typos.length - 5} more...`); + typos.length && console.warn('---'); + } +})(); diff --git a/src/backend/api.ts b/src/backend/api.ts new file mode 100644 index 0000000..72d0869 --- /dev/null +++ b/src/backend/api.ts @@ -0,0 +1,17 @@ +import { getEnv } from 'universe/backend/env'; +import type { PageConfig } from 'next'; + +/** + * The default app-wide Next.js API configuration object. + * + * @see https://nextjs.org/docs/api-routes/api-middlewares#custom-config + */ +export const defaultConfig: PageConfig = { + api: { + bodyParser: { + get sizeLimit() { + return getEnv().MAX_CONTENT_LENGTH_BYTES; + } + } + } +}; diff --git a/src/backend/db.ts b/src/backend/db.ts new file mode 100644 index 0000000..8106405 --- /dev/null +++ b/src/backend/db.ts @@ -0,0 +1,911 @@ +import { getCommonSchemaConfig } from 'multiverse/mongo-common'; + +import type { Document, ObjectId, WithId, WithoutId } from 'mongodb'; +import type { UnixEpochMs } from '@xunnamius/types'; +import { DbSchema, getDb } from 'multiverse/mongo-schema'; +import { GuruMeditationError } from 'named-app-errors'; + +/** + * A generic projection specification. + */ +type Projection = { [key in keyof InternalAnswer]?: unknown } & Document; + +/** + * A JSON representation of the backend Mongo database structure. This is used + * for consistent app-wide db access across projects and to generate transient + * versions of the db during testing. + */ +export function getSchemaConfig(): DbSchema { + return getCommonSchemaConfig({ + databases: { + 'hscc-api-blogpress': { + collections: [ + { + name: 'users', + // ? Collation allows for case-insensitive searching. See: + // ? https://stackoverflow.com/a/40914924/1367414 + createOptions: { collation: { locale: 'en', strength: 2 } }, + indices: [ + { spec: 'key' }, + { + spec: 'username', + options: { unique: true } + }, + { + spec: 'email', + options: { unique: true } + } + ] + }, + { + name: 'mail', + indices: [{ spec: 'sender' }, { spec: 'receiver' }] + }, + { + name: 'questions', + indices: [ + { spec: 'creator' }, + { spec: 'title-lowercase' }, + { spec: 'createdAt' }, + { spec: 'status' }, + { spec: 'upvotes' }, + { spec: 'upvoterUsernames' }, + { spec: 'downvotes' }, + { spec: 'downvoterUsernames' }, + { spec: 'answers' }, + { spec: 'answerItems._id' }, + { spec: 'answerItems.upvoterUsernames' }, + { spec: 'answerItems.downvoterUsernames' }, + { spec: 'answerItems.commentItems._id' }, + { spec: 'answerItems.commentItems.upvoterUsernames' }, + { spec: 'answerItems.commentItems.downvoterUsernames' }, + { spec: 'comments' }, + { spec: 'commentItems._id' }, + { spec: 'commentItems.upvoterUsernames' }, + { spec: 'commentItems.downvoterUsernames' }, + { spec: 'views' }, + { spec: 'sorter.uvc' }, + { spec: 'sorter.uvac' } + ] + } + ] + } + }, + aliases: { + app: 'hscc-api-blogpress' + } + }); +} + +export type Username = string; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UserId extends ObjectId {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface MailId extends ObjectId {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface QuestionId extends ObjectId {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AnswerId extends ObjectId {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CommentId extends ObjectId {} + +/** + * The shape of an update operation on a question's views total. + */ +export type ViewsUpdateOperation = 'increment'; + +/** + * The shape of an update operation on a question or comment's + * upvotes/downvotes. + */ +export type VotesUpdateOperation = { + op: 'increment' | 'decrement'; + target: 'upvotes' | 'downvotes'; +}; + +/** + * The shape of an update operation on a user's points total. + */ +export type PointsUpdateOperation = { + op: 'increment' | 'decrement'; + amount: number; +}; + +/** + * The shape of an internal application user. + */ +export type InternalUser = WithId<{ + username: Username; + salt: string; + email: string; + key: string; + points: number; + questionIds: QuestionId[]; + answerIds: [questionId: QuestionId, answerId: AnswerId][]; +}>; + +/** + * The shape of a public application user. + */ +export type PublicUser = Omit< + WithoutId, + 'key' | 'questionIds' | 'answerIds' +> & { + user_id: string; + questions: number; + answers: number; +}; + +/** + * The shape of a new application user. + */ +export type NewUser = Partial< + Omit, 'points' | 'questionIds' | 'answerIds'> +>; + +/** + * The shape of a patch application user. + */ +export type PatchUser = Partial< + Omit< + WithoutId, + 'username' | 'questionIds' | 'answerIds' | 'points' + > & { + points: + | InternalUser['points'] + | { + [key in keyof PointsUpdateOperation]?: PointsUpdateOperation[key] extends string + ? string + : PointsUpdateOperation[key]; + }; + } +>; + +/** + * The shape of internal mail. + */ +export type InternalMail = WithId<{ + sender: Username; + receiver: Username; + createdAt: UnixEpochMs; + subject: string; + text: string; +}>; + +/** + * The shape of public mail. + */ +export type PublicMail = WithoutId & { + mail_id: string; +}; + +/** + * The shape of new mail. + */ +export type NewMail = Partial, 'createdAt'>>; + +/** + * Valid internal question statuses. + */ +export const questionStatuses = ['open', 'closed', 'protected'] as const; + +/** + * The shape of an internal question. + */ +export type InternalQuestion = WithId<{ + creator: Username; + title: string; + 'title-lowercase': string; + createdAt: UnixEpochMs; + text: string; + status: typeof questionStatuses[number]; + hasAcceptedAnswer: boolean; + upvotes: number; + upvoterUsernames: Username[]; + downvotes: number; + downvoterUsernames: Username[]; + answers: number; + answerItems: InternalAnswer[]; + views: number; + comments: number; + commentItems: InternalComment[]; + sorter: { + uvc: number; + uvac: number; + }; +}>; + +/** + * The shape of a public question. + */ +export type PublicQuestion = Omit< + WithoutId, + | 'title-lowercase' + | 'upvoterUsernames' + | 'downvoterUsernames' + | 'answerItems' + | 'commentItems' + | 'sorter' +> & { + question_id: string; +}; + +/** + * The shape of a new question. + */ +export type NewQuestion = Omit< + WithoutId, + | 'createdAt' + | 'title-lowercase' + | 'createdAt' + | 'status' + | 'hasAcceptedAnswer' + | 'upvotes' + | 'upvoterUsernames' + | 'downvotes' + | 'downvoterUsernames' + | 'answers' + | 'answerItems' + | 'comments' + | 'commentItems' + | 'views' + | 'sorter' +>; + +/** + * The shape of a patch question. + */ +export type PatchQuestion = Partial< + Omit< + WithoutId, + | 'creator' + | 'title-lowercase' + | 'createdAt' + | 'hasAcceptedAnswer' + | 'upvoterUsernames' + | 'downvoterUsernames' + | 'answers' + | 'answerItems' + | 'comments' + | 'commentItems' + | 'views' + | 'sorter' + > & { views: InternalQuestion['views'] | ViewsUpdateOperation } +>; + +/** + * The shape of an internal answer. + */ +export type InternalAnswer = WithId<{ + creator: Username; + createdAt: UnixEpochMs; + text: string; + accepted: boolean; + upvotes: number; + upvoterUsernames: Username[]; + downvotes: number; + downvoterUsernames: Username[]; + commentItems: InternalComment[]; +}>; + +/** + * The shape of a public answer. + */ +export type PublicAnswer = Omit< + WithoutId, + 'upvoterUsernames' | 'downvoterUsernames' | 'commentItems' +> & { + answer_id: string; + question_id: string; + comments: number; +}; + +/** + * The shape of a new answer. + */ +export type NewAnswer = Omit< + WithoutId, + | 'createdAt' + | 'accepted' + | 'upvotes' + | 'upvoterUsernames' + | 'downvotes' + | 'downvoterUsernames' + | 'commentItems' +>; + +/** + * The shape of a patch answer. + */ +export type PatchAnswer = Partial< + Omit< + WithoutId, + | 'creator' + | 'createdAt' + | 'upvoterUsernames' + | 'downvoterUsernames' + | 'commentItems' + > +>; + +/** + * The shape of an internal comment. + */ +export type InternalComment = WithId<{ + creator: Username; + createdAt: UnixEpochMs; + text: string; + upvotes: number; + upvoterUsernames: Username[]; + downvotes: number; + downvoterUsernames: Username[]; +}>; + +/** + * The shape of a public comment. + */ +export type PublicComment = Omit< + WithoutId, + 'upvoterUsernames' | 'downvoterUsernames' +> & { + comment_id: string; +}; + +/** + * The shape of a new comment. + */ +export type NewComment = Omit< + WithoutId, + 'createdAt' | 'upvotes' | 'upvoterUsernames' | 'downvotes' | 'downvoterUsernames' +>; + +/** + * Transforms an internal user into a public user. + */ +export function toPublicUser(internalUser: InternalUser): PublicUser { + return { + user_id: internalUser._id.toString(), + username: internalUser.username, + email: internalUser.email, + salt: internalUser.salt, + points: internalUser.points, + answers: internalUser.answerIds.length, + questions: internalUser.questionIds.length + }; +} + +/** + * Transforms internal mail into a public mail. + */ +export function toPublicMail(internalMail: InternalMail): PublicMail { + return { + mail_id: internalMail._id.toString(), + sender: internalMail.sender, + receiver: internalMail.receiver, + createdAt: internalMail.createdAt, + subject: internalMail.subject, + text: internalMail.text + }; +} + +/** + * Transforms an internal question into a public question. + */ +export function toPublicQuestion(internalQuestion: InternalQuestion): PublicQuestion { + return { + question_id: internalQuestion._id.toString(), + creator: internalQuestion.creator, + createdAt: internalQuestion.createdAt, + title: internalQuestion.title, + text: internalQuestion.text, + status: internalQuestion.status, + answers: internalQuestion.answerItems.length, + comments: internalQuestion.commentItems.length, + upvotes: internalQuestion.upvotes, + downvotes: internalQuestion.downvotes, + views: internalQuestion.views, + hasAcceptedAnswer: internalQuestion.hasAcceptedAnswer + }; +} + +/** + * Transforms an internal answer into a public answer. + */ +export function toPublicAnswer( + internalAnswer: InternalAnswer, + questionId: QuestionId +): PublicAnswer { + return { + answer_id: internalAnswer._id.toString(), + question_id: questionId.toString(), + creator: internalAnswer.creator, + createdAt: internalAnswer.createdAt, + accepted: internalAnswer.accepted, + text: internalAnswer.text, + upvotes: internalAnswer.upvotes, + downvotes: internalAnswer.downvotes, + comments: internalAnswer.commentItems.length + }; +} + +/** + * Transforms an internal comment into a public comment. + */ +export function toPublicComment(internalComment: InternalComment): PublicComment { + return { + comment_id: internalComment._id.toString(), + creator: internalComment.creator, + createdAt: internalComment.createdAt, + text: internalComment.text, + upvotes: internalComment.upvotes, + downvotes: internalComment.downvotes + }; +} + +/** + * A MongoDB cursor projection that transforms an internal user into a public + * user. + */ +export const publicUserProjection = { + _id: false, + user_id: { $toString: '$_id' }, + username: true, + salt: true, + email: true, + points: true, + answers: { $size: '$answerIds' }, + questions: { $size: '$questionIds' } +} as const; + +/** + * A MongoDB cursor projection that transforms internal mail into public mail. + */ +export const publicMailProjection = { + _id: false, + mail_id: { $toString: '$_id' }, + sender: true, + receiver: true, + createdAt: true, + subject: true, + text: true +} as const; + +/** + * A MongoDB cursor projection that transforms an internal question into a + * public question. + */ +export const publicQuestionProjection = { + _id: false, + question_id: { $toString: '$_id' }, + creator: true, + createdAt: true, + title: true, + text: true, + status: true, + answers: true, + comments: true, + upvotes: true, + downvotes: true, + views: true, + hasAcceptedAnswer: true +} as const; + +/** + * A MongoDB cursor projection that transforms an internal answer into a public + * answer. + */ +export const publicAnswerProjection = (questionId: QuestionId) => + ({ + _id: false, + answer_id: { $toString: '$_id' }, + question_id: questionId.toString(), + creator: true, + createdAt: true, + accepted: true, + text: true, + upvotes: true, + downvotes: true, + comments: { $size: '$commentItems' } + } as const); + +/** + * A MongoDB aggregation expression that maps an internal answer into a public + * answer. + */ +export const publicAnswerMap = (variable: string, questionId: QuestionId) => + ({ + answer_id: { $toString: `$$${variable}._id` }, + question_id: questionId.toString(), + creator: `$$${variable}.creator`, + createdAt: `$$${variable}.createdAt`, + accepted: `$$${variable}.accepted`, + text: `$$${variable}.text`, + upvotes: `$$${variable}.upvotes`, + downvotes: `$$${variable}.downvotes`, + comments: { $size: `$$${variable}.commentItems` } + } as const); + +/** + * A MongoDB cursor projection that transforms an internal comment into a public + * comment. + */ +export const publicCommentProjection = { + _id: false, + comment_id: { $toString: '$_id' }, + creator: true, + createdAt: true, + text: true, + upvotes: true, + downvotes: true +} as const; + +/** + * A MongoDB aggregation expression that maps an internal comment into a public + * comment. + */ +export const publicCommentMap = (variable: string) => + ({ + comment_id: { $toString: `$$${variable}._id` }, + creator: `$$${variable}.creator`, + createdAt: `$$${variable}.createdAt`, + text: `$$${variable}.text`, + upvotes: `$$${variable}.upvotes`, + downvotes: `$$${variable}.downvotes` + } as const); + +/** + * A meaningless MongoDB cursor projection used for existence checking without + * wasting the bandwidth to pull down all of the data that might be embedded + * within an object's fields. + */ +export const vacuousProjection = { exists: { $literal: true } }; + +/** + * A type representing how a user voted on a question, answer, or comment. + */ +export type VoterStatus = 'upvoted' | 'downvoted' | null; + +/** + * A type representing the result of applying the voterStatusProjection output + * via cursor projection. + */ +export type VoterStatusResult = { voterStatus: VoterStatus } | null; + +/** + * A MongoDB cursor projection that evaluates an internal question, answer, or + * comment and returns how the specified user voted on said item. + */ +export function voterStatusProjection(username: Username) { + return { + _id: false, + voterStatus: { + $switch: { + branches: [ + { + case: { $in: [username, '$upvoterUsernames'] }, + then: 'upvoted' + }, + { + case: { $in: [username, '$downvoterUsernames'] }, + then: 'downvoted' + } + ], + default: null + } + } + }; +} + +/** + * The shape of vote count update operation authorization information. + */ +export type SelectResult = + | (NonNullable & { + isCreator: boolean; + }) + | null; + +/** + * A MongoDB cursor projection that returns select information about an internal + * question, answer, or comment for the purpose of vote update operation + * authorization. + */ +export function selectResultProjection(username: Username) { + return { + ...voterStatusProjection(username), + isCreator: { $eq: [username, '$creator'] } + }; +} + +async function genericSelectAggregation({ + questionId, + answerId, + answer_creator, + commentId, + projection +}: { + questionId: QuestionId | undefined; + answerId: AnswerId | undefined; + answer_creator?: Username | undefined; + commentId: CommentId | undefined; + projection: Projection | undefined; +}): Promise { + /* istanbul ignore next */ + if ((!answerId && !answer_creator && !commentId) || (answerId && answer_creator)) { + throw new GuruMeditationError('illegal parameter combination'); + } + + return (await getDb({ name: 'app' })) + .collection('questions') + .aggregate([ + { $match: { _id: questionId } }, + ...(answerId || answer_creator + ? [ + { + $project: { + answer: { + $first: { + $filter: { + input: '$answerItems', + as: 'answer', + cond: answer_creator + ? { $eq: ['$$answer.creator', answer_creator] } + : { $eq: ['$$answer._id', answerId] } + } + } + } + } + }, + { + $replaceWith: '$answer' + } + ] + : []), + ...(commentId + ? [ + { + $project: { + comment: { + $first: { + $filter: { + input: '$commentItems', + as: 'comment', + cond: { $eq: ['$$comment._id', commentId] } + } + } + } + } + }, + { + $replaceWith: '$comment' + } + ] + : []), + ...(projection ? [{ $project: projection }] : /* istanbul ignore next */ []) + ]) + .next() as Promise; +} + +/** + * Returns a nested answer object via aggregation pipeline, optionally applying + * a projection to the result. + */ +export async function selectAnswerFromDb({ + questionId, + answerId, + answer_creator, + projection +}: { + questionId: QuestionId; + answerId?: AnswerId; + answer_creator?: Username; + projection?: Projection; +}): Promise { + return genericSelectAggregation({ + questionId: questionId, + answerId: answerId, + answer_creator, + commentId: undefined, + projection + }); +} + +/** + * Returns a nested comment object via aggregation pipeline, optionally applying + * a projection to the result. + */ +export async function selectCommentFromDb({ + questionId, + answerId, + commentId, + projection +}: { + questionId: QuestionId; + answerId?: AnswerId; + commentId: CommentId; + projection?: Projection; +}): Promise { + return genericSelectAggregation({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }); +} + +/** + * Adds a nested answer object to a question document. + */ +export async function addAnswerToDb({ + questionId, + answer +}: { + questionId: QuestionId; + answer: InternalAnswer; +}) { + return (await getDb({ name: 'app' })) + .collection('questions') + .updateOne( + { _id: questionId }, + { + $inc: { answers: 1, 'sorter.uvac': 1 }, + $push: { answerItems: answer } + } + ); +} + +/** + * Adds a nested comment object to a question document. + */ +export async function addCommentToDb({ + questionId, + answerId, + comment +}: { + questionId: QuestionId; + answerId?: AnswerId; + comment: InternalComment; +}) { + const db = (await getDb({ name: 'app' })).collection('questions'); + + if (answerId) { + return db.updateOne( + { _id: questionId }, + { $push: { 'answerItems.$[answer].commentItems': comment } }, + { arrayFilters: [{ 'answer._id': answerId }] } + ); + } else { + return db.updateOne( + { _id: questionId }, + { + $inc: { comments: 1, 'sorter.uvc': 1, 'sorter.uvac': 1 }, + $push: { commentItems: comment } + } + ); + } +} + +/** + * Helper function that flattens an update specification for a sub-object so + * that it may be used in a MongoDB update function. + * + * `updateOps` must be a valid MongoDB update document, e.g. `{ $set: ... }`. + */ +function translateToFlatUpdateOps(updateOps: Document, predicate: string) { + return Object.entries(updateOps).reduce((newUpdateOps, [updateOp, opSpec]) => { + newUpdateOps[updateOp] ??= {}; + + Object.entries(opSpec).forEach(([targetField, updateVal]) => { + newUpdateOps[updateOp][`${predicate}.${targetField}`] = updateVal; + }); + + return newUpdateOps; + }, {} as Document); +} + +/** + * Patches a nested answer object in a question document. + */ +export async function patchAnswerInDb({ + questionId, + answerId, + updateOps +}: { + questionId: QuestionId; + answerId: AnswerId; + updateOps: Document; +}) { + return (await getDb({ name: 'app' })) + .collection('questions') + .updateOne( + { _id: questionId }, + translateToFlatUpdateOps(updateOps, 'answerItems.$[answer]'), + { arrayFilters: [{ 'answer._id': answerId }] } + ); +} + +/** + * Patches a nested comment object in a question document. + */ +export async function patchCommentInDb({ + questionId, + answerId, + commentId, + updateOps +}: { + questionId: QuestionId; + answerId?: AnswerId; + commentId: CommentId; + updateOps: Document; +}) { + const db = (await getDb({ name: 'app' })).collection('questions'); + + if (answerId) { + return db.updateOne( + { _id: questionId }, + translateToFlatUpdateOps( + updateOps, + 'answerItems.$[answer].commentItems.$[comment]' + ), + { arrayFilters: [{ 'answer._id': answerId }, { 'comment._id': commentId }] } + ); + } else { + return db.updateOne( + { _id: questionId }, + translateToFlatUpdateOps(updateOps, 'commentItems.$[comment]'), + { arrayFilters: [{ 'comment._id': commentId }] } + ); + } +} + +/** + * Deletes a nested answer object from a question document. + */ +export async function removeAnswerFromDb({ + questionId, + answerId +}: { + questionId: QuestionId; + answerId: AnswerId; +}) { + return (await getDb({ name: 'app' })) + .collection('questions') + .updateOne( + { _id: questionId }, + { + $inc: { answers: -1, 'sorter.uvac': -1 }, + $pull: { answerItems: { _id: answerId } } + } + ); +} + +/** + * Deletes a nested comment object from a question document. + */ +export async function removeCommentFromDb({ + questionId, + answerId, + commentId +}: { + questionId: QuestionId; + answerId?: AnswerId; + commentId: CommentId; +}) { + const db = (await getDb({ name: 'app' })).collection('questions'); + + if (answerId) { + return db.updateOne( + { _id: questionId }, + { $pull: { 'answerItems.$[answer].commentItems': { _id: commentId } } }, + { arrayFilters: [{ 'answer._id': answerId }] } + ); + } else { + return db.updateOne( + { _id: questionId }, + { + $inc: { comments: -1, 'sorter.uvc': -1, 'sorter.uvac': -1 }, + $pull: { commentItems: { _id: commentId } } + } + ); + } +} diff --git a/src/backend/env.ts b/src/backend/env.ts new file mode 100644 index 0000000..84816f8 --- /dev/null +++ b/src/backend/env.ts @@ -0,0 +1,109 @@ +import { parse as parseAsBytes } from 'bytes'; +import { getEnv as getDefaultEnv } from 'multiverse/next-env'; +import { InvalidAppEnvironmentError } from 'universe/error'; + +import type { Environment } from 'multiverse/next-env'; + +/** + * Returns an object representing the application's runtime environment. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export function getEnv() { + const env = getDefaultEnv({ + STACKAPPS_INTERVAL_PERIOD_MS: Number( + process.env.STACKAPPS_INTERVAL_PERIOD_MS || 0 + ), + STACKAPPS_MAX_REQUESTS_PER_INTERVAL: + Number(process.env.STACKAPPS_MAX_REQUESTS_PER_INTERVAL) || null, + STACKAPPS_TOTAL_API_GENERATED_QUESTIONS: + Number(process.env.STACKAPPS_TOTAL_API_GENERATED_QUESTIONS) || null, + STACKAPPS_COLLECTALL_QUESTION_ANSWERS: + Number(process.env.STACKAPPS_COLLECTALL_QUESTION_ANSWERS) || null, + STACKAPPS_COLLECTALL_QUESTION_COMMENTS: + Number(process.env.STACKAPPS_COLLECTALL_QUESTION_COMMENTS) || null, + STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS: + Number(process.env.STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS) || null, + STACKAPPS_MAX_PAGE_SIZE: Number(process.env.STACKAPPS_MAX_PAGE_SIZE) || null, + STACKAPPS_AUTH_KEY: process.env.STACKAPPS_AUTH_KEY || null, + MAX_PARAMS_PER_REQUEST: Number(process.env.MAX_PARAMS_PER_REQUEST) || 100, + MIN_USER_NAME_LENGTH: Number(process.env.MIN_USER_NAME_LENGTH) || 4, + MAX_USER_NAME_LENGTH: Number(process.env.MAX_USER_NAME_LENGTH) || 16, + MIN_USER_EMAIL_LENGTH: Number(process.env.MIN_USER_EMAIL_LENGTH) || 4, + MAX_USER_EMAIL_LENGTH: Number(process.env.MAX_USER_EMAIL_LENGTH) || 75, + USER_SALT_LENGTH: Number(process.env.USER_SALT_LENGTH) || 32, + USER_KEY_LENGTH: Number(process.env.USER_KEY_LENGTH) || 128, + MAX_COMMENT_LENGTH: Number(process.env.MAX_COMMENT_LENGTH) || 150, + MAX_QUESTION_TITLE_LENGTH: Number(process.env.MAX_QUESTION_TITLE_LENGTH) || 150, + MAX_QUESTION_BODY_LENGTH_BYTES: + parseAsBytes(process.env.MAX_QUESTION_BODY_LENGTH_BYTES ?? '-Infinity') || 3000, + MAX_ANSWER_BODY_LENGTH_BYTES: + parseAsBytes(process.env.MAX_ANSWER_BODY_LENGTH_BYTES ?? '-Infinity') || 3000, + MAX_MAIL_SUBJECT_LENGTH: + parseAsBytes(process.env.MAX_MAIL_SUBJECT_LENGTH ?? '-Infinity') || 75, + MAX_MAIL_BODY_LENGTH_BYTES: + parseAsBytes(process.env.MAX_MAIL_BODY_LENGTH_BYTES ?? '-Infinity') || 512, + + PRUNE_DATA_MAX_MAIL_BYTES: + parseAsBytes(process.env.PRUNE_DATA_MAX_MAIL_BYTES ?? '-Infinity') || null, + PRUNE_DATA_MAX_QUESTIONS_BYTES: + parseAsBytes(process.env.PRUNE_DATA_MAX_QUESTIONS_BYTES ?? '-Infinity') || null, + PRUNE_DATA_MAX_USERS_BYTES: + parseAsBytes(process.env.PRUNE_DATA_MAX_USERS_BYTES ?? '-Infinity') || null + }); + + // TODO: retire all of the following logic when expect-env is created. Also, + // TODO: expect-env should have the ability to skip runs on certain NODE_ENV + // TODO: unless OVERRIDE_EXPECT_ENV is properly defined. + /* istanbul ignore next */ + if ( + (env.NODE_ENV != 'test' && env.OVERRIDE_EXPECT_ENV != 'force-no-check') || + env.OVERRIDE_EXPECT_ENV == 'force-check' + ) { + const errors: string[] = []; + + ( + [ + 'MAX_PARAMS_PER_REQUEST', + 'MAX_USER_NAME_LENGTH', + 'MAX_USER_EMAIL_LENGTH', + 'MIN_USER_EMAIL_LENGTH', + 'USER_SALT_LENGTH', + 'USER_KEY_LENGTH', + 'MAX_COMMENT_LENGTH', + 'MAX_QUESTION_TITLE_LENGTH', + 'MAX_QUESTION_BODY_LENGTH_BYTES', + 'MAX_ANSWER_BODY_LENGTH_BYTES', + 'MAX_MAIL_BODY_LENGTH_BYTES' + ] as (keyof typeof env)[] + ).forEach((name) => { + const value = env[name]; + if (!value || value <= 0) { + errors.push( + `bad ${name}, saw "${env[name]}" (expected a non-negative number)` + ); + } + }); + + if (env.MIN_USER_NAME_LENGTH >= env.MAX_USER_NAME_LENGTH) { + errors.push( + 'MIN_USER_NAME_LENGTH must be strictly less than MAX_USER_NAME_LENGTH' + ); + } + + if (env.MIN_USER_EMAIL_LENGTH >= env.MAX_USER_EMAIL_LENGTH) { + errors.push( + 'MIN_USER_EMAIL_LENGTH must be strictly less than MAX_USER_EMAIL_LENGTH' + ); + } + + // TODO: make it easier to reuse error code from getDefaultEnv. Or is it + // TODO: obsoleted by expect-env package? Either way, factor this logic out! + if (errors.length) { + throw new InvalidAppEnvironmentError( + `bad variables:\n - ${errors.join('\n - ')}` + ); + } + } + + return env as typeof env & T; +} diff --git a/src/backend/index.ts b/src/backend/index.ts new file mode 100644 index 0000000..a971a27 --- /dev/null +++ b/src/backend/index.ts @@ -0,0 +1,2082 @@ +import { MongoServerError, ObjectId } from 'mongodb'; +import { toss } from 'toss-expression'; + +import { getEnv } from 'universe/backend/env'; + +import { + addAnswerToDb, + addCommentToDb, + patchAnswerInDb, + patchCommentInDb, + publicAnswerMap, + publicAnswerProjection, + publicCommentMap, + publicMailProjection, + publicQuestionProjection, + publicUserProjection, + questionStatuses, + removeAnswerFromDb, + removeCommentFromDb, + selectAnswerFromDb, + selectCommentFromDb, + selectResultProjection, + toPublicAnswer, + toPublicComment, + toPublicMail, + toPublicQuestion, + toPublicUser, + vacuousProjection, + voterStatusProjection +} from 'universe/backend/db'; + +import { + ClientValidationError, + ErrorMessage, + InvalidItemError, + ItemNotFoundError, + ValidationError, + NotAuthorizedError +} from 'universe/error'; + +import type { + AnswerId, + CommentId, + InternalAnswer, + InternalComment, + InternalMail, + InternalQuestion, + InternalUser, + MailId, + NewAnswer, + NewComment, + NewMail, + NewQuestion, + NewUser, + PatchAnswer, + PatchQuestion, + PatchUser, + PointsUpdateOperation, + PublicAnswer, + PublicComment, + PublicMail, + PublicQuestion, + PublicUser, + QuestionId, + SelectResult, + UserId, + Username, + VoterStatus, + VoterStatusResult, + VotesUpdateOperation +} from 'universe/backend/db'; + +import { isPlainObject } from 'multiverse/is-plain-object'; +import { getDb } from 'multiverse/mongo-schema'; +import { itemExists, itemToObjectId } from 'multiverse/mongo-item'; + +import type { Document } from 'mongodb'; + +const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; +const usernameRegex = /^[a-zA-Z0-9_-]+$/; +const hexadecimalRegex = /^[a-fA-F0-9]+$/; + +/** + * Question properties that can be matched against with `searchQuestions()` + * matchers. Proxied properties should be listed in their final form. + */ +const matchableStrings = [ + 'creator', + 'title-lowercase', // * Proxied from title + 'createdAt', + 'text', + 'status', + 'hasAcceptedAnswer', + 'upvotes', + 'downvotes', + 'answers', + 'views', + 'comments' +]; + +/** + * Question properties that can be matched against with `searchQuestions()` + * regexMatchers. Must be string fields. Proxied properties should be listed in + * their final form. + */ +const regexMatchableStrings = [ + 'creator', + 'title-lowercase', // * Proxied from title + 'text', + 'status' +]; + +/** + * Whitelisted MongoDB sub-matchers that can be used with `searchQuestions()`, + * not including the special "$or" sub-matcher. + */ +const matchableSubStrings = ['$gt', '$lt', '$gte', '$lte']; + +/** + * Whitelisted MongoDB-esque sub-specifiers that can be used with + * `searchQuestions()` via the "$or" sub-matcher. + */ +type SubSpecifierObject = { + [subspecifier in '$gt' | '$lt' | '$gte' | '$lte']?: number; +}; + +/** + * The shape of a specification used to construct $inc update operations to feed + * directly to MongoDB. Used for complex updates involving the `sorter.uvc` and + * `sorter.uvac` fields. + */ +type SorterUpdateAggregationOp = { + $add: [ + original: string, + nUpdate: number, + ...sUpdates: { $subtract: (string | number)[] }[] + ]; +}; + +/** + * Validate a vote update operation object for correctness. + */ +function validateVotesUpdateOperation( + operation: unknown +): asserts operation is VotesUpdateOperation { + const rawOperation = operation as VotesUpdateOperation; + + if (!rawOperation.op || !['increment', 'decrement'].includes(rawOperation.op)) { + throw new ValidationError( + ErrorMessage.InvalidFieldValue('operation', rawOperation.op, [ + 'increment', + 'decrement' + ]) + ); + } + + if ( + !rawOperation.target || + !['upvotes', 'downvotes'].includes(rawOperation.target) + ) { + throw new ValidationError( + ErrorMessage.InvalidFieldValue('target', rawOperation.target, [ + 'upvotes', + 'downvotes' + ]) + ); + } +} + +/** + * Validate a username string for correctness. + */ +function validateUsername(username: unknown): username is Username { + return ( + typeof username == 'string' && + usernameRegex.test(username) && + username.length >= getEnv().MIN_USER_NAME_LENGTH && + username.length <= getEnv().MAX_USER_NAME_LENGTH + ); +} + +/** + * Validate a new or patch user data object. + */ +function validateUserData( + data: NewUser | PatchUser | undefined, + { required }: { required: boolean } +): asserts data is NewUser | PatchUser { + if (!isPlainObject(data)) { + throw new ValidationError(ErrorMessage.InvalidJSON()); + } + + const { + USER_KEY_LENGTH, + USER_SALT_LENGTH, + MIN_USER_EMAIL_LENGTH, + MAX_USER_EMAIL_LENGTH + } = getEnv(); + + if ( + (required || (!required && data.email !== undefined)) && + (typeof data.email != 'string' || + !emailRegex.test(data.email) || + data.email.length < MIN_USER_EMAIL_LENGTH || + data.email.length > MAX_USER_EMAIL_LENGTH) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength( + 'email', + MIN_USER_EMAIL_LENGTH, + MAX_USER_EMAIL_LENGTH, + 'string' + ) + ); + } + + if ( + (required || (!required && data.salt !== undefined)) && + (typeof data.salt != 'string' || + !hexadecimalRegex.test(data.salt) || + data.salt.length != USER_SALT_LENGTH) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('salt', USER_SALT_LENGTH, null, 'hexadecimal') + ); + } + + if ( + (required || (!required && data.key !== undefined)) && + (typeof data.key != 'string' || + !hexadecimalRegex.test(data.key) || + data.key.length != USER_KEY_LENGTH) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('key', USER_KEY_LENGTH, null, 'hexadecimal') + ); + } +} + +/** + * Validate a new or patch question data object. + */ +function validateQuestionData( + data: NewQuestion | PatchQuestion | undefined, + { required }: { required: boolean } +): asserts data is NewQuestion | PatchQuestion { + if (!isPlainObject(data)) { + throw new ValidationError(ErrorMessage.InvalidJSON()); + } + + const { + MAX_QUESTION_TITLE_LENGTH: maxTitleLen, + MAX_QUESTION_BODY_LENGTH_BYTES: maxBodyLen + } = getEnv(); + + if ( + (required || (!required && data.title !== undefined)) && + (typeof data.title != 'string' || + data.title.length < 1 || + data.title.length > maxTitleLen) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ); + } + + if ( + (required || (!required && data.text !== undefined)) && + (typeof data.text != 'string' || + data.text.length < 1 || + data.text.length > maxBodyLen) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ); + } +} + +/** + * Validate a new or patch answer data object. + */ +function validateAnswerData( + data: NewAnswer | PatchAnswer | undefined, + { required }: { required: boolean } +): asserts data is NewAnswer | PatchAnswer { + if (!isPlainObject(data)) { + throw new ValidationError(ErrorMessage.InvalidJSON()); + } + + const { MAX_ANSWER_BODY_LENGTH_BYTES: maxBodyLen } = getEnv(); + + if ( + (required || (!required && data.text !== undefined)) && + (typeof data.text != 'string' || + data.text.length < 1 || + data.text.length > maxBodyLen) + ) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ); + } +} + +export async function getAllUsers({ + after_id +}: { + after_id: string | undefined; +}): Promise { + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const afterId = after_id ? itemToObjectId(after_id) : undefined; + + if (afterId && !(await itemExists(userDb, afterId))) { + throw new ItemNotFoundError(after_id, 'user_id'); + } + + return userDb + .find(afterId ? { _id: { $lt: afterId } } : {}, { + projection: publicUserProjection, + limit: getEnv().RESULTS_PER_PAGE, + sort: { _id: -1 } + }) + .toArray(); +} + +export async function getUser({ + username +}: { + username: Username | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + + return ( + (await userDb + .find({ username }, { projection: publicUserProjection }) + .next()) || toss(new ItemNotFoundError(username, 'user')) + ); +} + +export async function getUserQuestions({ + username, + after_id +}: { + username: string | undefined; + after_id: string | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const afterId = after_id ? itemToObjectId(after_id) : undefined; + const questionDb = db.collection('questions'); + + const { questionIds } = + (await userDb + .find<{ questionIds: InternalUser['questionIds'] }>( + { username }, + { projection: { questionIds: true } } + ) + .next()) || toss(new ItemNotFoundError(username, 'user')); + + let offset: number | null = null; + + if (afterId) { + const afterIdIndex = questionIds.findIndex((q) => q.equals(afterId)); + + if (afterIdIndex < 0) { + throw new ItemNotFoundError(afterId, 'question_id'); + } + + offset = afterIdIndex; + } + + return questionDb + .find( + { + _id: { + $in: + offset !== null + ? questionIds.slice(-getEnv().RESULTS_PER_PAGE + offset, offset) + : questionIds.slice(-getEnv().RESULTS_PER_PAGE) + } + }, + { projection: publicQuestionProjection, sort: { _id: -1 } } + ) + .toArray(); +} + +export async function getUserAnswers({ + username, + after_id +}: { + username: string | undefined; + after_id: string | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const afterId = after_id ? itemToObjectId(after_id) : undefined; + + const { answerIds: answerIdTuples } = + (await userDb + .find<{ answerIds: InternalUser['answerIds'] }>( + { username }, + { projection: { answerIds: true } } + ) + .next()) || toss(new ItemNotFoundError(username, 'user')); + + let offset: number | null = null; + + if (afterId) { + const afterIdIndex = answerIdTuples.findIndex(([, a]) => a.equals(afterId)); + + if (afterIdIndex < 0) { + throw new ItemNotFoundError(afterId, 'answer_id'); + } + + offset = afterIdIndex; + } + + const targetIdTuples = + offset !== null + ? answerIdTuples.slice(-getEnv().RESULTS_PER_PAGE + offset, offset) + : answerIdTuples.slice(-getEnv().RESULTS_PER_PAGE); + + return ( + await Promise.all( + targetIdTuples.map(async ([questionId, answerId]) => { + return selectAnswerFromDb({ + questionId, + answerId, + projection: publicAnswerProjection(questionId) + }); + }) + ) + ).sort((a, b) => b.createdAt - a.createdAt); +} + +export async function createUser({ + data +}: { + data: NewUser | undefined; +}): Promise { + validateUserData(data, { required: true }); + + const { MAX_USER_NAME_LENGTH, MIN_USER_NAME_LENGTH } = getEnv(); + + if (!validateUsername(data.username)) { + throw new ValidationError( + ErrorMessage.InvalidStringLength( + 'username', + MIN_USER_NAME_LENGTH, + MAX_USER_NAME_LENGTH, + 'alphanumeric' + ) + ); + } + + const { email, username, key, salt, ...rest } = data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + + const newUser = { + _id: new ObjectId(), + username, + email, + salt: salt.toLowerCase(), + key: key.toLowerCase(), + points: 1, + answerIds: [], + questionIds: [] + }; + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + try { + await userDb.insertOne(newUser); + } catch (e) { + /* istanbul ignore else */ + if (e instanceof MongoServerError && e.code == 11000) { + if (e.keyPattern?.username !== undefined) { + throw new ValidationError(ErrorMessage.DuplicateFieldValue('username')); + } + + /* istanbul ignore else */ + if (e.keyPattern?.email !== undefined) { + throw new ValidationError(ErrorMessage.DuplicateFieldValue('email')); + } + } + + /* istanbul ignore next */ + throw e; + } + + return toPublicUser(newUser); +} + +export async function updateUser({ + username, + data +}: { + username: Username | undefined; + data: PatchUser | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + // ? Optimization + if (data && !Object.keys(data).length) return; + + validateUserData(data, { required: false }); + + const { email, key, salt, points, ...rest } = data as Required; + const restKeys = Object.keys(rest); + let pointsUpdateOp: PointsUpdateOperation | null = null; + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (points !== undefined) { + if (isPlainObject(points)) { + if (typeof points.amount != 'number' || points.amount < 0) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ); + } else if (!['increment', 'decrement'].includes(points.op as string)) { + throw new ValidationError( + ErrorMessage.InvalidFieldValue('operation', points.op, [ + 'increment', + 'decrement' + ]) + ); + } + + const { amount: _, op: __, ...restOp } = points; + const restOpKeys = Object.keys(restOp); + + if (restOpKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restOpKeys[0])); + } + + pointsUpdateOp = points as PointsUpdateOperation; + } else if (typeof points != 'number' || points < 0) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('points', 0, null, 'integer') + ); + } + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + try { + const result = await userDb.updateOne( + { username }, + { + $set: { + ...(email ? { email } : {}), + ...(salt ? { salt: salt.toLowerCase() } : {}), + ...(key ? { key: key.toLowerCase() } : {}), + ...(typeof points == 'number' ? { points } : {}) + }, + ...(pointsUpdateOp + ? { + $inc: { + points: + pointsUpdateOp.op == 'decrement' + ? -pointsUpdateOp.amount + : pointsUpdateOp.amount + } + } + : {}) + } + ); + + if (!result.matchedCount) { + throw new ItemNotFoundError(username, 'user'); + } + } catch (e) { + if (e instanceof MongoServerError && e.code == 11000) { + /* istanbul ignore else */ + if (e.keyPattern?.email !== undefined) { + throw new ValidationError(ErrorMessage.DuplicateFieldValue('email')); + } + } + + throw e; + } +} + +export async function deleteUser({ + username +}: { + username: Username | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const result = await userDb.deleteOne({ username }); + + if (!result.deletedCount) { + throw new ItemNotFoundError(username, 'user'); + } +} + +export async function authAppUser({ + username, + key +}: { + username: Username | undefined; + key: string | undefined; +}): Promise { + if (!key || !username) return false; + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + + return !!(await userDb.countDocuments({ username, key })); +} + +export async function getUserMessages({ + username, + after_id +}: { + username: string | undefined; + after_id: string | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const mailDb = db.collection('mail'); + const afterId = after_id ? itemToObjectId(after_id) : undefined; + + if (!(await itemExists(userDb, { key: 'username', id: username }))) { + throw new ItemNotFoundError(username, 'user'); + } + + if (afterId && !(await itemExists(mailDb, afterId))) { + throw new ItemNotFoundError(after_id, 'mail_id'); + } + + return mailDb + .find( + { + ...(afterId ? { _id: { $lt: afterId } } : {}), + receiver: username + }, + { + sort: { _id: -1 }, + limit: getEnv().RESULTS_PER_PAGE, + projection: publicMailProjection + } + ) + .toArray(); +} + +export async function createMessage({ + data +}: { + data: NewMail | undefined; +}): Promise { + if (!isPlainObject(data)) { + throw new ValidationError(ErrorMessage.InvalidJSON()); + } + + const { sender, receiver, subject, text, ...rest } = data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (!receiver) { + throw new ValidationError(ErrorMessage.InvalidFieldValue('receiver')); + } + + if (!sender) { + throw new ValidationError(ErrorMessage.InvalidFieldValue('sender')); + } + + const { + MAX_MAIL_SUBJECT_LENGTH: maxSubjectLen, + MAX_MAIL_BODY_LENGTH_BYTES: maxBodyLen + } = getEnv(); + + if (typeof subject != 'string' || !subject || subject.length > maxSubjectLen) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('subject', 1, maxSubjectLen, 'string') + ); + } + + if (typeof text != 'string' || !text || text.length > maxBodyLen) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ); + } + + const db = await getDb({ name: 'app' }); + const mailDb = db.collection('mail'); + const userDb = db.collection('users'); + + if (!(await itemExists(userDb, { key: 'username', id: receiver }))) { + throw new ItemNotFoundError(receiver, 'user'); + } + + if (!(await itemExists(userDb, { key: 'username', id: sender }))) { + throw new ItemNotFoundError(sender, 'user'); + } + + const newMail: InternalMail = { + _id: new ObjectId(), + createdAt: Date.now(), + sender, + receiver, + subject, + text + }; + + // * At this point, we can finally trust this data is valid and not malicious + await mailDb.insertOne(newMail); + + return toPublicMail(newMail); +} + +export async function deleteMessage({ + mail_id +}: { + mail_id: string | undefined; +}): Promise { + if (!mail_id) { + throw new InvalidItemError('mail_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const mailDb = db.collection('mail'); + const result = await mailDb.deleteOne({ _id: itemToObjectId(mail_id) }); + + if (!result.deletedCount) { + throw new ItemNotFoundError(mail_id, 'mail message'); + } +} + +export async function searchQuestions({ + after_id, + match, + regexMatch, + sort +}: { + after_id: string | undefined; + match: { + [specifier: string]: + | string + | number + | boolean + | SubSpecifierObject + | { $or: SubSpecifierObject[] }; + }; + regexMatch: { + [specifier: string]: string; + }; + sort: string | undefined; +}): Promise { + // ? Validate sort parameter + if ( + sort !== undefined && + (typeof sort != 'string' || !['u', 'uvc', 'uvac'].includes(sort)) + ) { + throw new ValidationError(ErrorMessage.InvalidItem('nope', 'sort parameter')); + } + + const { RESULTS_PER_PAGE } = getEnv(); + const afterId = after_id ? itemToObjectId(after_id) : undefined; + + // ? Initial matcher validation + if (!isPlainObject(match)) { + throw new ValidationError(ErrorMessage.InvalidMatcher('match')); + } else if (!isPlainObject(regexMatch)) { + throw new ValidationError(ErrorMessage.InvalidMatcher('regexMatch')); + } + + // ? Handle aliasing/proxying + [regexMatch, match].forEach((matchSpec) => { + if (typeof matchSpec.title == 'string') { + matchSpec['title-lowercase'] = matchSpec.title.toLowerCase(); + delete matchSpec.title; + } + }); + + // ? Validate username and after_id + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + + const sortByField = + sort === undefined + ? '_id' + : sort == 'u' + ? 'upvotes' + : sort == 'uvc' + ? 'sorter.uvc' + : 'sorter.uvac'; + + let afterCriterion = undefined; + + if (afterId) { + const afterItem = await questionDb.findOne( + { _id: afterId }, + { projection: { [sortByField]: true } } + ); + + if (afterItem) { + afterCriterion = afterItem[sortByField]; + } else { + throw new ItemNotFoundError(after_id, 'question_id'); + } + } + + // ? Validate the match object + for (const [key, val] of Object.entries(match)) { + if (!matchableStrings.includes(key)) { + throw new ValidationError(ErrorMessage.UnknownSpecifier(key)); + } + + if (isPlainObject(val)) { + let valNotEmpty = false; + + for (const [subkey, subval] of Object.entries(val)) { + if (subkey == '$or') { + if (!Array.isArray(subval) || subval.length != 2) { + throw new ValidationError(ErrorMessage.InvalidOrSpecifier()); + } + + if ( + subval.every((sv, ndx) => { + if (!isPlainObject(sv)) { + throw new ValidationError( + ErrorMessage.InvalidOrSpecifierNonObject(ndx) + ); + } + + const entries = Object.entries(sv); + + if (!entries.length) return false; + if (entries.length != 1) { + throw new ValidationError( + ErrorMessage.InvalidOrSpecifierBadLength(ndx) + ); + } + + entries.forEach(([k, v]) => { + if (!matchableSubStrings.includes(k)) { + throw new ValidationError( + ErrorMessage.InvalidOrSpecifierInvalidKey(ndx, k) + ); + } + + if (typeof v != 'number') { + throw new ValidationError( + ErrorMessage.InvalidOrSpecifierInvalidValueType(ndx, k) + ); + } + }); + + return true; + }) + ) { + valNotEmpty = true; + } + } else { + valNotEmpty = true; + if (!matchableSubStrings.includes(subkey)) { + throw new ValidationError(ErrorMessage.UnknownSpecifier(subkey, true)); + } + + if (typeof subval != 'number') { + throw new ValidationError( + ErrorMessage.InvalidSpecifierValueType(subkey, 'a number', true) + ); + } + } + } + + if (!valNotEmpty) + throw new ValidationError( + ErrorMessage.InvalidSpecifierValueType(key, 'a non-empty object') + ); + } else if ( + val !== null && + !['number', 'string', 'boolean'].includes(typeof val) + ) { + throw new ValidationError( + ErrorMessage.InvalidSpecifierValueType( + key, + 'a number, string, boolean, or sub-specifier object' + ) + ); + } + } + + // ? Validate the regexMatch object + for (const [key, val] of Object.entries(regexMatch)) { + if (!regexMatchableStrings.includes(key)) { + throw new ValidationError(ErrorMessage.UnknownSpecifier(key)); + } + + if (!val || typeof val != 'string') { + throw new ValidationError(ErrorMessage.InvalidRegexString(key)); + } + } + + // ? Construct aggregation primitives + + const finalRegexMatch = Object.entries(regexMatch).reduce((obj, [spec, val]) => { + obj[spec] = { $regex: val, $options: 'mi' }; + return obj; + }, {} as Record); + + const orMatcher: { [key: string]: SubSpecifierObject }[] = []; + + // ? Separate out the $or sub-specifiers for special treatment + Object.entries(match).forEach(([spec, val]) => { + if (isPlainObject(val)) { + const obj = val as { $or?: unknown }; + + if (obj.$or) { + (obj.$or as SubSpecifierObject[]).forEach((operand) => + orMatcher.push({ + [spec]: operand + }) + ); + delete obj.$or; + } + + // ? Delete useless matchers if they've been emptied out + if (obj && !Object.keys(obj).length) delete match[spec]; + } + }); + + const $match = { + ...(afterId + ? { + [sortByField]: { + // ? "$lte" for all fields that are not guaranteed to be unique! + [sortByField == '_id' ? '$lt' : '$lte']: afterCriterion + } + } + : {}), + ...match, + ...(orMatcher.length ? { $or: orMatcher } : {}), + ...finalRegexMatch + }; + + const pipeline = [ + { $match }, + { + $sort: { + [sortByField]: -1, + // ? Ensure stable sorting when sortByField values are not unique + ...(sortByField != '_id' ? { _id: -1 } : {}) + } + }, + // ? If we sorted by a non-unique field... + ...(afterId && sortByField != '_id' + ? [ + { + $match: { + $or: [ + // ? Since sort field isn't unique, we must manually exclude the + // ? previous result(s) using an actually-unique field (ie. _id) + { [sortByField]: afterCriterion, _id: { $lt: afterId } }, + { [sortByField]: { $lt: afterCriterion } } + ] + } + } + ] + : []), + // ? This has to come after the second match instead of before it because + // ? there many be >PER_PAGE number of results with the same non-unique + // ? field value! + { $limit: RESULTS_PER_PAGE }, + { $project: publicQuestionProjection } + ]; + + // ? Run the aggregation and return the result + return questionDb.aggregate(pipeline).toArray(); +} + +export async function getQuestion({ + question_id +}: { + question_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + + return ( + (await questionDb + .find( + { _id: itemToObjectId(question_id) }, + { projection: publicQuestionProjection } + ) + .next()) || toss(new ItemNotFoundError(question_id, 'question')) + ); +} + +export async function createQuestion({ + data +}: { + data: NewQuestion | undefined; +}): Promise { + validateQuestionData(data, { required: true }); + + const { creator, title, text, ...rest } = data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (!creator) { + throw new ValidationError(ErrorMessage.InvalidFieldValue('creator')); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionDb = db.collection('questions'); + + if (!(await itemExists(userDb, { key: 'username', id: creator }))) { + throw new ItemNotFoundError(creator, 'user'); + } + + const newQuestion: InternalQuestion = { + _id: new ObjectId(), + creator, + title, + 'title-lowercase': title.toLowerCase(), + createdAt: Date.now(), + text, + status: 'open', + hasAcceptedAnswer: false, + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + answers: 0, + answerItems: [], + views: 0, + comments: 0, + commentItems: [], + sorter: { uvc: 0, uvac: 0 } + }; + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + + await questionDb.insertOne(newQuestion); + + await userDb.updateOne( + { username: creator }, + { $push: { questionIds: newQuestion._id } } + ); + + return toPublicQuestion(newQuestion); +} + +export async function updateQuestion({ + question_id, + data +}: { + question_id: string | undefined; + data: PatchQuestion | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + // ? Optimization + if (data && !Object.keys(data).length) return; + + validateQuestionData(data, { required: false }); + + const { title, text, status, upvotes, downvotes, views, ...rest } = + data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (status !== undefined && !questionStatuses.includes(status)) { + throw new ValidationError( + ErrorMessage.InvalidFieldValue('status', undefined, questionStatuses) + ); + } + + if (upvotes !== undefined && (typeof upvotes != 'number' || upvotes < 0)) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ); + } + + if (downvotes !== undefined && (typeof downvotes != 'number' || downvotes < 0)) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ); + } + + if ( + views !== undefined && + views !== 'increment' && + (typeof views != 'number' || views < 0) + ) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('views', 0, null, 'integer') + ); + } + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + + const incrementor: { + upvotes?: number; + views?: number | SorterUpdateAggregationOp; + 'sorter.uvc'?: SorterUpdateAggregationOp; + 'sorter.uvac'?: SorterUpdateAggregationOp; + } = {}; + + if (views !== undefined || upvotes !== undefined) { + Object.assign(incrementor, { + 'sorter.uvc': { $add: ['$$ROOT.sorter.uvc', 0] }, + 'sorter.uvac': { $add: ['$$ROOT.sorter.uvac', 0] } + }); + + if (views === 'increment') { + incrementor.views = { $add: ['$$ROOT.views', 1] }; + incrementor['sorter.uvc']!.$add[1]++; + incrementor['sorter.uvac']!.$add[1]++; + } else if (typeof views == 'number') { + incrementor.views = views; + + incrementor['sorter.uvc']!.$add.push({ + $subtract: [views, '$$ROOT.views'] + }); + + incrementor['sorter.uvac']!.$add.push({ + $subtract: [views, '$$ROOT.views'] + }); + } + + if (typeof upvotes == 'number') { + incrementor.upvotes = upvotes; + + incrementor['sorter.uvc']!.$add.push({ + $subtract: [upvotes, '$$ROOT.upvotes'] + }); + + incrementor['sorter.uvac']!.$add.push({ + $subtract: [upvotes, '$$ROOT.upvotes'] + }); + } + } + + const result = await questionDb.updateOne( + { _id: itemToObjectId(question_id) }, + [ + { + $set: { + ...(title ? { title, 'title-lowercase': title.toLowerCase() } : {}), + ...(text ? { text } : {}), + ...(status ? { status } : {}), + ...(typeof downvotes == 'number' ? { downvotes } : {}), + ...incrementor + } + } + ] + ); + + if (!result.matchedCount) { + throw new ItemNotFoundError(question_id, 'question'); + } +} + +export async function deleteQuestion({ + question_id +}: { + question_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionDb = db.collection('questions'); + const questionId = itemToObjectId(question_id); + + const { value: deletedQuestion } = await questionDb.findOneAndDelete( + { _id: questionId }, + { projection: { creator: true } } + ); + + if (!deletedQuestion) { + throw new ItemNotFoundError(questionId, 'question'); + } + + await userDb.updateOne( + { username: deletedQuestion.creator }, + { $pull: { questionIds: questionId } } + ); + + await userDb.updateMany( + { answerIds: { $elemMatch: { $elemMatch: { $in: [questionId] } } } }, + // @ts-expect-error: work around MongoDB driver bug + { $pull: { answerIds: { $in: [questionId] } } } + ); +} + +export async function getAnswers({ + question_id, + after_id +}: { + question_id: string | undefined; + after_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + const afterId = after_id ? itemToObjectId(after_id) : undefined; + const questionId = itemToObjectId(question_id); + + // ? Ensure after_id exists + if (afterId) { + try { + void (await selectAnswerFromDb({ + questionId: questionId, + answerId: afterId, + projection: vacuousProjection + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(afterId, 'answer_id'); + } + + /* istanbul ignore next */ + throw e; + } + } + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + + const pipeline = [ + { $match: { _id: questionId } }, + { + $project: { + answers: { + $map: { + input: { + $slice: [ + afterId + ? { + $filter: { + input: '$answerItems', + as: 'this', + cond: { $gt: ['$$this._id', afterId] } + } + } + : '$answerItems', + getEnv().RESULTS_PER_PAGE + ] + }, + as: 'answer', + in: publicAnswerMap('answer', questionId) + } + } + } + } + ]; + + const { answers } = + (await questionDb.aggregate<{ answers: PublicAnswer[] }>(pipeline).next()) || + toss(new ItemNotFoundError(question_id, 'question')); + + return answers; +} + +export async function createAnswer({ + question_id, + data +}: { + question_id: string | undefined; + data: NewAnswer | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + validateAnswerData(data, { required: true }); + + const { creator, text, ...rest } = data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (!creator) { + throw new ValidationError(ErrorMessage.InvalidFieldValue('creator')); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionId = itemToObjectId(question_id); + + if (!(await itemExists(userDb, { key: 'username', id: creator }))) { + throw new ItemNotFoundError(creator, 'user'); + } + + try { + if ( + await selectAnswerFromDb({ + questionId: questionId, + answer_creator: creator, + projection: vacuousProjection + }) + ) { + throw new ClientValidationError(ErrorMessage.UserAlreadyAnswered()); + } + } catch (e) { + if (e instanceof ClientValidationError) { + throw e; + } + } + + const newAnswer: InternalAnswer = { + _id: new ObjectId(), + creator, + createdAt: Date.now(), + text, + accepted: false, + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + commentItems: [] + }; + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + + const result = await addAnswerToDb({ questionId: questionId, answer: newAnswer }); + + if (!result.matchedCount) { + throw new ItemNotFoundError(question_id, 'question'); + } + + await userDb.updateOne( + { username: creator }, + { $push: { answerIds: [questionId, newAnswer._id] } } + ); + + return toPublicAnswer(newAnswer, questionId); +} + +export async function updateAnswer({ + question_id, + answer_id, + data +}: { + question_id: string | undefined; + answer_id: string | undefined; + data: PatchAnswer | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + if (!answer_id) { + throw new InvalidItemError('answer_id', 'parameter'); + } + + // ? Optimization + if (data && !Object.keys(data).length) return; + + validateAnswerData(data, { required: false }); + + const { text, accepted, upvotes, downvotes, ...rest } = + data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (accepted !== undefined && !accepted) { + throw new ValidationError( + ErrorMessage.InvalidFieldValue('accepted', undefined, ['true']) + ); + } + + if (upvotes !== undefined && (typeof upvotes != 'number' || upvotes < 0)) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ); + } + + if (downvotes !== undefined && (typeof downvotes != 'number' || downvotes < 0)) { + throw new ValidationError( + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ); + } + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + const questionId = itemToObjectId(question_id); + const answerId = itemToObjectId(answer_id); + + if ( + accepted && + (await questionDb.countDocuments({ _id: questionId, hasAcceptedAnswer: true })) != + 0 + ) { + throw new ClientValidationError(ErrorMessage.QuestionAlreadyAcceptedAnswer()); + } + + try { + void (await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + projection: vacuousProjection + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + /* istanbul ignore next */ + throw e; + } + + const result = await patchAnswerInDb({ + questionId: questionId, + answerId: answerId, + updateOps: { + $set: { + ...(text ? { text } : {}), + ...(accepted ? { accepted } : {}), + ...(typeof upvotes == 'number' ? { upvotes } : {}), + ...(typeof downvotes == 'number' ? { downvotes } : {}) + } + } + }); + + if (!result.matchedCount) { + throw new ItemNotFoundError(question_id, 'question'); + } + + if (accepted) { + await questionDb.updateOne( + { _id: questionId }, + { $set: { hasAcceptedAnswer: true } } + ); + } +} + +export async function deleteAnswer({ + question_id, + answer_id +}: { + question_id: string | undefined; + answer_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + if (!answer_id) { + throw new InvalidItemError('answer_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionId = itemToObjectId(question_id); + const answerId = itemToObjectId(answer_id); + + const { creator } = await (async () => { + try { + const result = await selectAnswerFromDb<{ creator: Username } | null>({ + questionId: questionId, + answerId: answerId, + projection: { creator: true } + }); + + return result || { creator: null }; + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + /* istanbul ignore next */ + throw e; + } + })(); + + if (!creator) { + throw new ItemNotFoundError(question_id, 'question'); + } + + await removeAnswerFromDb({ + questionId: questionId, + answerId: answerId + }); + + await userDb.updateOne( + { username: creator }, + { $pull: { answerIds: [questionId, answerId] } } + ); +} + +export async function getComments({ + question_id, + answer_id, + after_id +}: { + question_id: string | undefined; + answer_id: string | undefined; + after_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const questionDb = db.collection('questions'); + const questionId = itemToObjectId(question_id); + const answerId = answer_id ? itemToObjectId(answer_id) : undefined; + const afterId = after_id ? itemToObjectId(after_id) : undefined; + + if (answerId) { + try { + void (await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + projection: vacuousProjection + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + /* istanbul ignore next */ + throw e; + } + } + + // ? Ensure after_id exists + if (afterId) { + try { + await selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: afterId, + projection: vacuousProjection + }); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(afterId, 'comment_id'); + } + + /* istanbul ignore next */ + throw e; + } + } + + const pipeline = [ + { $match: { _id: questionId } }, + ...(answerId + ? [ + { + $project: { + answer: { + $first: { + $filter: { + input: '$answerItems', + as: 'answer', + cond: { $eq: ['$$answer._id', answerId] } + } + } + } + } + }, + { + $replaceWith: '$answer' + } + ] + : []), + { + $project: { + comments: { + $map: { + input: { + $slice: [ + afterId + ? { + $filter: { + input: '$commentItems', + as: 'this', + cond: { $gt: ['$$this._id', afterId] } + } + } + : '$commentItems', + getEnv().RESULTS_PER_PAGE + ] + }, + as: 'comment', + in: publicCommentMap('comment') + } + } + } + } + ]; + + const { comments } = + (await questionDb.aggregate<{ comments: PublicComment[] }>(pipeline).next()) || + toss(new ItemNotFoundError(question_id, 'question')); + + return comments; +} + +export async function createComment({ + question_id, + answer_id, + data +}: { + question_id: string | undefined; + answer_id: string | undefined; + data: NewComment | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + if (!isPlainObject(data)) { + throw new ValidationError(ErrorMessage.InvalidJSON()); + } + + const { creator, text, ...rest } = data as Required; + const restKeys = Object.keys(rest); + + if (restKeys.length != 0) { + throw new ValidationError(ErrorMessage.UnknownField(restKeys[0])); + } + + if (!creator) { + throw new ValidationError(ErrorMessage.InvalidFieldValue('creator')); + } + + const { MAX_COMMENT_LENGTH: maxTextLen } = getEnv(); + + if (typeof text != 'string' || !text || text.length > maxTextLen) { + throw new ValidationError( + ErrorMessage.InvalidStringLength('text', 1, maxTextLen, 'string') + ); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionId = itemToObjectId(question_id); + const answerId = answer_id ? itemToObjectId(answer_id) : undefined; + + if (!(await itemExists(userDb, { key: 'username', id: creator }))) { + throw new ItemNotFoundError(creator, 'user'); + } + + if (answerId) { + try { + await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + projection: vacuousProjection + }); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + /* istanbul ignore next */ + throw e; + } + } + + const newComment: InternalComment = { + _id: new ObjectId(), + creator, + createdAt: Date.now(), + text, + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [] + }; + + // * At this point, we can finally trust this data is not malicious, but not + // * necessarily valid... + + const result = await addCommentToDb({ + questionId: questionId, + ...(answerId ? { answerId: answerId } : {}), + comment: newComment + }); + + if (!result.matchedCount) { + throw new ItemNotFoundError(question_id, 'question'); + } + + return toPublicComment(newComment); +} + +export async function deleteComment({ + question_id, + answer_id, + comment_id +}: { + question_id: string | undefined; + answer_id: string | undefined; + comment_id: string | undefined; +}): Promise { + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + if (!comment_id) { + throw new InvalidItemError('comment_id', 'parameter'); + } + + const questionId = itemToObjectId(question_id); + const answerId = answer_id ? itemToObjectId(answer_id) : undefined; + const commentId = itemToObjectId(comment_id); + + if (answerId) { + try { + void (await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + projection: vacuousProjection + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + /* istanbul ignore next */ + throw e; + } + } + + try { + void (await selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection: vacuousProjection + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(commentId, 'comment'); + } + + /* istanbul ignore next */ + throw e; + } + + const result = await removeCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId + }); + + if (!result.matchedCount) { + throw new ItemNotFoundError(question_id, 'question'); + } +} + +export async function getHowUserVoted({ + username, + question_id, + answer_id, + comment_id +}: { + username: string | undefined; + question_id: string | undefined; + answer_id: string | undefined; + comment_id: string | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionDb = db.collection('questions'); + + if (!(await itemExists(userDb, { key: 'username', id: username }))) { + throw new ItemNotFoundError(username, 'user'); + } + + const questionId = itemToObjectId(question_id); + const answerId = answer_id ? itemToObjectId(answer_id) : undefined; + const commentId = comment_id ? itemToObjectId(comment_id) : undefined; + + if (answerId) { + try { + const result = await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + // ? If we're interested in a comment, only do an existence check + projection: commentId ? vacuousProjection : voterStatusProjection(username) + }); + + if (!result) { + throw new ItemNotFoundError(questionId, 'question'); + } + + if (!commentId) { + return result.voterStatus; + } + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + throw e; + } + } + + if (commentId) { + try { + const result = await selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection: voterStatusProjection(username) + }); + + if (!result) { + throw new ItemNotFoundError(questionId, 'question'); + } + + return result.voterStatus; + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(commentId, 'comment'); + } + + throw e; + } + } + + // ? If we've come this far, then we must only be interested in a question! + + const result = await questionDb.findOne( + { _id: questionId }, + { projection: voterStatusProjection(username) } + ); + + if (!result) { + throw new ItemNotFoundError(questionId, 'question'); + } + + return result.voterStatus; +} + +export async function applyVotesUpdateOperation({ + username, + question_id, + answer_id, + comment_id, + operation +}: { + username: string | undefined; + question_id: string | undefined; + answer_id: string | undefined; + comment_id: string | undefined; + operation: Partial | undefined; +}): Promise { + if (!username) { + throw new InvalidItemError('username', 'parameter'); + } + + if (!question_id) { + throw new InvalidItemError('question_id', 'parameter'); + } + + if (!operation) { + throw new InvalidItemError('operation', 'parameter'); + } + + validateVotesUpdateOperation(operation); + + const db = await getDb({ name: 'app' }); + const userDb = db.collection('users'); + const questionDb = db.collection('questions'); + + if (!(await itemExists(userDb, { key: 'username', id: username }))) { + throw new ItemNotFoundError(username, 'user'); + } + + const questionId = itemToObjectId(question_id); + const answerId = answer_id ? itemToObjectId(answer_id) : undefined; + const commentId = comment_id ? itemToObjectId(comment_id) : undefined; + + const validateOperationAuthorization = ( + selectResult: SelectResult, + { skip }: { skip: boolean } + ) => { + if (!selectResult) { + throw new ItemNotFoundError(questionId, 'question'); + } + + if (!skip) { + if (selectResult.isCreator) { + throw new NotAuthorizedError(ErrorMessage.IllegalOperation()); + } + + if (selectResult.voterStatus !== null) { + if (operation.op == 'increment') { + if ( + (operation.target == 'upvotes' && + selectResult.voterStatus == 'upvoted') || + (operation.target == 'downvotes' && + selectResult.voterStatus == 'downvoted') + ) { + throw new NotAuthorizedError(ErrorMessage.DuplicateIncrementOperation()); + } + + throw new NotAuthorizedError(ErrorMessage.MultipleIncrementTargets()); + } else { + if ( + !( + (operation.target == 'upvotes' && + selectResult.voterStatus == 'upvoted') || + (operation.target == 'downvotes' && + selectResult.voterStatus == 'downvoted') + ) + ) { + throw new NotAuthorizedError(ErrorMessage.MultitargetDecrement()); + } + } + } else if (operation.op == 'decrement') { + throw new NotAuthorizedError(ErrorMessage.InvalidDecrementOperation()); + } + } + }; + + const calculateUpdateOps = ({ includeSorter }: { includeSorter: boolean }) => { + const { op, target } = operation; + const delta = op == 'increment' ? 1 : -1; + + return { + $inc: { + [target]: delta, + ...(includeSorter && target == 'upvotes' + ? { 'sorter.uvc': delta, 'sorter.uvac': delta } + : {}) + }, + [op == 'increment' ? '$push' : '$pull']: { + [`${target.slice(0, -1)}rUsernames`]: username + } + }; + }; + + // ? Ensure answer_id is legit + if (answerId) { + try { + const result = await selectAnswerFromDb({ + questionId: questionId, + answerId: answerId, + projection: selectResultProjection(username) + }); + + validateOperationAuthorization(result, { + // ? If we're actually interested in a comment, defer authorization + skip: !!commentId + }); + + if (!commentId) { + return void (await patchAnswerInDb({ + questionId: questionId, + answerId: answerId, + updateOps: calculateUpdateOps({ includeSorter: false }) + })); + } + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(answerId, 'answer'); + } + + throw e; + } + } + + // ? Ensure comment_id is legit + if (commentId) { + try { + const result = await selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection: selectResultProjection(username) + }); + + validateOperationAuthorization(result, { skip: false }); + + return void (await patchCommentInDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + updateOps: calculateUpdateOps({ includeSorter: false }) + })); + } catch (e) { + if (e instanceof MongoServerError) { + throw new ItemNotFoundError(commentId, 'comment'); + } + + throw e; + } + } + + // ? If we've come this far, then we must only be interested in a question! + const result = await questionDb.findOne( + { _id: questionId }, + { projection: selectResultProjection(username) } + ); + + validateOperationAuthorization(result, { skip: false }); + + await questionDb.updateOne( + { _id: questionId }, + calculateUpdateOps({ includeSorter: true }) + ); +} diff --git a/src/backend/middleware.ts b/src/backend/middleware.ts new file mode 100644 index 0000000..007ec0a --- /dev/null +++ b/src/backend/middleware.ts @@ -0,0 +1,103 @@ +import { middlewareFactory } from 'multiverse/next-api-glue'; + +import logRequest, { + Options as LogRequestOptions +} from 'multiverse/next-adhesive/log-request'; + +import checkVersion, { + Options as CheckVersionOptions +} from 'multiverse/next-adhesive/check-version'; + +import useCors, { + Options as UseCorsOptions +} from 'multiverse/next-adhesive/use-cors'; + +import authRequest, { + Options as AuthRequestOptions +} from 'multiverse/next-adhesive/auth-request'; + +import limitRequest, { + Options as LimitRequestOptions +} from 'multiverse/next-adhesive/limit-request'; + +import checkMethod, { + Options as CheckMethodOptions +} from 'multiverse/next-adhesive/check-method'; + +import checkContentType, { + Options as CheckContentTypeOptions +} from 'multiverse/next-adhesive/check-content-type'; + +import handleError, { + Options as HandleErrorOptions +} from 'multiverse/next-adhesive/handle-error'; + +import contriveError, { + Options as ContriveErrorOptions +} from 'multiverse/next-adhesive/contrive-error'; + +/** + * Primary middleware runner for the REST API. Decorates a request handler. + * + * Passing `undefined` as `handler` or not calling `res.end()` (and not sending + * headers) in your handler or use chain will trigger an `HTTP 501 Not + * Implemented` response. This can be used to to stub out endpoints and their + * middleware for later implementation. + */ +/* istanbul ignore next */ +const withMiddleware = middlewareFactory< + LogRequestOptions & + CheckVersionOptions & + UseCorsOptions & + AuthRequestOptions & + LimitRequestOptions & + CheckMethodOptions & + CheckContentTypeOptions & + HandleErrorOptions & + ContriveErrorOptions +>({ + use: [ + logRequest, + checkVersion, + useCors, + authRequest, + limitRequest, + checkMethod, + checkContentType, + contriveError + ], + useOnError: [handleError], + options: { + allowedContentTypes: ['application/json'], + requiresAuth: true, + enableContrivedErrors: true + } +}); + +/** + * Middleware runner for the special /sys API endpoints. Decorates a request + * handler. + * + * Passing `undefined` as `handler` or not calling `res.end()` (and not sending + * headers) in your handler or use chain will trigger an `HTTP 501 Not + * Implemented` response. This can be used to to stub out endpoints and their + * middleware for later implementation. + */ +/* istanbul ignore next */ +const withSysMiddleware = middlewareFactory< + LogRequestOptions & + AuthRequestOptions & + LimitRequestOptions & + CheckMethodOptions & + CheckContentTypeOptions & + HandleErrorOptions +>({ + use: [logRequest, authRequest, limitRequest, checkMethod, checkContentType], + useOnError: [handleError], + options: { + allowedContentTypes: ['application/json'], + requiresAuth: { constraints: 'isGlobalAdmin' } + } +}); + +export { withMiddleware, withSysMiddleware }; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..65a5bba --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,7 @@ +import { name as pkgName } from 'package'; + +/** + * This value is used in first party source app-wide as a debug package + * namespace. + */ +export const debugNamespace = pkgName; diff --git a/src/error.ts b/src/error.ts new file mode 100644 index 0000000..b359d79 --- /dev/null +++ b/src/error.ts @@ -0,0 +1,104 @@ +import { ErrorMessage as NamedErrorMessage } from 'named-app-errors'; + +export * from 'named-app-errors'; + +/** + * A collection of possible error and warning messages. + */ +/* istanbul ignore next */ +export const ErrorMessage = { + ...NamedErrorMessage, + DuplicateFieldValue: (prop: string) => `an item with that "${prop}" already exists`, + InvalidFieldValue: ( + prop: string, + value?: string, + validValues?: readonly string[] + ) => + `\`${prop}\` field has ${ + value + ? `invalid or illegal value "${value}"` + : 'a missing, invalid, or illegal value' + }${validValues ? `. Valid values: ${validValues.join(', ')}` : ''}`, + InvalidArrayValue: (prop: string, value: string, validValues?: readonly string[]) => + `the \`${prop}\` array element "${value}" is invalid or illegal${ + validValues ? `. Valid values: ${validValues.join(', ')}` : '' + }`, + InvalidObjectKeyValue: ( + prop: string, + value?: string, + validValues?: readonly string[] + ) => + `a \`${prop}\` object key has ${ + value + ? `invalid or illegal value "${value}"` + : 'a missing, invalid, or illegal value' + }${validValues ? `. Valid values: ${validValues.join(', ')}` : ''}`, + InvalidJSON: () => 'encountered invalid JSON', + InvalidNumberValue: ( + prop: string, + min: number | string, + max: number | string | null, + type: 'number' | 'integer', + nullable = false, + isArray = false + ) => + `${isArray ? `each \`${prop}\` element` : `\`${prop}\``} must be a${ + type == 'integer' ? 'n integer' : ' number' + } ${max ? `between ${min} and ${max} (inclusive)` : `>= ${min}`}${ + nullable ? ' or null' : '' + }`, + InvalidStringLength: ( + prop: string, + min: number | string, + max: number | string | null, + syntax: 'string' | 'alphanumeric' | 'hexadecimal' | 'bytes', + nullable = false, + isArray = false + ) => + `${isArray ? `each \`${prop}\` element` : `\`${prop}\``} must be a${ + syntax == 'alphanumeric' + ? 'n alphanumeric' + : syntax == 'hexadecimal' + ? ' hexadecimal' + : '' + } ${ + max + ? `string between ${min} and ${max} ${ + syntax == 'bytes' ? 'byte' : 'character' + }s (inclusive)` + : `${min} ${syntax == 'bytes' ? 'byte' : 'character'} string` + }${nullable ? ' or null' : ''}`, + InvalidObjectId: (id: string) => `invalid id "${id}"`, + UnknownField: (prop: string) => `encountered unknown or illegal field \`${prop}\``, + UnknownSpecifier: (prop: string, sub = false) => + `encountered unknown or illegal ${sub ? 'sub-' : ''}specifier \`${prop}\``, + InvalidSpecifierValueType: (prop: string, type: string, sub = false) => + `\`${prop}\` has invalid ${ + sub ? 'sub-' : '' + }specifier value type (must be ${type})`, + InvalidRegexString: (prop: string) => + `\`${prop}\` has invalid or illegal regex value`, + InvalidMatcher: (prop: string) => `invalid \`${prop}\`: must be object`, + InvalidOrSpecifier: () => + 'invalid "$or" sub-specifier: must be array with exactly two elements', + InvalidOrSpecifierNonObject: (index: number) => + `invalid "$or" sub-specifier at index ${index}: all array elements must be objects`, + InvalidOrSpecifierBadLength: (index: number) => + `invalid "$or" sub-specifier at index ${index}: only one sub-specifier allowed per array element`, + InvalidOrSpecifierInvalidKey: (index: number, key: string) => + `invalid "$or" sub-specifier at index ${index}: invalid sub-key "${key}"`, + InvalidOrSpecifierInvalidValueType: (index: number, key: string) => + `invalid "$or" sub-specifier at index ${index}: sub-key "${key}" has invalid value type (must be number)`, + UserAlreadyAnswered: () => 'cannot answer the same question more than once', + QuestionAlreadyAcceptedAnswer: () => 'question already has an accepted answer', + DuplicateIncrementOperation: () => + 'cannot execute duplicate increment without preceding decrement', + InvalidDecrementOperation: () => + 'cannot execute decrement on this target without preceding increment', + MultipleIncrementTargets: () => + 'cannot execute increment without preceding decrement on other target', + MultitargetDecrement: () => + 'cannot execute decrement while other target is incremented', + IllegalOperation: () => + 'this user is not authorized to execute operations on this item' +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx new file mode 100644 index 0000000..6ab50d6 --- /dev/null +++ b/src/pages/_app.tsx @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import * as React from 'react'; +import Head from 'next/head'; + +import type { AppProps } from 'next/app'; + +export default function App({ Component, pageProps }: AppProps) { + return ( + + + No Browser Access! + + + + + + ); +} diff --git a/src/pages/api/sys/auth/index.ts b/src/pages/api/sys/auth/index.ts new file mode 100644 index 0000000..668c2f5 --- /dev/null +++ b/src/pages/api/sys/auth/index.ts @@ -0,0 +1,50 @@ +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { + createEntry, + deleteEntry, + getOwnersEntries, + updateAttributes +} from 'multiverse/next-auth'; +import { withSysMiddleware } from 'universe/backend/middleware'; + +// ? https://nextjs.org/docs/api-routes/api-middlewares#custom-config +export { defaultConfig as config } from 'universe/backend/api'; + +/** + * An endpoint to test if the API is up and reachable. + */ +export default withSysMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + entries: await getOwnersEntries({ + owners: [ + req.headers['x-target-owners'] + ?.toString() + .split(',') + .map((s) => s.trim()) + ].flat() + }) + }); + } else if (req.method == 'POST') { + sendHttpOk(res, { + entry: await createEntry({ + entry: req.body + }) + }); + } else if (req.method == 'PATCH') { + await updateAttributes({ + target: req.body?.target, + attributes: req.body?.attributes + }); + sendHttpOk(res); + } else { + await deleteEntry({ target: req.body?.target }); + sendHttpOk(res); + } + }, + { + descriptor: '/sys/auth', + options: { allowedMethods: ['GET', 'POST', 'PATCH', 'DELETE'] } + } +); diff --git a/src/pages/api/sys/auth/unban.ts b/src/pages/api/sys/auth/unban.ts new file mode 100644 index 0000000..84ab3f4 --- /dev/null +++ b/src/pages/api/sys/auth/unban.ts @@ -0,0 +1,27 @@ +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { getAllRateLimits, removeRateLimit } from 'multiverse/next-limit'; +import { withSysMiddleware } from 'universe/backend/middleware'; + +// ? https://nextjs.org/docs/api-routes/api-middlewares#custom-config +export { defaultConfig as config } from 'universe/backend/api'; + +/** + * An endpoint to test if the API is up and reachable. + */ +export default withSysMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { entries: await getAllRateLimits() }); + } else { + sendHttpOk(res, { + unbannedCount: await removeRateLimit({ + target: req.body?.target + }) + }); + } + }, + { + descriptor: '/sys/auth/unban', + options: { allowedMethods: ['DELETE', 'GET'] } + } +); diff --git a/src/pages/api/sys/ping.ts b/src/pages/api/sys/ping.ts new file mode 100644 index 0000000..93d5e7d --- /dev/null +++ b/src/pages/api/sys/ping.ts @@ -0,0 +1,25 @@ +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { withSysMiddleware } from 'universe/backend/middleware'; + +// ? https://nextjs.org/docs/api-routes/api-middlewares#custom-config +export { defaultConfig as config } from 'universe/backend/api'; + +/** + * An endpoint to test if the API is up and reachable. + */ +export default withSysMiddleware( + async (req, res) => { + const { name = 'Mr. World' } = req.query; + sendHttpOk(res, { + message: `Hello to ${name} at ${new Date().toLocaleString()}` + }); + }, + { + descriptor: '/sys/ping', + options: { + allowedMethods: ['GET'], + allowedContentTypes: 'any', + requiresAuth: false + } + } +); diff --git a/src/pages/api/v1/mail/[username].ts b/src/pages/api/v1/mail/[username].ts new file mode 100644 index 0000000..2dd167b --- /dev/null +++ b/src/pages/api/v1/mail/[username].ts @@ -0,0 +1,26 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { getUserMessages } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/mail/:username' +}; + +export default withMiddleware( + async (req, res) => { + // * GET + sendHttpOk(res, { + messages: await getUserMessages({ + username: req.query.username?.toString(), + after_id: req.query.after?.toString() + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/mail/index.ts b/src/pages/api/v1/mail/index.ts new file mode 100644 index 0000000..9e0a956 --- /dev/null +++ b/src/pages/api/v1/mail/index.ts @@ -0,0 +1,25 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { createMessage } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/mail' +}; + +export default withMiddleware( + async (req, res) => { + // * POST + sendHttpOk(res, { + message: await createMessage({ + data: req.body + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['POST'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/index.ts b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/index.ts new file mode 100644 index 0000000..a4bed77 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/index.ts @@ -0,0 +1,27 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { deleteComment } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/answers/:answer_id/comments/:comment_id' +}; + +export default withMiddleware( + async (req, res) => { + // * DELETE + await deleteComment({ + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + comment_id: req.query.comment_id?.toString() + }); + + sendHttpOk(res); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['DELETE'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/vote/[username].ts b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/vote/[username].ts new file mode 100644 index 0000000..cc794a8 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/vote/[username].ts @@ -0,0 +1,51 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpNotFound, sendHttpOk } from 'multiverse/next-api-respond'; +import { applyVotesUpdateOperation, getHowUserVoted } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: + '/questions/:question_id/answers/:answer_id/comments/:comment_id/vote/:username' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + const vote = await getHowUserVoted({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + comment_id: req.query.comment_id?.toString() + }); + + if (!vote) { + sendHttpNotFound(res, { + success: true, + error: 'a vote matching this user was not found on this comment' + }); + } else { + sendHttpOk(res, { vote }); + } + } // * PATCH + else { + await applyVotesUpdateOperation({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + comment_id: req.query.comment_id?.toString(), + operation: { + op: req.body.operation, + target: req.body.target + } + }); + + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/index.ts b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/index.ts new file mode 100644 index 0000000..dc9b8d5 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/index.ts @@ -0,0 +1,37 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { createComment, getComments } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/answers/:answer_id/comments' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + comments: await getComments({ + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + after_id: req.query.after?.toString() + }) + }); + } // * POST + else { + sendHttpOk(res, { + comment: await createComment({ + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + data: req.body + }) + }); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'POST'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/index.ts b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/index.ts new file mode 100644 index 0000000..1080f51 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/index.ts @@ -0,0 +1,27 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { updateAnswer } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/answers/:answer_id' +}; + +export default withMiddleware( + async (req, res) => { + // * PATCH + await updateAnswer({ + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + data: req.body + }); + + sendHttpOk(res); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/vote/[username].ts b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/vote/[username].ts new file mode 100644 index 0000000..3d18410 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/[answer_id]/vote/[username].ts @@ -0,0 +1,50 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpNotFound, sendHttpOk } from 'multiverse/next-api-respond'; +import { applyVotesUpdateOperation, getHowUserVoted } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/answers/:answer_id/vote/:username' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + const vote = await getHowUserVoted({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + comment_id: undefined + }); + + if (!vote) { + sendHttpNotFound(res, { + success: true, + error: 'a vote matching this user was not found on this answer' + }); + } else { + sendHttpOk(res, { vote }); + } + } // * PATCH + else { + await applyVotesUpdateOperation({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: req.query.answer_id?.toString(), + comment_id: undefined, + operation: { + op: req.body.operation, + target: req.body.target + } + }); + + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/answers/index.ts b/src/pages/api/v1/questions/[question_id]/answers/index.ts new file mode 100644 index 0000000..f3f3a4c --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/answers/index.ts @@ -0,0 +1,35 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { createAnswer, getAnswers } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/answers' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + answers: await getAnswers({ + question_id: req.query.question_id?.toString(), + after_id: req.query.after?.toString() + }) + }); + } // * POST + else { + sendHttpOk(res, { + answer: await createAnswer({ + question_id: req.query.question_id?.toString(), + data: req.body + }) + }); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'POST'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/index.ts b/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/index.ts new file mode 100644 index 0000000..8222bbc --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/index.ts @@ -0,0 +1,27 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { deleteComment } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/comments/:comment_id' +}; + +export default withMiddleware( + async (req, res) => { + // * DELETE + await deleteComment({ + question_id: req.query.question_id?.toString(), + answer_id: undefined, + comment_id: req.query.comment_id?.toString() + }); + + sendHttpOk(res); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['DELETE'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/vote/[username].ts b/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/vote/[username].ts new file mode 100644 index 0000000..d3a46d0 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/comments/[comment_id]/vote/[username].ts @@ -0,0 +1,50 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpNotFound, sendHttpOk } from 'multiverse/next-api-respond'; +import { applyVotesUpdateOperation, getHowUserVoted } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/comments/:comment_id/vote/:username' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + const vote = await getHowUserVoted({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: undefined, + comment_id: req.query.comment_id?.toString() + }); + + if (!vote) { + sendHttpNotFound(res, { + success: true, + error: 'a vote matching this user was not found on this comment' + }); + } else { + sendHttpOk(res, { vote }); + } + } // * PATCH + else { + await applyVotesUpdateOperation({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: undefined, + comment_id: req.query.comment_id?.toString(), + operation: { + op: req.body.operation, + target: req.body.target + } + }); + + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/comments/index.ts b/src/pages/api/v1/questions/[question_id]/comments/index.ts new file mode 100644 index 0000000..d98e295 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/comments/index.ts @@ -0,0 +1,40 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { createComment, getComments } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/comments' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + comments: await getComments({ + question_id: req.query.question_id?.toString(), + answer_id: undefined, + after_id: req.query.after?.toString() + }) + }); + } // * POST + else { + sendHttpOk(res, { + comment: await createComment({ + question_id: req.query.question_id?.toString(), + answer_id: undefined, + data: req.body + }) + }); + } + }, + { + descriptor: metadata.descriptor, + options: { + allowedMethods: ['GET', 'POST'], + apiVersion: '1' + } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/index.ts b/src/pages/api/v1/questions/[question_id]/index.ts new file mode 100644 index 0000000..ca5757b --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/index.ts @@ -0,0 +1,34 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { getQuestion, updateQuestion } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + question: await getQuestion({ + question_id: req.query.question_id?.toString() + }) + }); + } // * PATCH + else { + await updateQuestion({ + question_id: req.query.question_id?.toString(), + data: req.body + }); + + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/[question_id]/vote/[username].ts b/src/pages/api/v1/questions/[question_id]/vote/[username].ts new file mode 100644 index 0000000..f5f0047 --- /dev/null +++ b/src/pages/api/v1/questions/[question_id]/vote/[username].ts @@ -0,0 +1,50 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpNotFound, sendHttpOk } from 'multiverse/next-api-respond'; +import { applyVotesUpdateOperation, getHowUserVoted } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/:question_id/vote/:username' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + const vote = await getHowUserVoted({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: undefined, + comment_id: undefined + }); + + if (!vote) { + sendHttpNotFound(res, { + success: true, + error: 'a vote matching this user was not found on this question' + }); + } else { + sendHttpOk(res, { vote }); + } + } // * PATCH + else { + await applyVotesUpdateOperation({ + username: req.query.username?.toString(), + question_id: req.query.question_id?.toString(), + answer_id: undefined, + comment_id: undefined, + operation: { + op: req.body.operation, + target: req.body.target + } + }); + + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/index.ts b/src/pages/api/v1/questions/index.ts new file mode 100644 index 0000000..1f5d3be --- /dev/null +++ b/src/pages/api/v1/questions/index.ts @@ -0,0 +1,25 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { createQuestion } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions' +}; + +export default withMiddleware( + async (req, res) => { + // * POST + sendHttpOk(res, { + question: await createQuestion({ + data: req.body + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['POST'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/questions/search.ts b/src/pages/api/v1/questions/search.ts new file mode 100644 index 0000000..9fd452d --- /dev/null +++ b/src/pages/api/v1/questions/search.ts @@ -0,0 +1,45 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { searchQuestions } from 'universe/backend'; +import { ErrorMessage, ValidationError } from 'universe/error'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/questions/search' +}; + +export default withMiddleware( + async (req, res) => { + const match = (() => { + try { + return JSON.parse((req.query.match || '{}').toString()); + } catch { + throw new ValidationError(ErrorMessage.InvalidMatcher('match')); + } + })(); + + const regexMatch = (() => { + try { + return JSON.parse((req.query.regexMatch || '{}').toString()); + } catch { + throw new ValidationError(ErrorMessage.InvalidMatcher('regexMatch')); + } + })(); + + // * GET + sendHttpOk(res, { + questions: await searchQuestions({ + after_id: req.query.after?.toString(), + match, + regexMatch, + sort: req.query.sort?.toString() + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/[username]/answers.ts b/src/pages/api/v1/users/[username]/answers.ts new file mode 100644 index 0000000..07c6e18 --- /dev/null +++ b/src/pages/api/v1/users/[username]/answers.ts @@ -0,0 +1,26 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { getUserAnswers } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users/:username/answers' +}; + +export default withMiddleware( + async (req, res) => { + // * GET + sendHttpOk(res, { + answers: await getUserAnswers({ + username: req.query.username?.toString(), + after_id: req.query.after?.toString() + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/[username]/auth.ts b/src/pages/api/v1/users/[username]/auth.ts new file mode 100644 index 0000000..2a3a09d --- /dev/null +++ b/src/pages/api/v1/users/[username]/auth.ts @@ -0,0 +1,38 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { authAppUser } from 'universe/backend'; +import { sendHttpOk, sendHttpUnauthorized } from 'multiverse/next-api-respond'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users/:username/auth' +}; + +// * The next version of this should use GET and POST as follows: +// TODO: 1. GET to get the permanent user salt and a one-time fresh salt +// TODO: Fresh salts are stored per-ip and expire after 15 seconds +// TODO: 2. POST to send fresh salt & PBKDF(fresh salt, PBKDF(user salt, passwd)) +// TODO: Client sends fresh salt only to confirm that salt hasn't expired. +// ! !!! CLIENT-PROVIDED SALT IS NOT REFERENCED TO PERFORM PBKDF !!! +// TODO: 3. Server checks client-provided salt against db, confirms not expired +// TODO: If expired, server responds with HTTP 410 GONE and terminates +// TODO: 4. Server computes PBKDF(fresh salt, stored key) and compares +// TODO: 5. Server responds to POST with 200 on success, 403 on error +// TODO: 6. After response is sent, close connection & maybe prune expired salts + +export default withMiddleware( + async (req, res) => { + // * POST + (await authAppUser({ + username: req.query.username?.toString(), + key: req.body?.key + })) + ? sendHttpOk(res) + : sendHttpUnauthorized(res); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['POST'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/[username]/index.ts b/src/pages/api/v1/users/[username]/index.ts new file mode 100644 index 0000000..a66d2d3 --- /dev/null +++ b/src/pages/api/v1/users/[username]/index.ts @@ -0,0 +1,31 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { getUser, updateUser, deleteUser } from 'universe/backend'; +import { sendHttpOk } from 'multiverse/next-api-respond'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users/:username' +}; + +export default withMiddleware( + async (req, res) => { + const username = req.query.username?.toString(); + + if (req.method == 'GET') { + sendHttpOk(res, { user: await getUser({ username }) }); + } else if (req.method == 'DELETE') { + await deleteUser({ username }); + sendHttpOk(res); + } // * PATCH + else { + await updateUser({ username, data: req.body }); + sendHttpOk(res); + } + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'DELETE', 'PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/[username]/points.ts b/src/pages/api/v1/users/[username]/points.ts new file mode 100644 index 0000000..3178660 --- /dev/null +++ b/src/pages/api/v1/users/[username]/points.ts @@ -0,0 +1,31 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { updateUser } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users/:username/points' +}; + +export default withMiddleware( + async (req, res) => { + // * PATCH + await updateUser({ + username: req.query.username?.toString(), + data: { + points: { + op: req.body.operation, + amount: req.body.amount + } + } + }); + + sendHttpOk(res); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['PATCH'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/[username]/questions.ts b/src/pages/api/v1/users/[username]/questions.ts new file mode 100644 index 0000000..8adede0 --- /dev/null +++ b/src/pages/api/v1/users/[username]/questions.ts @@ -0,0 +1,26 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { sendHttpOk } from 'multiverse/next-api-respond'; +import { getUserQuestions } from 'universe/backend'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users/:username/questions' +}; + +export default withMiddleware( + async (req, res) => { + // * GET + sendHttpOk(res, { + questions: await getUserQuestions({ + username: req.query.username?.toString(), + after_id: req.query.after?.toString() + }) + }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET'], apiVersion: '1' } + } +); diff --git a/src/pages/api/v1/users/index.ts b/src/pages/api/v1/users/index.ts new file mode 100644 index 0000000..1a74bd1 --- /dev/null +++ b/src/pages/api/v1/users/index.ts @@ -0,0 +1,25 @@ +import { withMiddleware } from 'universe/backend/middleware'; +import { createUser, getAllUsers } from 'universe/backend'; +import { sendHttpOk } from 'multiverse/next-api-respond'; + +// ? This is a NextJS special "config" export +export { defaultConfig as config } from 'universe/backend/api'; + +export const metadata = { + descriptor: '/users' +}; + +export default withMiddleware( + async (req, res) => { + if (req.method == 'GET') { + sendHttpOk(res, { + users: await getAllUsers({ after_id: req.query.after?.toString() }) + }); + // * POST + } else sendHttpOk(res, { user: await createUser({ data: req.body }) }); + }, + { + descriptor: metadata.descriptor, + options: { allowedMethods: ['GET', 'POST'], apiVersion: '1' } + } +); diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100644 index 0000000..6e6b20b --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { version as pkgVersion } from 'package'; +import { getEnv } from 'universe/backend/env'; + +export async function getServerSideProps() { + const env = getEnv(); + + return { + props: { + isInProduction: env.NODE_ENV == 'production', + nodeEnv: env.NODE_ENV, + nodeVersion: process.version + } + }; +} + +export default function Index({ + isInProduction, + nodeEnv, + nodeVersion +}: Awaited>['props']) { + return ( + +

+ Serverless node runtime: {nodeVersion}
+ blogpress runtime: {`v${pkgVersion}`} +
+

+

+ Environment: {nodeEnv}
+ Production mode:{' '} + + {isInProduction ? ( + yes + ) : ( + no + )} + +
+

+
+ ); +} diff --git a/test/api/integration.test.ts b/test/api/integration.test.ts new file mode 100644 index 0000000..e52afa2 --- /dev/null +++ b/test/api/integration.test.ts @@ -0,0 +1,239 @@ +/* eslint-disable jest/require-hook */ +import { testApiHandler } from 'next-test-api-route-handler'; +import { get as dotPath } from 'dot-prop'; +import { toss } from 'toss-expression'; +import { GuruMeditationError } from 'universe/error'; +import { mockEnvFactory } from 'testverse/setup'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { getFixtures } from 'testverse/fixtures/integration'; +import { BANNED_BEARER_TOKEN, DUMMY_BEARER_TOKEN } from 'multiverse/next-auth'; +import { api } from 'testverse/fixtures'; +import { getDb } from 'multiverse/mongo-schema'; + +import type { TestResultset, TestResult } from 'testverse/fixtures/integration'; + +setupMemoryServerOverride({ + // ? Ensure all tests share the same database state + defer: true +}); + +const withMockedEnv = mockEnvFactory( + { OVERRIDE_EXPECT_ENV: 'force-check' }, + { replace: false } +); + +// ? Memory of the results of past fixture runs. +const memory: TestResultset = [ + { status: Infinity, json: {} } +] as unknown as TestResultset; + +memory.latest = memory[0]; +memory.getResultAt = () => memory[0]; +memory.idMap = {}; + +// ? Fail fast and early +let lastRunSuccess = true; + +describe('> middleware correctness tests', () => { + Object.values(api) + .flatMap((v) => Object.values(v)) + .forEach((endpoint) => { + it(`${endpoint.uri} fails on bad authentication`, async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: endpoint, + test: async ({ fetch }) => { + await expect(fetch().then((r) => r.status)).resolves.toBe(401); + } + }); + }, + { + REQUESTS_PER_CONTRIVED_ERROR: '0', + IGNORE_RATE_LIMITS: 'true' + } + ); + }); + + it(`${endpoint.uri} fails if rate limited`, async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: endpoint, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${BANNED_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(429); + } + }); + }, + { + REQUESTS_PER_CONTRIVED_ERROR: '0', + IGNORE_RATE_LIMITS: 'false' + } + ); + }); + }); +}); + +describe('> fable integration tests', () => { + // ? Clear the request-log so contrived errors are counted properly + beforeAll(async () => { + await (await getDb({ name: 'root' })).collection('request-log').deleteMany({}); + }); + + let countSkippedTests = 0; + + afterAll(() => { + if (countSkippedTests) + // eslint-disable-next-line no-console + console.warn(`${countSkippedTests} tests were skipped!`); + }); + + getFixtures(api).forEach( + ({ + displayIndex, + subject, + handler, + method, + response, + body, + id, + params, + invisible + }) => { + if (!displayIndex) { + throw new GuruMeditationError( + 'fixture is missing required property "displayIndex"' + ); + } + + const shouldSkip = + !subject || + !handler || + !method || + (!invisible && + (!response || !['number', 'function'].includes(typeof response.status))); + + // eslint-disable-next-line jest/prefer-expect-assertions + (process.env.RUN_ONLY ? it.only : it)( + `${shouldSkip ? ' ' : ''}${ + displayIndex <= 0 ? '###' : '#' + displayIndex + } ${method ? '[' + method + '] ' : ''}${ + handler?.uri ? handler.uri + ' ' : '' + }${subject || ''}`, + async () => { + if (shouldSkip || (!lastRunSuccess && process.env.FAIL_FAST)) { + countSkippedTests++; + return; + } + + expect.hasAssertions(); + lastRunSuccess = false; + + memory.getResultAt = ( + index: number | string, + prop?: string + ): TestResult | T => { + const result: TestResult = + typeof index == 'string' + ? memory.idMap[index] + : memory[index + (index < 0 ? displayIndex : 1)]; + + const retval = prop ? dotPath(result?.json, prop) : result; + + if (!result) { + throw new GuruMeditationError(`no result at index "${index}"`); + } else if (retval === undefined) { + throw new GuruMeditationError( + `${ + prop ? 'prop path "' + prop + '" ' : '' + }return value cannot be undefined` + ); + } + + return retval; + }; + + const requestParams = + typeof params == 'function' ? await params(memory) : params; + const requestBody = typeof body == 'function' ? await body(memory) : body; + + await withMockedEnv( + async () => { + await testApiHandler({ + handler: handler || toss(new GuruMeditationError()), + params: requestParams, + requestPatcher: (req) => { + req.headers.authorization = `bearer ${DUMMY_BEARER_TOKEN}`; + req.headers['content-type'] = 'application/json'; + }, + test: async ({ fetch }) => { + const res = await fetch({ + method: method, + ...(requestBody ? { body: JSON.stringify(requestBody) } : {}) + }); + + const expectedStatus = + typeof response?.status == 'function' + ? await response.status(res.status, memory) + : response?.status; + + let json: ReturnType; + + try { + const jsonText = await res.text(); + json = `${jsonText}`; + json = JSON.parse(jsonText); + } catch {} + + if (expectedStatus) { + if (res.status != expectedStatus) { + // eslint-disable-next-line no-console + console.warn('unexpected status for result:', json); + } + + // eslint-disable-next-line jest/no-conditional-expect + expect(res.status).toBe(expectedStatus); + // eslint-disable-next-line jest/no-conditional-expect + expect(json.success)[ + res.status == 200 ? 'toBeTrue' : 'toBeFalsy' + ](); + delete json.success; + } + + const expectedJson = + typeof response?.json == 'function' + ? await response.json(json, memory) + : response?.json; + + if (expectedJson) { + // eslint-disable-next-line jest/no-conditional-expect + expect(json).toStrictEqual(expectedJson); + } + + const memorize = { status: res.status, json } as TestResult; + + if (id) memory.idMap[id] = memorize; + memory[displayIndex] = memorize; + memory.latest = memorize; + lastRunSuccess = true; + } + }); + }, + { + REQUESTS_PER_CONTRIVED_ERROR: '10', + IGNORE_RATE_LIMITS: 'true' + } + ); + } + ); + } + ); +}); diff --git a/test/api/unit-api-mail.test.ts b/test/api/unit-api-mail.test.ts new file mode 100644 index 0000000..21d40af --- /dev/null +++ b/test/api/unit-api-mail.test.ts @@ -0,0 +1,59 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { testApiHandler } from 'next-test-api-route-handler'; +import { api, setupMockBackend } from 'testverse/fixtures'; + +jest.mock('universe/backend'); +jest.mock('universe/backend/middleware', () => { + const { middlewareFactory } = require('multiverse/next-api-glue'); + const { default: handleError } = require('multiverse/next-adhesive/handle-error'); + + return { + withMiddleware: jest + .fn() + .mockImplementation(middlewareFactory({ use: [], useOnError: [handleError] })) + }; +}); + +setupMockBackend(); + +describe('api/v1/mail', () => { + describe('/ [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.mail, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.message).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:username [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.mailUsername, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.messages).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); +}); diff --git a/test/api/unit-api-questions.test.ts b/test/api/unit-api-questions.test.ts new file mode 100644 index 0000000..4b61afd --- /dev/null +++ b/test/api/unit-api-questions.test.ts @@ -0,0 +1,532 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { testApiHandler } from 'next-test-api-route-handler'; +import { api, setupMockBackend } from 'testverse/fixtures'; + +jest.mock('universe/backend'); +jest.mock('universe/backend/middleware', () => { + const { middlewareFactory } = require('multiverse/next-api-glue'); + const { default: handleError } = require('multiverse/next-adhesive/handle-error'); + + return { + withMiddleware: jest + .fn() + .mockImplementation(middlewareFactory({ use: [], useOnError: [handleError] })) + }; +}); + +const { mockedGetHowUserVoted } = setupMockBackend(); + +describe('api/v1/questions', () => { + describe('/ [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questions, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.question).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/search [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsSearch, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.questions).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + + await testApiHandler({ + handler: api.v1.questionsSearch, + params: { + username: 'User1', + after: 'id', + match: '{"a":1}', + regexMatch: '{"b":1}' + }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.questions).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + + await testApiHandler({ + handler: api.v1.questionsSearch, + params: { username: 'User1', match: 'x' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(400); + expect(json.success).toBeFalse(); + expect(json.error).toBeString(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + + await testApiHandler({ + handler: api.v1.questionsSearch, + params: { username: 'User1', regexMatch: 'x' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(400); + expect(json.success).toBeFalse(); + expect(json.error).toBeString(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionId, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.question).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionId, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/vote/:username [GET]', () => { + it('accepts GET requests and responds with 200 or 404', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdVoteUsername, + test: async ({ fetch }) => { + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve(null)); + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve('upvoted')); + + const [status1, json1] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + const [status2, json2] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status1).toBe(404); + expect(json1.success).toBeTrue(); + expect(json1.error).toBeString(); + expect(Object.keys(json1)).toHaveLength(2); + + expect(status2).toBe(200); + expect(json2.success).toBeTrue(); + expect(json2.vote).toBeString(); + expect(Object.keys(json2)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/vote/:username [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdVoteUsername, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/comments [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdComments, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.comments).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/comments [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdComments, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.comment).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/comments/:comment_id [DELETE]', () => { + it('accepts DELETE requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdCommentsCommentId, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'DELETE' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/comments/:comment_id/vote/:username [GET]', () => { + it('accepts GET requests and responds with 200 or 404', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdCommentsCommentIdVoteUsername, + test: async ({ fetch }) => { + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve(null)); + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve('upvoted')); + + const [status1, json1] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + const [status2, json2] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status1).toBe(404); + expect(json1.success).toBeTrue(); + expect(json1.error).toBeString(); + expect(Object.keys(json1)).toHaveLength(2); + + expect(status2).toBe(200); + expect(json2.success).toBeTrue(); + expect(json2.vote).toBeString(); + expect(Object.keys(json2)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/comments/:comment_id/vote/:username [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdCommentsCommentIdVoteUsername, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/answers [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswers, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.answers).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswers, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.answer).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerId, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/vote/:username [GET]', () => { + it('accepts GET requests and responds with 200 or 404', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerIdVoteUsername, + test: async ({ fetch }) => { + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve(null)); + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve('upvoted')); + + const [status1, json1] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + const [status2, json2] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status1).toBe(404); + expect(json1.success).toBeTrue(); + expect(json1.error).toBeString(); + expect(Object.keys(json1)).toHaveLength(2); + + expect(status2).toBe(200); + expect(json2.success).toBeTrue(); + expect(json2.vote).toBeString(); + expect(Object.keys(json2)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/vote/:username [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerIdVoteUsername, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/comments [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerIdComments, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.comments).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/comments [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerIdComments, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.comment).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/comments/:comment_id [DELETE]', () => { + it('accepts DELETE requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentId, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'DELETE' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/comments/:comment_id/vote/:username [GET]', () => { + it('accepts GET requests and responds with 200 or 404', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: + api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername, + test: async ({ fetch }) => { + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve(null)); + mockedGetHowUserVoted.mockReturnValueOnce(Promise.resolve('upvoted')); + + const [status1, json1] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + const [status2, json2] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status1).toBe(404); + expect(json1.success).toBeTrue(); + expect(json1.error).toBeString(); + expect(Object.keys(json1)).toHaveLength(2); + + expect(status2).toBe(200); + expect(json2.success).toBeTrue(); + expect(json2.vote).toBeString(); + expect(Object.keys(json2)).toHaveLength(2); + } + }); + }); + }); + + describe('/:question_id/answers/:answer_id/comments/:comment_id/vote/:username [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: + api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); +}); diff --git a/test/api/unit-api-users.test.ts b/test/api/unit-api-users.test.ts new file mode 100644 index 0000000..beaf173 --- /dev/null +++ b/test/api/unit-api-users.test.ts @@ -0,0 +1,219 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { testApiHandler } from 'next-test-api-route-handler'; +import { api, setupMockBackend } from 'testverse/fixtures'; + +jest.mock('universe/backend'); +jest.mock('universe/backend/middleware', () => { + const { middlewareFactory } = require('multiverse/next-api-glue'); + const { default: handleError } = require('multiverse/next-adhesive/handle-error'); + + return { + withMiddleware: jest + .fn() + .mockImplementation(middlewareFactory({ use: [], useOnError: [handleError] })) + }; +}); + +const { mockedAuthAppUser } = setupMockBackend(); + +describe('api/v1/users', () => { + describe('/ [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.users, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.users).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/ [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.users, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.user).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:username [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsername, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.user).toBeObject(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:username [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsername, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:username [DELETE]', () => { + it('accepts DELETE requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsername, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'DELETE' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:username/auth [POST]', () => { + it('accepts POST requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsernameAuth, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(403); + expect(json.success).toBeFalse(); + expect(json.error).toBeString(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + + mockedAuthAppUser.mockReturnValue(Promise.resolve(true)); + + await testApiHandler({ + handler: api.v1.usersUsernameAuth, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'POST' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); + + describe('/:username/questions [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsernameQuestions, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.questions).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:username/answers [GET]', () => { + it('accepts GET requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsernameAnswers, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'GET' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(json.answers).toBeArray(); + expect(Object.keys(json)).toHaveLength(2); + } + }); + }); + }); + + describe('/:username/points [PATCH]', () => { + it('accepts PATCH requests', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: api.v1.usersUsernamePoints, + params: { username: 'User1' }, + test: async ({ fetch }) => { + const [status, json] = await fetch({ method: 'PATCH' }).then( + async (r) => [r.status, await r.json()] as [status: number, json: any] + ); + + expect(status).toBe(200); + expect(json.success).toBeTrue(); + expect(Object.keys(json)).toHaveLength(1); + } + }); + }); + }); +}); diff --git a/test/api/unit-backend.test.ts b/test/api/unit-backend.test.ts new file mode 100644 index 0000000..d1084a9 --- /dev/null +++ b/test/api/unit-backend.test.ts @@ -0,0 +1,5800 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-await-in-loop */ +import { ObjectId } from 'mongodb'; +import randomCase from 'random-case'; + +import * as Backend from 'universe/backend'; +import { getEnv } from 'universe/backend/env'; +import { ErrorMessage, GuruMeditationError } from 'universe/error'; + +import { + selectAnswerFromDb, + selectCommentFromDb, + InternalQuestion, + InternalUser, + NewAnswer, + NewComment, + NewMail, + NewQuestion, + PatchAnswer, + PatchQuestion, + PointsUpdateOperation, + PublicAnswer, + PublicComment, + PublicMail, + PublicQuestion, + toPublicAnswer, + toPublicComment, + toPublicMail, + toPublicQuestion, + toPublicUser, + patchAnswerInDb, + patchCommentInDb, + questionStatuses, + InternalMail +} from 'universe/backend/db'; + +import { useMockDateNow } from 'multiverse/mongo-common'; +import { getDb } from 'multiverse/mongo-schema'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { itemToObjectId, itemToStringId } from 'multiverse/mongo-item'; + +import { dummyAppData } from 'testverse/db'; +import { mockEnvFactory } from 'testverse/setup'; + +import type { PublicUser, NewUser, PatchUser } from 'universe/backend/db'; +import { toss } from 'toss-expression'; + +setupMemoryServerOverride(); +useMockDateNow(); + +const withMockedEnv = mockEnvFactory({ NODE_ENV: 'test' }); +const sortedUsers = dummyAppData.users.slice().reverse(); + +// ? A primitive attempt to replicate MongoDB's sort by { upvotes: -1, _id: -1 } +const sortByFieldAndId = ( + questions: InternalQuestion[], + field: 'upvotes' | 'uvc' | 'uvac' +) => { + const getField = (question: InternalQuestion) => { + return field == 'upvotes' + ? question.upvotes + : field == 'uvc' + ? question.sorter.uvc + : field == 'uvac' + ? question.sorter.uvac + : toss(new GuruMeditationError('unknown sort field')); + }; + + const sortedQuestions = questions + .slice() + .sort( + (a, b) => + getField(b) - getField(a) || + parseInt(b._id.toString().slice(-5), 16) - + parseInt(a._id.toString().slice(-5), 16) + ); + + return sortedQuestions; +}; + +describe('::getAllUsers', () => { + it('returns all users in order (latest first)', async () => { + expect.hasAssertions(); + + await expect(Backend.getAllUsers({ after_id: undefined })).resolves.toStrictEqual( + sortedUsers.map(toPublicUser) + ); + }); + + it('does not crash when database is empty', async () => { + expect.hasAssertions(); + + await expect( + Backend.getAllUsers({ after_id: undefined }) + ).resolves.not.toStrictEqual([]); + + await (await getDb({ name: 'app' })).collection('users').deleteMany({}); + await expect(Backend.getAllUsers({ after_id: undefined })).resolves.toStrictEqual( + [] + ); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getAllUsers({ after_id: undefined }), + await Backend.getAllUsers({ + after_id: itemToStringId(sortedUsers[0]) + }), + await Backend.getAllUsers({ + after_id: itemToStringId(sortedUsers[1]) + }), + await Backend.getAllUsers({ + after_id: itemToStringId(sortedUsers[2]) + }), + await Backend.getAllUsers({ + after_id: itemToStringId(sortedUsers[3]) + }) + ]).toStrictEqual([...sortedUsers.map((user) => [toPublicUser(user)]), []]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect(Backend.getAllUsers({ after_id: 'fake-oid' })).rejects.toMatchObject( + { message: ErrorMessage.InvalidObjectId('fake-oid') } + ); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect(Backend.getAllUsers({ after_id })).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'user_id') + }); + }); +}); + +describe('::getUser', () => { + it('returns user by username', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUser({ username: dummyAppData.users[0].username }) + ).resolves.toStrictEqual(toPublicUser(dummyAppData.users[0])); + }); + + it('rejects if username missing or not found', async () => { + expect.hasAssertions(); + const username = 'does-not-exist'; + + await expect(Backend.getUser({ username })).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(username, 'user') + }); + + await expect(Backend.getUser({ username: undefined })).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::getUserQuestions', () => { + it('returns all questions created by the user in order (latest first)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserQuestions({ + username: dummyAppData.users[0].username, + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicQuestion(dummyAppData.questions[1]), + toPublicQuestion(dummyAppData.questions[0]) + ]); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getUserQuestions({ + username: dummyAppData.users[0].username, + after_id: undefined + }), + await Backend.getUserQuestions({ + username: dummyAppData.users[0].username, + after_id: dummyAppData.users[0].questionIds[1].toString() + }), + await Backend.getUserQuestions({ + username: dummyAppData.users[0].username, + after_id: dummyAppData.users[0].questionIds[0].toString() + }) + ]).toStrictEqual([ + [toPublicQuestion(dummyAppData.questions[1])], + [toPublicQuestion(dummyAppData.questions[0])], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserQuestions({ + username: dummyAppData.users[0].username, + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.getUserQuestions({ username: dummyAppData.users[0].username, after_id }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'question_id') + }); + }); + + it('rejects if username missing or not found', async () => { + expect.hasAssertions(); + const username = 'does-not-exist'; + + await expect( + Backend.getUserQuestions({ username, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(username, 'user') + }); + + await expect( + Backend.getUserQuestions({ username: undefined, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::getUserAnswers', () => { + it('returns all answers created by the user in order (latest first)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicAnswer( + dummyAppData.questions[4].answerItems[0], + dummyAppData.questions[4]._id + ), + toPublicAnswer( + dummyAppData.questions[1].answerItems[0], + dummyAppData.questions[1]._id + ), + toPublicAnswer( + dummyAppData.questions[0].answerItems[0], + dummyAppData.questions[0]._id + ) + ]); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: undefined + }), + await Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: dummyAppData.users[1].answerIds[2][1].toString() + }), + await Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: dummyAppData.users[1].answerIds[1][1].toString() + }), + await Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: dummyAppData.users[1].answerIds[0][1].toString() + }) + ]).toStrictEqual([ + [ + toPublicAnswer( + dummyAppData.questions[4].answerItems[0], + dummyAppData.questions[4]._id + ) + ], + [ + toPublicAnswer( + dummyAppData.questions[1].answerItems[0], + dummyAppData.questions[1]._id + ) + ], + [ + toPublicAnswer( + dummyAppData.questions[0].answerItems[0], + dummyAppData.questions[0]._id + ) + ], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserAnswers({ + username: dummyAppData.users[1].username, + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.getUserAnswers({ username: dummyAppData.users[1].username, after_id }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'answer_id') + }); + }); + + it('rejects if username missing or not found', async () => { + expect.hasAssertions(); + const username = 'does-not-exist'; + + await expect( + Backend.getUserAnswers({ username, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(username, 'user') + }); + + await expect( + Backend.getUserAnswers({ username: undefined, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::createUser', () => { + it('creates and returns a new user', async () => { + expect.hasAssertions(); + + const newUser: Required = { + username: 'new-user', + email: 'new-user@email.com', + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH) + }; + + await expect( + Backend.createUser({ data: newUser }) + ).resolves.toStrictEqual({ + user_id: expect.any(String), + username: newUser.username, + email: newUser.email, + salt: newUser.salt, + points: 1, + questions: 0, + answers: 0 + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .countDocuments({ username: 'new-user' }) + ).resolves.toBe(1); + }); + + it('rejects when attempting to create a user with a duplicate username or email', async () => { + expect.hasAssertions(); + + await expect( + Backend.createUser({ + data: { + username: dummyAppData.users[0].username, + email: 'new-user@email.com', + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH) + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.DuplicateFieldValue('username') + }); + + await expect( + Backend.createUser({ + data: { + username: 'new-user', + email: dummyAppData.users[0].email, + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH) + } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateFieldValue('email') }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { + MIN_USER_NAME_LENGTH: minULen, + MAX_USER_NAME_LENGTH: maxULen, + MIN_USER_EMAIL_LENGTH: minELen, + MAX_USER_EMAIL_LENGTH: maxELen, + USER_SALT_LENGTH: saltLen, + USER_KEY_LENGTH: keyLen + } = getEnv(); + + const newUsers: [NewUser, string][] = [ + [undefined as unknown as NewUser, ErrorMessage.InvalidJSON()], + ['string data' as NewUser, ErrorMessage.InvalidJSON()], + [ + {} as NewUser, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: null } as unknown as NewUser, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(minELen - 1) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(maxELen + 1) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(maxELen) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'valid@email.address' }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen - 1) + }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: null + } as unknown as NewUser, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: 'x'.repeat(saltLen) + }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen) + }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen - 1) + }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: 'x'.repeat(keyLen) + }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: null + } as unknown as NewUser, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { + username: 'must be alphanumeric', + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen) + }, + ErrorMessage.InvalidStringLength('username', minULen, maxULen, 'alphanumeric') + ], + [ + { + username: '#&*@^(#@(^$&*#', + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen) + }, + ErrorMessage.InvalidStringLength('username', minULen, maxULen, 'alphanumeric') + ], + [ + { + username: null, + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen) + } as unknown as NewUser, + ErrorMessage.InvalidStringLength('username', minULen, maxULen, 'alphanumeric') + ], + [ + { + username: 'x'.repeat(minULen - 1), + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen) + }, + ErrorMessage.InvalidStringLength('username', minULen, maxULen, 'alphanumeric') + ], + [ + { + username: 'x'.repeat(maxULen + 1), + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen) + }, + ErrorMessage.InvalidStringLength('username', minULen, maxULen, 'alphanumeric') + ], + [ + { + username: 'x'.repeat(maxULen - 1), + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen), + user_id: 1 + } as NewUser, + ErrorMessage.UnknownField('user_id') + ] + ]; + + await Promise.all( + newUsers.map(([data, message]) => + expect(Backend.createUser({ data })).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::updateUser', () => { + it('updates an existing user', async () => { + expect.hasAssertions(); + + const usersDb = (await getDb({ name: 'app' })).collection('users'); + + const patchUser: PatchUser = { + email: 'fake@email.com', + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH), + points: 50 + }; + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + ...patchUser + }) + ).resolves.toBe(0); + + await expect( + Backend.updateUser({ + username: dummyAppData.users[0].username, + data: patchUser + }) + ).resolves.toBeUndefined(); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + ...patchUser + }) + ).resolves.toBe(1); + }); + + it('supports PointsUpdateOperation updates alongside normal points updates', async () => { + expect.hasAssertions(); + + const usersDb = (await getDb({ name: 'app' })).collection('users'); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: dummyAppData.users[0].points + 1000 + }) + ).resolves.toBe(0); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: dummyAppData.users[0].points + 1000 - 456 + }) + ).resolves.toBe(0); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: 0 + }) + ).resolves.toBe(0); + + await Backend.updateUser({ + username: dummyAppData.users[0].username, + data: { points: { op: 'increment', amount: 1000 } } + }); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: dummyAppData.users[0].points + 1000 + }) + ).resolves.toBe(1); + + await Backend.updateUser({ + username: dummyAppData.users[0].username, + data: { points: { op: 'decrement', amount: 456 } } + }); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: dummyAppData.users[0].points + 1000 - 456 + }) + ).resolves.toBe(1); + + await Backend.updateUser({ + username: dummyAppData.users[0].username, + data: { points: 0 } + }); + + await expect( + usersDb.countDocuments({ + username: dummyAppData.users[0].username, + points: 0 + }) + ).resolves.toBe(1); + }); + + it('does not reject when demonstrating idempotency', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateUser({ + username: dummyAppData.users[0].username, + data: { salt: dummyAppData.users[0].salt } + }) + ).resolves.toBeUndefined(); + }); + + it('does not reject if no data passed in', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateUser({ + username: 'does-not-exist', + data: {} + }) + ).resolves.toBeUndefined(); + }); + + it('rejects if the username is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateUser({ + username: 'fake-user', + data: { + email: 'fake@email.com', + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH) + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('fake-user', 'user') + }); + + await expect( + Backend.updateUser({ + username: undefined, + data: { + email: 'fake@email.com', + key: '0'.repeat(getEnv().USER_KEY_LENGTH), + salt: '0'.repeat(getEnv().USER_SALT_LENGTH) + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); + + it('rejects when attempting to update a user to a duplicate email', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateUser({ + username: dummyAppData.users[1].username, + data: { + email: dummyAppData.users[0].email + } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateFieldValue('email') }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { + MIN_USER_EMAIL_LENGTH: minELen, + MAX_USER_EMAIL_LENGTH: maxELen, + USER_SALT_LENGTH: saltLen, + USER_KEY_LENGTH: keyLen + } = getEnv(); + + const patchUsers: [PatchUser, string][] = [ + [undefined as unknown as PatchUser, ErrorMessage.InvalidJSON()], + ['string data' as PatchUser, ErrorMessage.InvalidJSON()], + [ + { email: '' }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(minELen - 1) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(maxELen + 1) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { email: 'x'.repeat(maxELen) }, + ErrorMessage.InvalidStringLength('email', minELen, maxELen, 'string') + ], + [ + { salt: '' }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { salt: '0'.repeat(saltLen - 1) }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { salt: 'x'.repeat(saltLen) }, + ErrorMessage.InvalidStringLength('salt', saltLen, null, 'hexadecimal') + ], + [ + { key: '' }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { key: '0'.repeat(keyLen - 1) }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [ + { key: 'x'.repeat(keyLen) }, + ErrorMessage.InvalidStringLength('key', keyLen, null, 'hexadecimal') + ], + [{ points: -1 }, ErrorMessage.InvalidNumberValue('points', 0, null, 'integer')], + [ + { points: null as unknown as number }, + ErrorMessage.InvalidNumberValue('points', 0, null, 'integer') + ], + [ + { points: '10' as unknown as number }, + ErrorMessage.InvalidNumberValue('points', 0, null, 'integer') + ], + [ + { points: {} as PointsUpdateOperation }, + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ], + [ + { points: { amount: 5 } as PointsUpdateOperation }, + ErrorMessage.InvalidFieldValue('operation', undefined, [ + 'increment', + 'decrement' + ]) + ], + [ + { + points: { + amount: '5', + op: 'decrement' + } as unknown as PointsUpdateOperation + }, + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ], + [ + { points: { op: 'decrement' } as PointsUpdateOperation }, + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ], + [ + { points: { amount: -1, op: 'decrement' } as PointsUpdateOperation }, + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ], + [ + { + points: { amount: 5, op: 'nope' } as unknown as PointsUpdateOperation + }, + ErrorMessage.InvalidFieldValue('operation', 'nope', [ + 'increment', + 'decrement' + ]) + ], + [ + { + points: { + amount: 'x', + op: 'nope' + } as unknown as PointsUpdateOperation + }, + ErrorMessage.InvalidNumberValue('amount', 0, null, 'integer') + ], + [ + { + points: { + amount: 5, + op: 'increment', + bad: 'bad not good' + } as unknown as PointsUpdateOperation + }, + ErrorMessage.UnknownField('bad') + ], + [{ data: 1 } as NewUser, ErrorMessage.UnknownField('data')], + [{ name: 'username' } as NewUser, ErrorMessage.UnknownField('name')], + [ + { + email: 'valid@email.address', + salt: '0'.repeat(saltLen), + key: '0'.repeat(keyLen), + points: 0, + username: 'new-username' + } as PatchUser, + ErrorMessage.UnknownField('username') + ] + ]; + + await Promise.all( + patchUsers.map(([data, message]) => + expect( + Backend.updateUser({ username: dummyAppData.users[0].username, data }) + ).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::deleteUser', () => { + it('deletes a user', async () => { + expect.hasAssertions(); + + const usersDb = (await getDb({ name: 'app' })).collection('users'); + + await expect( + usersDb.countDocuments({ _id: itemToObjectId(dummyAppData.users[0]) }) + ).resolves.toBe(1); + + await expect( + Backend.deleteUser({ username: dummyAppData.users[0].username }) + ).resolves.toBeUndefined(); + + await expect( + usersDb.countDocuments({ _id: itemToObjectId(dummyAppData.users[0]) }) + ).resolves.toBe(0); + }); + + it('rejects if the username is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.deleteUser({ username: 'does-not-exist' }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect(Backend.deleteUser({ username: undefined })).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::authAppUser', () => { + it('returns true iff application-level key matches', async () => { + expect.hasAssertions(); + + await expect( + Backend.authAppUser({ username: 'User1', key: dummyAppData.users[0].key }) + ).resolves.toBeTrue(); + + await expect( + Backend.authAppUser({ username: 'User1', key: 'bad' }) + ).resolves.toBeFalse(); + }); + + it('returns false if application-level key is empty, null, or undefined', async () => { + expect.hasAssertions(); + + await expect( + Backend.authAppUser({ username: 'User1', key: '' }) + ).resolves.toBeFalse(); + + await expect( + Backend.authAppUser({ username: 'User1', key: null as unknown as string }) + ).resolves.toBeFalse(); + + await expect( + Backend.authAppUser({ username: 'User1', key: undefined as unknown as string }) + ).resolves.toBeFalse(); + }); +}); + +describe('::getUserMessages', () => { + it('returns all messages received by the user in order (latest first)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserMessages({ + username: dummyAppData.users[0].username, + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicMail(dummyAppData.mail[1]), + toPublicMail(dummyAppData.mail[0]) + ]); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getUserMessages({ + username: dummyAppData.users[0].username, + after_id: undefined + }), + await Backend.getUserMessages({ + username: dummyAppData.users[0].username, + after_id: itemToStringId(dummyAppData.mail[1]) + }), + await Backend.getUserMessages({ + username: dummyAppData.users[0].username, + after_id: itemToStringId(dummyAppData.mail[0]) + }) + ]).toStrictEqual([ + [toPublicMail(dummyAppData.mail[1])], + [toPublicMail(dummyAppData.mail[0])], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getUserMessages({ + username: dummyAppData.users[0].username, + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.getUserMessages({ username: dummyAppData.users[0].username, after_id }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'mail_id') + }); + }); + + it('rejects if username missing or not found', async () => { + expect.hasAssertions(); + const username = 'does-not-exist'; + + await expect( + Backend.getUserMessages({ username, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(username, 'user') + }); + + await expect( + Backend.getUserMessages({ username: undefined, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::createMessage', () => { + it('creates and returns a new message', async () => { + expect.hasAssertions(); + + const newMessage: Required = { + receiver: dummyAppData.users[2].username, + sender: dummyAppData.users[1].username, + subject: 'You have got mail!', + text: 'World, hello!' + }; + + await expect( + (await getDb({ name: 'app' })) + .collection('mail') + .countDocuments({ subject: 'You have got mail!' }) + ).resolves.toBe(0); + + await expect( + Backend.createMessage({ data: newMessage }) + ).resolves.toStrictEqual({ + mail_id: expect.any(String), + createdAt: Date.now(), + receiver: newMessage.receiver, + sender: newMessage.sender, + subject: newMessage.subject, + text: newMessage.text + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('mail') + .countDocuments({ subject: 'You have got mail!' }) + ).resolves.toBe(1); + }); + + it('rejects if the sender or receiver is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.createMessage({ + data: { + receiver: dummyAppData.users[0].username, + sender: 'does-not-exist', + subject: 'You have got mail!', + text: 'World, hello!' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.createMessage({ + data: { + receiver: 'does-not-exist', + sender: dummyAppData.users[0].username, + subject: 'You have got mail!', + text: 'World, hello!' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.createMessage({ + data: { + receiver: undefined, + sender: dummyAppData.users[0].username, + subject: 'You have got mail!', + text: 'World, hello!' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('receiver') + }); + + await expect( + Backend.createMessage({ + data: { + receiver: dummyAppData.users[0].username, + sender: undefined, + subject: 'You have got mail!', + text: 'World, hello!' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('sender') + }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { + MAX_MAIL_SUBJECT_LENGTH: maxSubjectLen, + MAX_MAIL_BODY_LENGTH_BYTES: maxBodyLen + } = getEnv(); + + const newMailMessages: [NewMail, string][] = [ + [undefined as unknown as NewMail, ErrorMessage.InvalidJSON()], + ['string data' as unknown as NewMail, ErrorMessage.InvalidJSON()], + [{} as NewMail, ErrorMessage.InvalidFieldValue('receiver')], + [ + { receiver: dummyAppData.users[0].username } as NewMail, + ErrorMessage.InvalidFieldValue('sender') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username + } as NewMail, + ErrorMessage.InvalidStringLength('subject', 1, maxSubjectLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: '' + } as NewMail, + ErrorMessage.InvalidStringLength('subject', 1, maxSubjectLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: 'x'.repeat(maxSubjectLen + 1) + } as NewMail, + ErrorMessage.InvalidStringLength('subject', 1, maxSubjectLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: 'x' + } as NewMail, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: 'x', + text: '' + } as NewMail, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: 'x', + text: 'x'.repeat(maxBodyLen + 1) + } as NewMail, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: dummyAppData.users[0].username, + subject: 'x', + text: 'x', + createdAt: Date.now() + } as NewMail, + ErrorMessage.UnknownField('createdAt') + ], + [ + { + receiver: 'does-not-exist', + sender: 'ignored', + subject: 'x', + text: 'x' + } as NewMail, + ErrorMessage.ItemNotFound('does-not-exist', 'user') + ], + [ + { + receiver: dummyAppData.users[0].username, + sender: 'does-not-exist', + subject: 'x', + text: 'x' + } as NewMail, + ErrorMessage.ItemNotFound('does-not-exist', 'user') + ] + ]; + + await Promise.all( + newMailMessages.map(([data, message]) => + expect(Backend.createMessage({ data })).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::deleteMessage', () => { + it('deletes a message (mail)', async () => { + expect.hasAssertions(); + + const mailDb = ( + await getDb({ + name: 'app' + }) + ).collection('mail'); + + await expect( + mailDb.countDocuments({ _id: itemToObjectId(dummyAppData.mail[0]) }) + ).resolves.toBe(1); + + await expect( + Backend.deleteMessage({ mail_id: itemToStringId(dummyAppData.mail[0]) }) + ).resolves.toBeUndefined(); + + await expect( + mailDb.countDocuments({ _id: itemToObjectId(dummyAppData.mail[0]) }) + ).resolves.toBe(0); + }); + + it('rejects if the mail_id is missing or not found', async () => { + expect.hasAssertions(); + + const mail_id = new ObjectId().toString(); + + await expect(Backend.deleteMessage({ mail_id })).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(mail_id, 'mail message') + }); + + await expect(Backend.deleteMessage({ mail_id: undefined })).rejects.toMatchObject( + { + message: ErrorMessage.InvalidItem('mail_id', 'parameter') + } + ); + }); +}); + +describe('::searchQuestions', () => { + const reversedInternalQuestions = dummyAppData.questions.slice().reverse(); + const reversedPublicQuestions = reversedInternalQuestions.map(toPublicQuestion); + + it('returns RESULTS_PER_PAGE questions in default sort order (insertion, latest first) if no query params given', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual(reversedPublicQuestions.slice(0, 3)); + }, + { RESULTS_PER_PAGE: '3' } + ); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + let prevQuestion: PublicQuestion | null = null; + + for (const question of reversedPublicQuestions) { + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion.question_id : undefined, + match: {}, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual([question]); + prevQuestion = question; + } + + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion.question_id : undefined, + match: {}, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual([]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('does not crash when database is empty', async () => { + expect.hasAssertions(); + + const db = await getDb({ name: 'app' }); + const questionsDb = db.collection('questions'); + + await questionsDb.deleteMany({}); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual([]); + }); + + it('returns expected questions when using match and regexMatch simultaneously', async () => { + expect.hasAssertions(); + + const regex = /(open|closed)/im; + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $lt: Date.now() } }, + regexMatch: { status: 'open|closed' }, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter( + (q) => q.createdAt < Date.now() && regex.test(q.status) + ) + ); + }); + + it('returns expected questions when matching case-insensitively by title', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { title: randomCase('where is the nhscc github page?') }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedInternalQuestions + .filter((q) => q['title-lowercase'] == 'where is the nhscc github page?') + .map(toPublicQuestion) + ); + }); + + it('returns expected questions when matching conditioned on createdAt', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $lt: Date.now() - 5000, $gt: Date.now() - 10 ** 5 } }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter( + (q) => q.createdAt < Date.now() - 5000 && q.createdAt > Date.now() - 10 ** 5 + ) + ); + }); + + it('supports special "$gt", "$gte", "$lt", "$lte" sub-matcher', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $lt: Date.now() - 10 ** 4 } }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter((q) => q.createdAt < Date.now() - 10 ** 4) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $lte: Date.now() - 5000 } }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter((q) => q.createdAt <= Date.now() - 5000) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $gt: Date.now() - 10 ** 4 } }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter((q) => q.createdAt > Date.now() - 10 ** 4) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { createdAt: { $gte: Date.now() - 98765 } }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter((q) => q.createdAt >= Date.now() - 98765) + ); + }); + + it('supports special "$or" sub-matcher', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { + createdAt: { + $or: [{ $lt: Date.now() - 10 ** 5 }, { $gte: Date.now() - 5000 }] + } + }, + regexMatch: {}, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter( + (q) => q.createdAt < Date.now() - 10 ** 5 || q.createdAt >= Date.now() - 5000 + ) + ); + }); + + it('supports multi-line case-insensitive regex matching of text via regexMatch', async () => { + expect.hasAssertions(); + + const regex = /^alsO:.*$/im; + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: { text: '^alsO:.*$' }, + sort: undefined + }) + ).resolves.toStrictEqual( + reversedPublicQuestions.filter((q) => regex.test(q.text)) + ); + }); + + it('supports sorting results by upvotes, upvotes+views+comments, and upvotes+views+answers+comments (highest first)', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: 'u' + }) + ).resolves.toStrictEqual( + sortByFieldAndId(reversedInternalQuestions, 'upvotes').map(toPublicQuestion) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: 'uvc' + }) + ).resolves.toStrictEqual( + sortByFieldAndId(reversedInternalQuestions, 'uvc').map(toPublicQuestion) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: 'uvac' + }) + ).resolves.toStrictEqual( + sortByFieldAndId(reversedInternalQuestions, 'uvac').map(toPublicQuestion) + ); + }); + + it('supports sorting matched results', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { answers: 0 }, + regexMatch: {}, + sort: 'uvc' + }) + ).resolves.toStrictEqual( + sortByFieldAndId(reversedInternalQuestions, 'uvc') + .filter((q) => !q.answers) + .map(toPublicQuestion) + ); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: { hasAcceptedAnswer: false }, + regexMatch: {}, + sort: 'uvac' + }) + ).resolves.toStrictEqual( + sortByFieldAndId(reversedInternalQuestions, 'uvac') + .filter((q) => !q.hasAcceptedAnswer) + .map(toPublicQuestion) + ); + }); + + it('supports pagination when sorting', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + let prevQuestion: InternalQuestion | null = null; + + for (const question of sortByFieldAndId( + reversedInternalQuestions, + 'upvotes' + )) { + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion._id.toString() : undefined, + match: {}, + regexMatch: {}, + sort: 'u' + }) + ).resolves.toStrictEqual([toPublicQuestion(question)]); + prevQuestion = question; + } + + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion._id.toString() : undefined, + match: {}, + regexMatch: {}, + sort: 'u' + }) + ).resolves.toStrictEqual([]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('supports paginated sorted results where matcher matches sorter', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + const interestingQuestions = sortByFieldAndId( + reversedInternalQuestions, + 'upvotes' + ) + .filter((q) => q.upvotes < 2048 && q.upvotes > 0) + .map(toPublicQuestion); + + let prevQuestion: PublicQuestion | null = null; + + for (const question of interestingQuestions) { + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion.question_id : undefined, + match: { upvotes: { $lt: 2048, $gt: 0 } }, + regexMatch: {}, + sort: 'u' + }) + ).resolves.toStrictEqual([question]); + prevQuestion = question; + } + + await expect( + Backend.searchQuestions({ + after_id: prevQuestion ? prevQuestion.question_id : undefined, + match: { upvotes: { $lt: 2048, $gt: 0 } }, + regexMatch: {}, + sort: 'u' + }) + ).resolves.toStrictEqual([]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if passed an invalid sort parameter', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: {}, + sort: 'nope' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('nope', 'sort parameter') + }); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.searchQuestions({ + after_id: 'fake-oid', + match: {}, + regexMatch: {}, + sort: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.searchQuestions({ + after_id, + match: {}, + regexMatch: {}, + sort: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'question_id') + }); + }); + + it('rejects when using match/regexMatch with disallowed, unknown, or non-proxied fields', async () => { + expect.hasAssertions(); + + const matchers: [ + match: Parameters[0]['match'], + regexMatch: Parameters[0]['regexMatch'], + errorMessage: string + ][] = [ + [ + { question_id: new ObjectId().toString() }, + {}, + ErrorMessage.UnknownSpecifier('question_id') + ], + [ + {}, + { question_id: new ObjectId().toString() }, + ErrorMessage.UnknownSpecifier('question_id') + ], + [ + { upvoterUsernames: [] as any }, + {}, + ErrorMessage.UnknownSpecifier('upvoterUsernames') + ], + [ + {}, + { upvoterUsernames: [] as any }, + ErrorMessage.UnknownSpecifier('upvoterUsernames') + ], + [{ 'sorter.uvc': {} as any }, {}, ErrorMessage.UnknownSpecifier('sorter.uvc')], + [{}, { sorter: {} as any }, ErrorMessage.UnknownSpecifier('sorter')], + [{}, { createdAt: '12345' }, ErrorMessage.UnknownSpecifier('createdAt')], + [{}, { upvotes: '10' }, ErrorMessage.UnknownSpecifier('upvotes')], + [ + {}, + { hasAcceptedAnswer: 'false' }, + ErrorMessage.UnknownSpecifier('hasAcceptedAnswer') + ], + [{ unknown: 'unknown' }, {}, ErrorMessage.UnknownSpecifier('unknown')], + [{}, { unknown: 'unknown' }, ErrorMessage.UnknownSpecifier('unknown')] + ]; + + await Promise.all( + matchers.map(([match, regexMatch, message]) => + expect( + Backend.searchQuestions({ + after_id: undefined, + match, + regexMatch, + sort: undefined + }) + ).rejects.toMatchObject({ message }) + ) + ); + }); + + it('rejects when match and regexMatch are given strange or bad inputs', async () => { + expect.hasAssertions(); + + const matchers: [ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + matcher: any, + errors: [matchError: string, regexMatchError: string] + ][] = [ + [ + 'wtf', + [ + ErrorMessage.InvalidMatcher('match'), + ErrorMessage.InvalidMatcher('regexMatch') + ] + ], + [ + null, + [ + ErrorMessage.InvalidMatcher('match'), + ErrorMessage.InvalidMatcher('regexMatch') + ] + ], + [ + undefined, + [ + ErrorMessage.InvalidMatcher('match'), + ErrorMessage.InvalidMatcher('regexMatch') + ] + ], + [ + { bad: 'super-bad' }, + [ErrorMessage.UnknownSpecifier('bad'), ErrorMessage.UnknownSpecifier('bad')] + ], + [ + { createdAt: () => 'wtf' }, + [ + ErrorMessage.InvalidSpecifierValueType( + 'createdAt', + 'a number, string, boolean, or sub-specifier object' + ), + ErrorMessage.UnknownSpecifier('createdAt') + ] + ], + [ + { status: /nope/g }, + [ + ErrorMessage.InvalidSpecifierValueType( + 'status', + 'a number, string, boolean, or sub-specifier object' + ), + ErrorMessage.InvalidRegexString('status') + ] + ], + [ + { upvotes: {} }, + [ + ErrorMessage.InvalidSpecifierValueType('upvotes', 'a non-empty object'), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $in: [5] } }, + [ + ErrorMessage.UnknownSpecifier('$in', true), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $lt: [5] } }, + [ + ErrorMessage.InvalidSpecifierValueType('$lt', 'a number', true), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: { $gt: 6 } } }, + [ErrorMessage.InvalidOrSpecifier(), ErrorMessage.UnknownSpecifier('upvotes')] + ], + [ + { upvotes: { $or: [{ $gt: 6 }, { $gt: 6 }, { $gt: 6 }] } }, + [ErrorMessage.InvalidOrSpecifier(), ErrorMessage.UnknownSpecifier('upvotes')] + ], + [ + { upvotes: { $or: ['b', { $gt: 6 }] } }, + [ + ErrorMessage.InvalidOrSpecifierNonObject(0), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ $gt: 6 }, 'b'] } }, + [ + ErrorMessage.InvalidOrSpecifierNonObject(1), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ $gt: 6 }, { $gt: 6, $lte: 5 }] } }, + [ + ErrorMessage.InvalidOrSpecifierBadLength(1), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ $gt: 7 }, undefined] } }, + [ + ErrorMessage.InvalidOrSpecifierNonObject(1), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{}] } }, + [ErrorMessage.InvalidOrSpecifier(), ErrorMessage.UnknownSpecifier('upvotes')] + ], + [ + { upvotes: { $or: [{}, {}] } }, + [ + ErrorMessage.InvalidSpecifierValueType('upvotes', 'a non-empty object'), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ bad: 1 }, { $gte: 5 }] } }, + [ + ErrorMessage.InvalidOrSpecifierInvalidKey(0, 'bad'), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ $gte: 5 }, { bad: 1 }] } }, + [ + ErrorMessage.InvalidOrSpecifierInvalidKey(1, 'bad'), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ], + [ + { upvotes: { $or: [{ $gte: 'bad' }, { $gte: 5 }] } }, + [ + ErrorMessage.InvalidOrSpecifierInvalidValueType(0, '$gte'), + ErrorMessage.UnknownSpecifier('upvotes') + ] + ] + ]; + + await Promise.all( + matchers.flatMap(([matcher, [matchMessage, regexMatchMessage]]) => { + return [ + // eslint-disable-next-line jest/valid-expect + expect( + Backend.searchQuestions({ + after_id: undefined, + match: matcher, + regexMatch: {}, + sort: undefined + }) + ).rejects.toMatchObject({ message: matchMessage }), + // eslint-disable-next-line jest/valid-expect + expect( + Backend.searchQuestions({ + after_id: undefined, + match: {}, + regexMatch: matcher, + sort: undefined + }) + ).rejects.toMatchObject({ message: regexMatchMessage }) + ]; + }) + ); + }); +}); + +describe('::getQuestion', () => { + it('returns question by question_id', async () => { + expect.hasAssertions(); + + await expect( + Backend.getQuestion({ question_id: itemToStringId(dummyAppData.questions[1]) }) + ).resolves.toStrictEqual(toPublicQuestion(dummyAppData.questions[1])); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + await expect( + Backend.getQuestion({ question_id: 'does-not-exist' }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('does-not-exist') + }); + + await expect( + Backend.getQuestion({ question_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + await expect(Backend.getQuestion({ question_id })).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); +}); + +describe('::createQuestion', () => { + it('creates and returns a new question', async () => { + expect.hasAssertions(); + + const newQuestion: Required = { + creator: dummyAppData.users[0].username, + title: 'Title', + text: 'Text' + }; + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .countDocuments({ text: 'Text' }) + ).resolves.toBe(0); + + await expect( + Backend.createQuestion({ data: newQuestion }) + ).resolves.toStrictEqual({ + question_id: expect.any(String), + creator: dummyAppData.users[0].username, + createdAt: Date.now(), + hasAcceptedAnswer: false, + title: 'Title', + text: 'Text', + status: 'open', + answers: 0, + comments: 0, + views: 0, + upvotes: 0, + downvotes: 0 + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .countDocuments({ text: 'Text' }) + ).resolves.toBe(1); + }); + + it("updates user's questions array when they create a new question", async () => { + expect.hasAssertions(); + + const newQuestion = await Backend.createQuestion({ + data: { + creator: dummyAppData.users[0].username, + title: 'Title', + text: 'Text' + } + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[0].username }, + { projection: { _id: false, questionIds: true } } + ) + ).resolves.toStrictEqual({ + questionIds: expect.arrayContaining([itemToObjectId(newQuestion.question_id)]) + }); + }); + + it('rejects if the creator is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.createQuestion({ + data: { + creator: 'does-not-exist', + title: 'Title', + text: 'Text' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.createQuestion({ + data: { + creator: undefined as any, + title: 'Title', + text: 'Text' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('creator') + }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { + MAX_QUESTION_TITLE_LENGTH: maxTitleLen, + MAX_QUESTION_BODY_LENGTH_BYTES: maxBodyLen + } = getEnv(); + + const newQuestions: [NewQuestion, string][] = [ + [undefined as unknown as NewQuestion, ErrorMessage.InvalidJSON()], + ['string data' as unknown as NewQuestion, ErrorMessage.InvalidJSON()], + [ + {} as NewQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + title: '' + } as NewQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + title: 'x'.repeat(maxTitleLen + 1) + } as NewQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + title: 'x' + } as NewQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + title: 'x', + text: '' + } as NewQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + title: 'x', + text: 'x'.repeat(maxBodyLen + 1) + } as NewQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { creator: 'does-not-exist', title: 'x', text: 'x' } as NewQuestion, + ErrorMessage.ItemNotFound('does-not-exist', 'user') + ], + [ + { + creator: dummyAppData.users[0].username, + title: 'x', + text: 'x', + hasAcceptedAnswer: true + } as NewQuestion, + ErrorMessage.UnknownField('hasAcceptedAnswer') + ] + ]; + + await Promise.all( + newQuestions.map(([data, message]) => + expect(Backend.createQuestion({ data })).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::updateQuestion', () => { + it('updates an existing question', async () => { + expect.hasAssertions(); + + const patchQuestion: PatchQuestion = { + title: 'Title', + text: 'Text', + upvotes: 50, + downvotes: 50, + status: 'closed', + views: 5 + }; + + await expect( + (await getDb({ name: 'app' })).collection('questions').countDocuments({ + _id: itemToObjectId(dummyAppData.questions[0]), + ...patchQuestion, + 'title-lowercase': patchQuestion.title!.toLowerCase() + }) + ).resolves.toBe(0); + + await expect( + Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: patchQuestion + }) + ).resolves.toBeUndefined(); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne({ + _id: itemToObjectId(dummyAppData.questions[0]) + }) + ).resolves.toMatchObject({ + ...patchQuestion, + 'title-lowercase': patchQuestion.title!.toLowerCase() + }); + }); + + it('supports ViewsUpdateOperation updates alongside normal views count updates', async () => { + expect.hasAssertions(); + + const questionsDb = (await getDb({ name: 'app' })).collection( + 'questions' + ); + + await expect( + questionsDb.countDocuments({ + _id: itemToObjectId(dummyAppData.questions[0]), + views: dummyAppData.questions[0].views + 1 + }) + ).resolves.toBe(0); + + await expect( + questionsDb.countDocuments({ + _id: itemToObjectId(dummyAppData.questions[0]), + views: 0 + }) + ).resolves.toBe(0); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { views: 'increment' } + }); + + await expect( + questionsDb.findOne({ + _id: itemToObjectId(dummyAppData.questions[0]) + }) + ).resolves.toMatchObject({ views: dummyAppData.questions[0].views + 1 }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { views: 0 } + }); + + await expect( + questionsDb.findOne({ + _id: itemToObjectId(dummyAppData.questions[0]) + }) + ).resolves.toMatchObject({ views: 0 }); + }); + + it('updates sorter uvc/uvac counters when updating views or upvotes counts', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac }, + views, + upvotes + } = dummyAppData.questions[0]; + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { views: 'increment' } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc + 1, uvac: uvac + 1 }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { views: 0 } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc - views, uvac: uvac - views }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { views: 10 } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc - views + 10, uvac: uvac - views + 10 }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { upvotes: upvotes + 1 } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc - views + 11, uvac: uvac - views + 11 }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { upvotes: 0 } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ + uvc: uvc - views + 11 - (upvotes + 1), + uvac: uvac - views + 11 - (upvotes + 1) + }); + + await Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: { upvotes: 5 } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ + uvc: uvc - views + 15 - upvotes, + uvac: uvac - views + 15 - upvotes + }); + }); + + it('does not reject if no data passed in', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: {} + }) + ).resolves.toBeUndefined(); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const patchQuestion: PatchQuestion = { + title: 'Title', + text: 'Text' + }; + + await expect( + Backend.updateQuestion({ question_id: 'does-not-exist', data: patchQuestion }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('does-not-exist') + }); + + await expect( + Backend.updateQuestion({ question_id: undefined, data: patchQuestion }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const patchQuestion: PatchQuestion = { + title: 'Title', + text: 'Text' + }; + + const question_id = new ObjectId().toString(); + await expect( + Backend.updateQuestion({ question_id, data: patchQuestion }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { + MAX_QUESTION_TITLE_LENGTH: maxTitleLen, + MAX_QUESTION_BODY_LENGTH_BYTES: maxBodyLen + } = getEnv(); + + const patchQuestions: [PatchQuestion, string][] = [ + [undefined as unknown as PatchQuestion, ErrorMessage.InvalidJSON()], + ['string data' as unknown as PatchQuestion, ErrorMessage.InvalidJSON()], + [ + { creator: 'does-not-exist' } as PatchQuestion, + ErrorMessage.UnknownField('creator') + ], + [ + { title: null } as unknown as PatchQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { title: '' } as PatchQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { title: 'x'.repeat(maxTitleLen + 1) } as PatchQuestion, + ErrorMessage.InvalidStringLength('title', 1, maxTitleLen, 'string') + ], + [ + { text: 5 } as unknown as PatchQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { text: '' } as PatchQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { text: 'x'.repeat(maxBodyLen + 1) } as PatchQuestion, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { upvotes: null } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { upvotes: -1 } as PatchQuestion, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { upvotes: '5' } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { downvotes: null } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ], + [ + { downvotes: -1 } as PatchQuestion, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ], + [ + { downvotes: '5' } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ], + [ + { views: null } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('views', 0, null, 'integer') + ], + [ + { views: -1 } as PatchQuestion, + ErrorMessage.InvalidNumberValue('views', 0, null, 'integer') + ], + [ + { views: '5' } as unknown as PatchQuestion, + ErrorMessage.InvalidNumberValue('views', 0, null, 'integer') + ], + [ + { status: null } as unknown as PatchQuestion, + ErrorMessage.InvalidFieldValue('status', undefined, questionStatuses) + ], + [ + { status: -1 } as unknown as PatchQuestion, + ErrorMessage.InvalidFieldValue('status', undefined, questionStatuses) + ], + [ + { status: '5' } as unknown as PatchQuestion, + ErrorMessage.InvalidFieldValue('status', undefined, questionStatuses) + ], + [ + { status: '' } as unknown as PatchQuestion, + ErrorMessage.InvalidFieldValue('status', undefined, questionStatuses) + ] + ]; + + await Promise.all( + patchQuestions.map(([data, message]) => + expect( + Backend.updateQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]), + data + }) + ).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::deleteQuestion', () => { + it('deletes the specified question', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .countDocuments({ _id: itemToObjectId(dummyAppData.questions[0]) }) + ).resolves.toBe(1); + + await expect( + Backend.deleteQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]) + }) + ).resolves.toBeUndefined(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .countDocuments({ _id: itemToObjectId(dummyAppData.questions[0]) }) + ).resolves.toBe(0); + }); + + it("updates user's questions array when they delete a question", async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[0].username }, + { projection: { _id: false, questionIds: true } } + ) + ).resolves.toStrictEqual({ + questionIds: expect.arrayContaining([itemToObjectId(dummyAppData.questions[0])]) + }); + + await Backend.deleteQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]) + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[0].username }, + { projection: { _id: false, questionIds: true } } + ) + ).resolves.toStrictEqual({ + questionIds: expect.not.arrayContaining([ + itemToObjectId(dummyAppData.questions[0]) + ]) + }); + }); + + it("updates user's answers array when they delete a question (and hence its answers)", async () => { + expect.hasAssertions(); + + await expect( + ( + await getDb({ name: 'app' }) + ) + .collection('users') + .find( + { username: { $in: ['User1', 'User2', 'User3'] } }, + { projection: { _id: false, username: true, answerIds: true } } + ) + .toArray() + ).resolves.toStrictEqual([ + { username: 'User1', answerIds: dummyAppData.users[0].answerIds }, + { username: 'User2', answerIds: dummyAppData.users[1].answerIds }, + { username: 'User3', answerIds: dummyAppData.users[2].answerIds } + ]); + + await Backend.deleteQuestion({ + question_id: itemToStringId(dummyAppData.questions[0]) + }); + + await expect( + ( + await getDb({ name: 'app' }) + ) + .collection('users') + .find( + { username: { $in: ['User1', 'User2', 'User3'] } }, + { projection: { _id: false, username: true, answerIds: true } } + ) + .toArray() + ).resolves.toStrictEqual([ + { + username: 'User1', + answerIds: dummyAppData.users[0].answerIds.filter( + ([qid]) => !qid.equals(itemToObjectId(dummyAppData.questions[0])) + ) + }, + { + username: 'User2', + answerIds: dummyAppData.users[1].answerIds.filter( + ([qid]) => !qid.equals(itemToObjectId(dummyAppData.questions[0])) + ) + }, + { + username: 'User3', + answerIds: dummyAppData.users[2].answerIds.filter( + ([qid]) => !qid.equals(itemToObjectId(dummyAppData.questions[0])) + ) + } + ]); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + + await expect(Backend.deleteQuestion({ question_id })).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.deleteQuestion({ question_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect(Backend.deleteQuestion({ question_id })).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); +}); + +describe('::getAnswers', () => { + it("returns all of the specified question's answers in order (oldest first)", async () => { + expect.hasAssertions(); + + await expect( + Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicAnswer( + dummyAppData.questions[0].answerItems[0], + dummyAppData.questions[0]._id + ), + toPublicAnswer( + dummyAppData.questions[0].answerItems[1], + dummyAppData.questions[0]._id + ), + toPublicAnswer( + dummyAppData.questions[0].answerItems[2], + dummyAppData.questions[0]._id + ) + ]); + }); + + it('does not crash on questions with no answers', async () => { + expect.hasAssertions(); + + await expect( + Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[2]), + after_id: undefined + }) + ).resolves.toStrictEqual([]); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: undefined + }), + await Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }), + await Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: itemToStringId(dummyAppData.questions[0].answerItems[1]) + }), + await Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: itemToStringId(dummyAppData.questions[0].answerItems[2]) + }) + ]).toStrictEqual([ + [ + toPublicAnswer( + dummyAppData.questions[0].answerItems[0], + dummyAppData.questions[0]._id + ) + ], + [ + toPublicAnswer( + dummyAppData.questions[0].answerItems[1], + dummyAppData.questions[0]._id + ) + ], + [ + toPublicAnswer( + dummyAppData.questions[0].answerItems[2], + dummyAppData.questions[0]._id + ) + ], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.getAnswers({ + question_id: itemToStringId(dummyAppData.questions[0]), + after_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'answer_id') + }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + await expect( + Backend.getAnswers({ question_id: 'does-not-exist', after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('does-not-exist') + }); + + await expect( + Backend.getAnswers({ question_id: undefined, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + await expect( + Backend.getAnswers({ question_id, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); +}); + +describe('::createAnswer', () => { + it('creates and returns a new answer to a question', async () => { + expect.hasAssertions(); + + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + const question_id = itemToStringId(dummyAppData.questions[2]); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection: { _id: false, size: { $size: '$answerItems' } } } + ) + ).resolves.toStrictEqual({ size: 0 }); + + await expect( + Backend.createAnswer({ + question_id, + data: newAnswer + }) + ).resolves.toStrictEqual({ + answer_id: expect.any(String), + question_id, + creator: dummyAppData.users[0].username, + createdAt: Date.now(), + text: 'Text!', + accepted: false, + comments: 0, + upvotes: 0, + downvotes: 0 + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection: { _id: false, size: { $size: '$answerItems' } } } + ) + ).resolves.toStrictEqual({ size: 1 }); + }); + + it('updates answer count when creating new answer to question', async () => { + expect.hasAssertions(); + + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Comment.' + }; + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, answers: true } } + ) + ).resolves.toStrictEqual({ answers: 1 }); + + await Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[1]), + data: newAnswer + }); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { _id: false, answers: true } + } + ) + ).resolves.toStrictEqual({ answers: 2 }); + }); + + it("updates user's answers array when they create a new answer", async () => { + expect.hasAssertions(); + + const newAnswer = await Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[1]), + data: { + creator: dummyAppData.users[0].username, + text: 'Text' + } + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[0].username }, + { projection: { _id: false, answerIds: true } } + ) + ).resolves.toStrictEqual({ + answerIds: expect.arrayContaining([ + ...dummyAppData.users[0].answerIds, + [ + itemToObjectId(dummyAppData.questions[1]), + itemToObjectId(newAnswer.answer_id) + ] + ]) + }); + }); + + it('updates sorter uvac (and NOT uvc) counters when creating a new answer', async () => { + expect.hasAssertions(); + + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac } + } = dummyAppData.questions[1]; + + await Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[1]), + data: newAnswer + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc, uvac: uvac + 1 }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createAnswer({ question_id, data: newAnswer }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.createAnswer({ question_id: undefined, data: newAnswer }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createAnswer({ question_id, data: newAnswer }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if the creator is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[2]), + data: { + creator: 'does-not-exist', + text: 'Title' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[2]), + data: { + creator: undefined as any, + text: 'Title' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('creator') + }); + }); + + it('rejects if the creator has already answered the question', async () => { + expect.hasAssertions(); + + const newAnswer: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + data: newAnswer + }) + ).rejects.toMatchObject({ message: ErrorMessage.UserAlreadyAnswered() }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { MAX_ANSWER_BODY_LENGTH_BYTES: maxBodyLen } = getEnv(); + + const newAnswers: [NewAnswer, string][] = [ + [undefined as unknown as NewAnswer, ErrorMessage.InvalidJSON()], + ['string data' as unknown as NewAnswer, ErrorMessage.InvalidJSON()], + [ + {} as NewAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + text: '' + } as NewAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + text: 'x'.repeat(maxBodyLen + 1) + } as NewAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { creator: 'does-not-exist', text: 'x' } as NewAnswer, + ErrorMessage.ItemNotFound('does-not-exist', 'user') + ], + [ + { + creator: dummyAppData.users[0].username, + text: 'x', + accepted: true + } as NewAnswer, + ErrorMessage.UnknownField('accepted') + ] + ]; + + await Promise.all( + newAnswers.map(([data, message]) => + expect( + Backend.createAnswer({ + question_id: itemToStringId(dummyAppData.questions[2]), + data + }) + ).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::updateAnswer', () => { + it('updates an existing answer to a question', async () => { + expect.hasAssertions(); + + const patchAnswer: PatchAnswer = { + text: 'Text!', + upvotes: 50, + downvotes: 50, + accepted: true + }; + + await expect( + selectAnswerFromDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + projection: { _id: false } + }) + ).resolves.not.toMatchObject(patchAnswer); + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: patchAnswer + }) + ).resolves.toBeUndefined(); + + await expect( + selectAnswerFromDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + projection: { _id: false } + }) + ).resolves.toMatchObject(patchAnswer); + }); + + it('does not reject if no data passed in', async () => { + expect.hasAssertions(); + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: {} + }) + ).resolves.toBeUndefined(); + }); + + it("setting an answer as accepted updates the parent question's hasAcceptedAnswer to true", async () => { + expect.hasAssertions(); + + const patchAnswer: PatchAnswer = { accepted: true }; + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, hasAcceptedAnswer: true } } + ) + ).resolves.toStrictEqual({ hasAcceptedAnswer: false }); + + await Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: patchAnswer + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, hasAcceptedAnswer: true } } + ) + ).resolves.toStrictEqual({ hasAcceptedAnswer: true }); + }); + + it("rejects attempts to set accepted if the parent question's hasAcceptedAnswer is true", async () => { + expect.hasAssertions(); + + const patchAnswer: PatchAnswer = { accepted: true }; + + await (await getDb({ name: 'app' })) + .collection('questions') + .updateOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { $set: { hasAcceptedAnswer: true } } + ); + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.QuestionAlreadyAcceptedAnswer() + }); + }); + + it('rejects attempts to set accepted to false', async () => { + expect.hasAssertions(); + + const patchAnswer: PatchAnswer = { accepted: false }; + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('accepted', undefined, ['true']) + }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + const patchAnswer: PatchAnswer = { text: 'Text!' }; + + await expect( + Backend.updateAnswer({ + question_id, + answer_id: new ObjectId().toString(), + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.updateAnswer({ + question_id: undefined, + answer_id: new ObjectId().toString(), + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + const patchAnswer: PatchAnswer = { text: 'Text!' }; + + await expect( + Backend.updateAnswer({ + question_id, + answer_id: new ObjectId().toString(), + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + const patchAnswer: PatchAnswer = { text: 'Text!' }; + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('answer_id', 'parameter') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + const patchAnswer: PatchAnswer = { text: 'Text!' }; + + await expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + data: patchAnswer + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { MAX_ANSWER_BODY_LENGTH_BYTES: maxBodyLen } = getEnv(); + + const patchAnswers: [PatchAnswer, string][] = [ + [undefined as unknown as PatchAnswer, ErrorMessage.InvalidJSON()], + ['string data' as unknown as PatchAnswer, ErrorMessage.InvalidJSON()], + [ + { creator: 'does-not-exist' } as PatchAnswer, + ErrorMessage.UnknownField('creator') + ], + [ + { text: '' } as PatchAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { text: 'x'.repeat(maxBodyLen + 1) } as PatchAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { text: null } as unknown as PatchAnswer, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { upvotes: null } as unknown as PatchAnswer, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { accepted: null } as unknown as PatchAnswer, + ErrorMessage.InvalidFieldValue('accepted', undefined, ['true']) + ], + [ + { upvotes: -1 } as PatchAnswer, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { upvotes: '5' } as unknown as PatchAnswer, + ErrorMessage.InvalidNumberValue('upvotes', 0, null, 'integer') + ], + [ + { downvotes: null } as unknown as PatchAnswer, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ], + [ + { downvotes: -1 } as PatchAnswer, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ], + [ + { downvotes: '5' } as unknown as PatchAnswer, + ErrorMessage.InvalidNumberValue('downvotes', 0, null, 'integer') + ] + ]; + + await Promise.all( + patchAnswers.map(([data, message]) => + expect( + Backend.updateAnswer({ + question_id: itemToStringId(dummyAppData.questions[2]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data + }) + ).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::deleteAnswer', () => { + it('deletes the specified answer from a question', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, size: { $size: '$answerItems' } } } + ) + ).resolves.toStrictEqual({ size: 3 }); + + await expect( + Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }) + ).resolves.toBeUndefined(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, size: { $size: '$answerItems' } } } + ) + ).resolves.toStrictEqual({ size: 2 }); + }); + + it('updates answer count when deleting an answer', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, answers: true } } + ) + ).resolves.toStrictEqual({ answers: 3 }); + + await Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { + projection: { _id: false, answers: true } + } + ) + ).resolves.toStrictEqual({ answers: 2 }); + }); + + it("updates user's answers array when they delete an answer", async () => { + expect.hasAssertions(); + + const question_id = itemToStringId(dummyAppData.questions[0]); + const answer_id = itemToStringId(dummyAppData.questions[0].answerItems[0]); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[1].username }, + { projection: { _id: false, answerIds: true } } + ) + ).resolves.toStrictEqual({ + answerIds: expect.arrayContaining([ + [ + itemToObjectId(dummyAppData.questions[0]), + itemToObjectId(dummyAppData.questions[0].answerItems[0]) + ] + ]) + }); + + await Backend.deleteAnswer({ + question_id, + answer_id + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('users') + .findOne( + { username: dummyAppData.users[1].username }, + { projection: { _id: false, answerIds: true } } + ) + ).resolves.toStrictEqual({ + answerIds: expect.not.arrayContaining([ + [ + itemToObjectId(dummyAppData.questions[0]), + itemToObjectId(dummyAppData.questions[0].answerItems[0]) + ] + ]) + }); + }); + + it('updates sorter uvac (and NOT uvc) counters when deleting an answer', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac } + } = dummyAppData.questions[0]; + + await Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc, uvac: uvac - 1 }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + + await expect( + Backend.deleteAnswer({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.deleteAnswer({ + question_id: undefined, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + + await expect( + Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + + await expect( + Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('answer_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect( + Backend.deleteAnswer({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + + await expect( + Backend.deleteAnswer({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); +}); + +describe('::getComments', () => { + it("returns all of the specified question's comments in order (oldest first)", async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicComment(dummyAppData.questions[0].commentItems[0]), + toPublicComment(dummyAppData.questions[0].commentItems[1]) + ]); + }); + + it("returns all of the specified answer's comments in order (oldest first)", async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: undefined + }) + ).resolves.toStrictEqual([ + toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[0]), + toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[1]), + toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[2]) + ]); + }); + + it('does not crash on questions or answers with no comments', async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[2]), + answer_id: undefined, + after_id: undefined + }) + ).resolves.toStrictEqual([]); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: itemToStringId(dummyAppData.questions[1].answerItems[0]), + after_id: undefined + }) + ).resolves.toStrictEqual([]); + }); + + it('supports pagination', async () => { + expect.hasAssertions(); + + await withMockedEnv( + async () => { + expect([ + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id: undefined + }), + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }), + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id: itemToStringId(dummyAppData.questions[0].commentItems[1]) + }) + ]).toStrictEqual([ + [toPublicComment(dummyAppData.questions[0].commentItems[0])], + [toPublicComment(dummyAppData.questions[0].commentItems[1])], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + + await withMockedEnv( + async () => { + expect([ + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: undefined + }), + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[0] + ) + }), + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[1] + ) + }), + await Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[2] + ) + }) + ]).toStrictEqual([ + [toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[0])], + [toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[1])], + [toPublicComment(dummyAppData.questions[0].answerItems[1].commentItems[2])], + [] + ]); + }, + { RESULTS_PER_PAGE: '1' } + ); + }); + + it('rejects if after_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id: 'fake-oid' + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('fake-oid') + }); + }); + + it('rejects if after_id not found', async () => { + expect.hasAssertions(); + + const after_id = new ObjectId().toString(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + after_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'comment_id') + }); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + after_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(after_id, 'comment_id') + }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: 'does-not-exist', + answer_id: undefined, + after_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('does-not-exist') + }); + + await expect( + Backend.getComments({ + question_id: undefined, + answer_id: undefined, + after_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: 'does-not-exist', + after_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId('does-not-exist') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect( + Backend.getComments({ question_id, answer_id: undefined, after_id: undefined }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.getComments({ + question_id, + answer_id: question_id, + after_id: question_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + after_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + + await expect( + Backend.getComments({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + after_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); +}); + +describe('::createComment', () => { + it('creates and returns a new comment to a question', async () => { + expect.hasAssertions(); + + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Comment.' + }; + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, size: { $size: '$commentItems' } } } + ) + ).resolves.toStrictEqual({ size: 1 }); + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + data: newComment + }) + ).resolves.toStrictEqual({ + comment_id: expect.any(String), + creator: dummyAppData.users[0].username, + createdAt: Date.now(), + text: 'Comment.', + upvotes: 0, + downvotes: 0 + }); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, size: { $size: '$commentItems' } } } + ) + ).resolves.toStrictEqual({ size: 2 }); + }); + + it('creates and returns a new comment to an answer', async () => { + expect.hasAssertions(); + + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Comment.' + }; + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { + _id: false, + size: { $size: { $first: '$answerItems.commentItems' } } + } + } + ) + ).resolves.toStrictEqual({ size: 0 }); + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: itemToStringId(dummyAppData.questions[1].answerItems[0]), + data: newComment + }) + ).resolves.toStrictEqual({ + comment_id: expect.any(String), + creator: dummyAppData.users[0].username, + createdAt: Date.now(), + text: 'Comment.', + upvotes: 0, + downvotes: 0 + }); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { + _id: false, + size: { $size: { $first: '$answerItems.commentItems' } } + } + } + ) + ).resolves.toStrictEqual({ size: 1 }); + }); + + it('updates comment count when creating new comment to a question', async () => { + expect.hasAssertions(); + + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Comment.' + }; + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { _id: false, comments: true } + } + ) + ).resolves.toStrictEqual({ comments: 1 }); + + await Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + data: newComment + }); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { _id: false, comments: true } + } + ) + ).resolves.toStrictEqual({ comments: 2 }); + }); + + it('updates sorter uvc/uvac counters when creating a new comment to a question', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac } + } = dummyAppData.questions[1]; + + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Comment.' + }; + + await Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + data: newComment + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc + 1, uvac: uvac + 1 }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createComment({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.createComment({ + question_id: undefined, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createComment({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.createComment({ + question_id, + answer_id: undefined, + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + const newComment: Required = { + creator: dummyAppData.users[0].username, + text: 'Text!' + }; + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + data: newComment + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); + + it('rejects if the creator is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[2]), + answer_id: undefined, + data: { + creator: 'does-not-exist', + text: 'Title' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[2]), + answer_id: undefined, + data: { + creator: undefined as any, + text: 'Title' + } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidFieldValue('creator') + }); + }); + + it('rejects if data is invalid or contains properties that violates limits', async () => { + expect.hasAssertions(); + + const { MAX_COMMENT_LENGTH: maxBodyLen } = getEnv(); + + const newComments: [NewComment, string][] = [ + [undefined as unknown as NewComment, ErrorMessage.InvalidJSON()], + ['string data' as unknown as NewComment, ErrorMessage.InvalidJSON()], + [{} as NewComment, ErrorMessage.InvalidFieldValue('creator')], + [ + { + creator: dummyAppData.users[0].username + } as NewComment, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + text: '' + } as NewComment, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: dummyAppData.users[0].username, + text: 'x'.repeat(maxBodyLen + 1) + } as NewComment, + ErrorMessage.InvalidStringLength('text', 1, maxBodyLen, 'string') + ], + [ + { + creator: 'does-not-exist', + text: 'x' + } as NewComment, + ErrorMessage.ItemNotFound('does-not-exist', 'user') + ], + [ + { + creator: dummyAppData.users[0].username, + text: 'x', + createdAt: Date.now() + } as NewComment, + ErrorMessage.UnknownField('createdAt') + ] + ]; + + await Promise.all( + newComments.map(([data, message]) => + expect( + Backend.createComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + data + }) + ).rejects.toMatchObject({ message }) + ) + ); + }); +}); + +describe('::deleteComment', () => { + it('deletes the specified comment from a question', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, size: { $size: '$commentItems' } } } + ) + ).resolves.toStrictEqual({ size: 1 }); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[1].commentItems[0]) + }) + ).resolves.toBeUndefined(); + + await expect( + (await getDb({ name: 'app' })) + .collection('questions') + .findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, size: { $size: '$commentItems' } } } + ) + ).resolves.toStrictEqual({ size: 0 }); + }); + + it('deletes the specified comment from an answer', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { + projection: { + _id: false, + size: { $size: { $first: '$answerItems.commentItems' } } + } + } + ) + ).resolves.toStrictEqual({ size: 1 }); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).resolves.toBeUndefined(); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { + projection: { + _id: false, + size: { $size: { $first: '$answerItems.commentItems' } } + } + } + ) + ).resolves.toStrictEqual({ size: 0 }); + }); + + it('updates comment count when deleting a comment from a question', async () => { + expect.hasAssertions(); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { _id: false, comments: true } + } + ) + ).resolves.toStrictEqual({ comments: 1 }); + + await Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[1].commentItems[0]) + }); + + await expect( + (await getDb({ name: 'app' })).collection('questions').findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { + projection: { _id: false, comments: true } + } + ) + ).resolves.toStrictEqual({ comments: 0 }); + }); + + it('updates sorter uvc/uvac counters when deleting a comment from a question', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac } + } = dummyAppData.questions[1]; + + await Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[1].commentItems[0]) + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[1]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc - 1, uvac: uvac - 1 }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + + await expect( + Backend.deleteComment({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.deleteComment({ + question_id: undefined, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + }); + + it('rejects if comment_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const comment_id = 'does-not-exist'; + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(comment_id) + }); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('comment_id', 'parameter') + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect( + Backend.deleteComment({ + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); + + it('rejects if comment_id not found', async () => { + expect.hasAssertions(); + + const comment_id = new ObjectId().toString(); + + await expect( + Backend.deleteComment({ + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(comment_id, 'comment') + }); + }); +}); + +describe('::getHowUserVoted', () => { + it('returns how the user voted on a question or null if there is no vote', async () => { + expect.hasAssertions(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined + }) + ).resolves.toBeNull(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined + }) + ).resolves.toBe('upvoted'); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined + }) + ).resolves.toBe('downvoted'); + }); + + it('returns how the user voted on an answer or null if there is no vote', async () => { + expect.hasAssertions(); + + await patchAnswerInDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + updateOps: { + $inc: { downvotes: 1 }, + $push: { downvoterUsernames: dummyAppData.users[2].username } + } + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined + }) + ).resolves.toBeNull(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined + }) + ).resolves.toBe('upvoted'); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined + }) + ).resolves.toBe('downvoted'); + }); + + it('returns how the user voted on a question comment or null if there is no vote', async () => { + expect.hasAssertions(); + + await patchCommentInDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: undefined, + commentId: itemToObjectId(dummyAppData.questions[0].commentItems[0]), + updateOps: { + $inc: { upvotes: 1 }, + $push: { upvoterUsernames: dummyAppData.users[1].username } + } + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }) + ).resolves.toBeNull(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }) + ).resolves.toBe('upvoted'); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[0]) + }) + ).resolves.toBe('downvoted'); + }); + + it('returns how the user voted on an answer comment or null if there is no vote', async () => { + expect.hasAssertions(); + + await patchCommentInDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + commentId: itemToObjectId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + updateOps: { + $inc: { upvotes: 1, downvotes: 1 }, + $push: { + upvoterUsernames: dummyAppData.users[1].username, + downvoterUsernames: dummyAppData.users[2].username + } + } + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).resolves.toBeNull(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).resolves.toBe('upvoted'); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).resolves.toBe('downvoted'); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: new ObjectId().toString(), + comment_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: undefined, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + }); + + it('rejects if comment_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const comment_id = 'does-not-exist'; + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(comment_id) + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: undefined + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString() + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); + + it('rejects if comment_id not found', async () => { + expect.hasAssertions(); + + const comment_id = new ObjectId().toString(); + + await expect( + Backend.getHowUserVoted({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(comment_id, 'comment') + }); + }); + + it('rejects if username is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.getHowUserVoted({ + username: 'does-not-exist', + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.getHowUserVoted({ + username: undefined, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); +}); + +describe('::applyVotesUpdateOperation', () => { + it('applies increment/decrement operation to question and updates usernames', async () => { + expect.hasAssertions(); + + const questionsDb = (await getDb({ name: 'app' })).collection('questions'); + + const { upvotes, downvotes, upvoterUsernames, downvoterUsernames } = + dummyAppData.questions[2]; + + const username = dummyAppData.users[0].username; + const question_id = itemToStringId(dummyAppData.questions[2]); + const answer_id = undefined; + const comment_id = undefined; + + const projection = { + _id: false, + upvotes: true, + downvotes: true, + upvoterUsernames: true, + downvoterUsernames: true + }; + + await expect( + questionsDb.findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection } + ) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id, + answer_id, + comment_id, + operation: { op: 'increment', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + questionsDb.findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection } + ) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes, + upvoterUsernames: [...upvoterUsernames, dummyAppData.users[0].username], + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id, + answer_id, + comment_id, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + questionsDb.findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection } + ) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id, + answer_id, + comment_id, + operation: { op: 'increment', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + questionsDb.findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection } + ) + ).resolves.toStrictEqual({ + upvotes, + downvotes: downvotes + 1, + upvoterUsernames, + downvoterUsernames: [...downvoterUsernames, dummyAppData.users[0].username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id, + answer_id, + comment_id, + operation: { op: 'decrement', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + questionsDb.findOne( + { _id: itemToObjectId(dummyAppData.questions[2]) }, + { projection } + ) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + }); + + it('applies increment/decrement operation to answer and updates usernames', async () => { + expect.hasAssertions(); + + const { upvotes, downvotes, upvoterUsernames, downvoterUsernames } = + dummyAppData.questions[0].answerItems[0]; + + const questionId = itemToObjectId(dummyAppData.questions[0]); + const answerId = itemToObjectId(dummyAppData.questions[0].answerItems[0]); + + const projection = { + _id: false, + upvotes: true, + downvotes: true, + upvoterUsernames: true, + downvoterUsernames: true + }; + + await expect( + selectAnswerFromDb({ questionId: questionId, answerId: answerId, projection }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectAnswerFromDb({ questionId: questionId, answerId: answerId, projection }) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes, + upvoterUsernames: [...upvoterUsernames, dummyAppData.users[3].username], + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: undefined, + operation: { op: 'increment', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectAnswerFromDb({ questionId: questionId, answerId: answerId, projection }) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes: downvotes + 1, + upvoterUsernames: [...upvoterUsernames, dummyAppData.users[3].username], + downvoterUsernames: [...downvoterUsernames, dummyAppData.users[2].username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectAnswerFromDb({ questionId: questionId, answerId: answerId, projection }) + ).resolves.toStrictEqual({ + upvotes: upvotes, + downvotes: downvotes + 1, + upvoterUsernames, + downvoterUsernames: [...downvoterUsernames, dummyAppData.users[2].username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: undefined, + operation: { op: 'decrement', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectAnswerFromDb({ questionId: questionId, answerId: answerId, projection }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + }); + + it('applies increment/decrement operation to question comment and updates usernames', async () => { + expect.hasAssertions(); + + const { upvotes, downvotes, upvoterUsernames, downvoterUsernames } = + dummyAppData.questions[0].commentItems[0]; + + const questionId = itemToObjectId(dummyAppData.questions[0]); + const answerId = undefined; + const commentId = itemToObjectId(dummyAppData.questions[0].commentItems[0]); + + const projection = { + _id: false, + upvotes: true, + downvotes: true, + upvoterUsernames: true, + downvoterUsernames: true + }; + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(questionId), + answer_id: answerId, + comment_id: itemToStringId(commentId), + operation: { op: 'increment', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes, + upvoterUsernames: [...upvoterUsernames, dummyAppData.users[1].username], + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(questionId), + answer_id: answerId, + comment_id: itemToStringId(commentId), + operation: { op: 'increment', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes: downvotes + 1, + upvoterUsernames: [...upvoterUsernames, dummyAppData.users[1].username], + downvoterUsernames: [...downvoterUsernames, dummyAppData.users[3].username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(questionId), + answer_id: answerId, + comment_id: itemToStringId(commentId), + operation: { op: 'decrement', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes: downvotes + 1, + upvoterUsernames, + downvoterUsernames: [...downvoterUsernames, dummyAppData.users[3].username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(questionId), + answer_id: answerId, + comment_id: itemToStringId(commentId), + operation: { op: 'decrement', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + }); + + it('applies increment/decrement operation to answer comment and updates usernames', async () => { + expect.hasAssertions(); + + const { upvotes, downvotes, upvoterUsernames, downvoterUsernames } = + dummyAppData.questions[0].answerItems[0].commentItems[0]; + + const username = dummyAppData.users[3].username; + const questionId = itemToObjectId(dummyAppData.questions[0]); + const answerId = itemToObjectId(dummyAppData.questions[0].answerItems[0]); + const commentId = itemToObjectId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ); + + const projection = { + _id: false, + upvotes: true, + downvotes: true, + upvoterUsernames: true, + downvoterUsernames: true + }; + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: itemToStringId(commentId), + operation: { op: 'increment', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes: upvotes + 1, + downvotes, + upvoterUsernames: [...upvoterUsernames, username], + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: itemToStringId(commentId), + operation: { op: 'decrement', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: itemToStringId(commentId), + operation: { op: 'increment', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes: downvotes + 1, + upvoterUsernames, + downvoterUsernames: [...downvoterUsernames, username] + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username, + question_id: itemToStringId(questionId), + answer_id: itemToStringId(answerId), + comment_id: itemToStringId(commentId), + operation: { op: 'decrement', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + selectCommentFromDb({ + questionId: questionId, + answerId: answerId, + commentId: commentId, + projection + }) + ).resolves.toStrictEqual({ + upvotes, + downvotes, + upvoterUsernames, + downvoterUsernames + }); + }); + + it('updates sorter uvc/uvac counters ONLY when updating question upvotes', async () => { + expect.hasAssertions(); + + const db = (await getDb({ name: 'app' })).collection('questions'); + + const { + sorter: { uvc, uvac } + } = dummyAppData.questions[0]; + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc: uvc + 1, uvac: uvac + 1 }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc, uvac }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'downvotes' } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc, uvac }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'downvotes' } + }); + + await expect( + db.findOne( + { _id: itemToObjectId(dummyAppData.questions[0]) }, + { projection: { _id: false, uvc: '$sorter.uvc', uvac: '$sorter.uvac' } } + ) + ).resolves.toStrictEqual({ uvc, uvac }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }); + + await expect( + selectAnswerFromDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + projection: { _id: false, upvotes: true, sorter: true } + }) + ).resolves.toStrictEqual({ + upvotes: dummyAppData.questions[0].answerItems[0].upvotes + 1 + // * No "sorter"! + }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + operation: { op: 'increment', target: 'upvotes' } + }); + + await expect( + selectCommentFromDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: itemToObjectId(dummyAppData.questions[0].answerItems[0]), + commentId: itemToObjectId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + projection: { _id: false, upvotes: true, sorter: true } + }) + ).resolves.toStrictEqual({ + upvotes: dummyAppData.questions[0].answerItems[0].commentItems[0].upvotes + 1 + // * No "sorter"! + }); + + await Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[3].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[0]), + operation: { op: 'increment', target: 'upvotes' } + }); + + await expect( + selectCommentFromDb({ + questionId: itemToObjectId(dummyAppData.questions[0]), + answerId: undefined, + commentId: itemToObjectId(dummyAppData.questions[0].commentItems[0]), + projection: { _id: false, upvotes: true, sorter: true } + }) + ).resolves.toStrictEqual({ + upvotes: dummyAppData.questions[0].commentItems[0].upvotes + 1 + // * No "sorter"! + }); + }); + + it('does not reject when duplicating an increment after first undoing it', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[1] + ), + operation: { op: 'increment', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[1] + ), + operation: { op: 'decrement', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[1] + ), + operation: { op: 'increment', target: 'downvotes' } + }) + ).resolves.toBeUndefined(); + }); + + it('rejects when duplicating increment operation', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[1] + ), + operation: { op: 'increment', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[1]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[1].commentItems[0]), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.DuplicateIncrementOperation() }); + }); + + it('rejects decrement operations without preceding increment', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.InvalidDecrementOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + operation: { op: 'decrement', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.InvalidDecrementOperation() }); + }); + + it('rejects multi-target decrement operations', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.MultitargetDecrement() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[1].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.MultitargetDecrement() }); + }); + + it('rejects when attempting operations on multiple targets', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[2].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.MultipleIncrementTargets() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined, + operation: { op: 'increment', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.MultipleIncrementTargets() }); + }); + + it('rejects when attempting to vote on an entry created by the voter', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: undefined, + operation: { op: 'decrement', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.IllegalOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[2]), + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.IllegalOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[1]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[1].commentItems[0] + ), + operation: { op: 'increment', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.IllegalOperation() }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: undefined, + comment_id: itemToStringId(dummyAppData.questions[0].commentItems[1]), + operation: { op: 'decrement', target: 'downvotes' } + }) + ).rejects.toMatchObject({ message: ErrorMessage.IllegalOperation() }); + }); + + it('rejects if question_id is not a valid ObjectId', async () => { + expect.hasAssertions(); + + const question_id = 'does-not-exist'; + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString(), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(question_id) + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: undefined, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString(), + operation: { op: 'increment', target: 'downvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('question_id', 'parameter') + }); + }); + + it('rejects if answer_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const answer_id = 'does-not-exist'; + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString(), + operation: { op: 'decrement', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(answer_id) + }); + }); + + it('rejects if comment_id is not a valid ObjectId (undefined is okay)', async () => { + expect.hasAssertions(); + + const comment_id = 'does-not-exist'; + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id, + operation: { op: 'decrement', target: 'downvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidObjectId(comment_id) + }); + }); + + it('rejects if question_id not found', async () => { + expect.hasAssertions(); + + const question_id = new ObjectId().toString(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: new ObjectId().toString(), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: new ObjectId().toString(), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id, + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id, + answer_id: undefined, + comment_id: undefined, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(question_id, 'question') + }); + }); + + it('rejects if answer_id not found', async () => { + expect.hasAssertions(); + + const answer_id = new ObjectId().toString(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id, + comment_id: new ObjectId().toString(), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(answer_id, 'answer') + }); + }); + + it('rejects if comment_id not found', async () => { + expect.hasAssertions(); + + const comment_id = new ObjectId().toString(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id, + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound(comment_id, 'comment') + }); + }); + + it('rejects if username is missing or not found', async () => { + expect.hasAssertions(); + + await expect( + Backend.applyVotesUpdateOperation({ + username: 'does-not-exist', + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + operation: { op: 'increment', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.ItemNotFound('does-not-exist', 'user') + }); + + await expect( + Backend.applyVotesUpdateOperation({ + username: undefined, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ), + operation: { op: 'decrement', target: 'upvotes' } + }) + ).rejects.toMatchObject({ + message: ErrorMessage.InvalidItem('username', 'parameter') + }); + }); + + it('rejects if operation is invalid or missing', async () => { + expect.hasAssertions(); + + const params = { + username: dummyAppData.users[0].username, + question_id: itemToStringId(dummyAppData.questions[0]), + answer_id: itemToStringId(dummyAppData.questions[0].answerItems[0]), + comment_id: itemToStringId( + dummyAppData.questions[0].answerItems[0].commentItems[0] + ) + }; + + type Op = Parameters[0]['operation']; + const badOps: [op: Op, error: string][] = [ + [undefined, ErrorMessage.InvalidItem('operation', 'parameter')], + [ + {}, + ErrorMessage.InvalidFieldValue('operation', undefined, [ + 'increment', + 'decrement' + ]) + ], + [ + { target: 'downvotes' }, + ErrorMessage.InvalidFieldValue('operation', undefined, [ + 'increment', + 'decrement' + ]) + ], + [ + { op: undefined }, + ErrorMessage.InvalidFieldValue('operation', undefined, [ + 'increment', + 'decrement' + ]) + ], + [ + { op: null } as unknown as Op, + ErrorMessage.InvalidFieldValue('operation', undefined, [ + 'increment', + 'decrement' + ]) + ], + [ + { op: 'fake' } as unknown as Op, + ErrorMessage.InvalidFieldValue('operation', 'fake', [ + 'increment', + 'decrement' + ]) + ], + [ + { op: 'increment' } as unknown as Op, + ErrorMessage.InvalidFieldValue('target', undefined, ['upvotes', 'downvotes']) + ], + [ + { op: 'increment', target: undefined } as unknown as Op, + ErrorMessage.InvalidFieldValue('target', undefined, ['upvotes', 'downvotes']) + ], + [ + { op: 'increment', target: null } as unknown as Op, + ErrorMessage.InvalidFieldValue('target', null as any, [ + 'upvotes', + 'downvotes' + ]) + ], + [ + { op: 'increment', target: 'nope' } as unknown as Op, + ErrorMessage.InvalidFieldValue('target', 'nope', ['upvotes', 'downvotes']) + ] + ]; + + await Promise.all( + badOps.map(async ([op, error]) => { + await expect( + Backend.applyVotesUpdateOperation({ + ...params, + operation: op + }) + ).rejects.toMatchObject({ + message: error + }); + }) + ); + }); +}); diff --git a/test/api/unit-sys-auth.test.ts b/test/api/unit-sys-auth.test.ts new file mode 100644 index 0000000..cae69f2 --- /dev/null +++ b/test/api/unit-sys-auth.test.ts @@ -0,0 +1,640 @@ +/* eslint-disable no-global-assign */ +import { testApiHandler } from 'next-test-api-route-handler'; +import AuthEndpoint, { config as AuthConfig } from 'universe/pages/api/sys/auth'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { useMockDateNow } from 'multiverse/jest-mock-date'; +import { getDb } from 'multiverse/mongo-schema'; +import { dummyRootData } from 'multiverse/mongo-common'; +import { randomUUID } from 'node:crypto'; +import { ObjectId } from 'mongodb'; + +import { + BANNED_BEARER_TOKEN, + DEV_BEARER_TOKEN, + DUMMY_BEARER_TOKEN, + PublicAuthEntry, + toPublicAuthEntry +} from 'multiverse/next-auth'; + +import AuthUnbanEndpoint, { + config as AuthUnbanConfig +} from 'universe/pages/api/sys/auth/unban'; + +import type { NextApiHandlerMixin } from 'testverse/fixtures'; +import type { InternalAuthBearerEntry } from 'multiverse/next-auth'; +import type { Collection, Db } from 'mongodb'; +import type { InternalLimitedLogEntry } from 'multiverse/next-limit'; + +setupMemoryServerOverride(); +useMockDateNow(); + +const authHandler = AuthEndpoint as NextApiHandlerMixin; +const authUnbanHandler = AuthUnbanEndpoint as NextApiHandlerMixin; + +authHandler.config = AuthConfig; +authUnbanHandler.config = AuthUnbanConfig; + +// * This suite blurs the line between unit and integration tests for portability +// * reasons. +// TODO: replace with next-fable (formerly / in addition to: @xunnamius/fable) + +describe('middleware correctness tests', () => { + it('endpoints fail on req with bad authentication', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + test: async ({ fetch }) => { + await expect(fetch().then((r) => r.status)).resolves.toBe(401); + } + }); + + await testApiHandler({ + handler: authUnbanHandler, + test: async ({ fetch }) => { + await expect(fetch().then((r) => r.status)).resolves.toBe(401); + } + }); + }); + + it('endpoints fail if authenticated req is not authorized', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${DUMMY_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(403); + } + }); + + await testApiHandler({ + handler: authUnbanHandler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${DUMMY_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(403); + } + }); + }); + + it('endpoints fail if authed req is rate limited', async () => { + expect.hasAssertions(); + + await (await getDb({ name: 'root' })) + .collection('auth') + .updateOne( + { token: { bearer: BANNED_BEARER_TOKEN } }, + { $set: { 'attributes.isGlobalAdmin': true } } + ); + + await testApiHandler({ + handler: authHandler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${BANNED_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(429); + } + }); + + await testApiHandler({ + handler: authUnbanHandler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${BANNED_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(429); + } + }); + }); +}); + +describe('api/sys/auth', () => { + let db: Db; + let authDb: Collection; + let limitDb: Collection; + + const headerOverrides = { + authorization: `bearer ${DEV_BEARER_TOKEN}`, + 'content-type': 'application/json' + }; + + beforeEach(async () => { + db = await getDb({ name: 'root' }); + authDb = db.collection('auth'); + limitDb = db.collection('limited-log'); + }); + + describe('/ [GET]', () => { + it('returns all tokens belonging to the specified chapter', async () => { + expect.hasAssertions(); + + const newEntry: InternalAuthBearerEntry = { + ...dummyRootData.auth[0], + _id: new ObjectId(), + token: { bearer: randomUUID() } + }; + + await authDb.insertOne(newEntry); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'GET', + headers: { 'X-Target-Owners': dummyRootData.auth[0].attributes.owner } + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ + success: true, + entries: expect.arrayContaining([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(newEntry) + ]) + }); + + expect(res.status).toBe(200); + } + }); + }); + + it('returns all tokens belonging to all the specified chapters', async () => { + expect.hasAssertions(); + + const newEntry1: InternalAuthBearerEntry = { + ...dummyRootData.auth[0], + _id: new ObjectId(), + token: { bearer: randomUUID() } + }; + + const newEntry2: InternalAuthBearerEntry = { + ...dummyRootData.auth[1], + _id: new ObjectId(), + token: { bearer: randomUUID() } + }; + + await authDb.insertMany([newEntry1, newEntry2]); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'GET', + headers: [ + ['x-target-owners', dummyRootData.auth[0].attributes.owner], + ['x-target-owners', dummyRootData.auth[1].attributes.owner] + ] + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ + success: true, + entries: expect.arrayContaining([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(dummyRootData.auth[1]), + toPublicAuthEntry(newEntry1), + toPublicAuthEntry(newEntry2) + ]) + }); + + expect(res.status).toBe(200); + } + }); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'GET', + headers: { + 'X-Target-Owners': `${dummyRootData.auth[0].attributes.owner},${dummyRootData.auth[1].attributes.owner}` + } + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ + success: true, + entries: expect.arrayContaining([ + toPublicAuthEntry(dummyRootData.auth[0]), + toPublicAuthEntry(dummyRootData.auth[1]), + toPublicAuthEntry(newEntry1), + toPublicAuthEntry(newEntry2) + ]) + }); + + expect(res.status).toBe(200); + } + }); + }); + + it('returns all tokens in the system if no chapter specified', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ method: 'GET' }); + const json = await res.json(); + + expect(json).toStrictEqual({ + success: true, + entries: expect.arrayContaining(dummyRootData.auth.map(toPublicAuthEntry)) + }); + + expect(res.status).toBe(200); + } + }); + }); + + it('returns HTTP 400 on bad input', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + ( + await fetch({ + method: 'GET', + headers: { 'X-Target-Owners': '' } + }) + ).status + ).toBe(400); + } + }); + }); + }); + + describe('/ [POST]', () => { + it('returns a newly generated token owned by the specified chapter', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'POST', + body: JSON.stringify({ + attributes: { owner: 'new-owner' } + }) + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ + success: true, + entry: { + attributes: { owner: 'new-owner' }, + scheme: 'bearer', + token: { bearer: expect.any(String) } + } as PublicAuthEntry + }); + + expect(res.status).toBe(200); + } + }); + }); + + it('returns HTTP 400 on bad input', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect((await fetch({ method: 'POST' })).status).toBe(400); + } + }); + }); + }); + + describe('/ [PATCH]', () => { + it("updates the specified token's attributes", async () => { + expect.hasAssertions(); + + await expect( + authDb.countDocuments({ + 'attributes.owner': dummyRootData.auth[0].attributes.owner + }) + ).resolves.toBe(1); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'PATCH', + body: JSON.stringify({ + target: dummyRootData.auth[1], + attributes: dummyRootData.auth[0].attributes + }) + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ success: true }); + expect(res.status).toBe(200); + + await expect( + authDb.countDocuments({ + 'attributes.owner': dummyRootData.auth[0].attributes.owner + }) + ).resolves.toBe(2); + } + }); + }); + + it('returns HTTP 400 on bad input', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect((await fetch({ method: 'PATCH' })).status).toBe(400); + } + }); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + ( + await fetch({ + method: 'PATCH', + body: JSON.stringify({ + target: dummyRootData.auth[1] + }) + }) + ).status + ).toBe(400); + } + }); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + ( + await fetch({ + method: 'PATCH', + body: JSON.stringify({ + attributes: dummyRootData.auth[0].attributes + }) + }) + ).status + ).toBe(400); + } + }); + }); + }); + + describe('/ [DELETE]', () => { + it('deletes the specified token', async () => { + expect.hasAssertions(); + + await expect( + authDb.countDocuments({ + scheme: 'bearer', + token: { bearer: dummyRootData.auth[0].token.bearer } + }) + ).resolves.toBe(1); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'DELETE', + body: JSON.stringify({ target: dummyRootData.auth[0] }) + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ success: true }); + expect(res.status).toBe(200); + + await expect( + authDb.countDocuments({ + scheme: 'bearer', + token: { bearer: dummyRootData.auth[0].token.bearer } + }) + ).resolves.toBe(0); + } + }); + }); + + it('returns HTTP 400 on bad input', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect((await fetch({ method: 'DELETE' })).status).toBe(400); + } + }); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + (await fetch({ method: 'DELETE', body: JSON.stringify({}) })).status + ).toBe(400); + } + }); + + await testApiHandler({ + handler: authHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + (await fetch({ method: 'DELETE', body: JSON.stringify({}) })).status + ).toBe(400); + } + }); + }); + }); + + describe('/unban', () => { + describe('/ [GET]', () => { + it('list all banned tokens and ips', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authUnbanHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + await expect( + (await fetch({ method: 'GET' })).json() + ).resolves.toStrictEqual({ + success: true, + entries: dummyRootData['limited-log'] + .slice() + .reverse() + .map((ent) => { + const { _id, ...entry } = ent; + return entry; + }) + }); + } + }); + }); + }); + + describe('/ [DELETE]', () => { + it('unbans a token', async () => { + expect.hasAssertions(); + + await expect( + limitDb.countDocuments({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + until: { $gt: Date.now() } + }) + ).resolves.toBe(1); + + await testApiHandler({ + handler: authUnbanHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'DELETE', + body: JSON.stringify({ + target: { header: `bearer ${BANNED_BEARER_TOKEN}` } + }) + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ success: true, unbannedCount: 1 }); + expect(res.status).toBe(200); + + await expect( + limitDb.countDocuments({ + header: `bearer ${BANNED_BEARER_TOKEN}`, + until: { $gt: Date.now() } + }) + ).resolves.toBe(0); + } + }); + }); + + it('unbans an ip address', async () => { + expect.hasAssertions(); + + await expect( + limitDb.countDocuments({ + ip: '1.2.3.4', + until: { $gt: Date.now() } + }) + ).resolves.toBe(1); + + await testApiHandler({ + handler: authUnbanHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + const res = await fetch({ + method: 'DELETE', + body: JSON.stringify({ target: { ip: '1.2.3.4' } }) + }); + + const json = await res.json(); + + expect(json).toStrictEqual({ success: true, unbannedCount: 1 }); + expect(res.status).toBe(200); + + await expect( + limitDb.countDocuments({ + ip: '1.2.3.4', + until: { $gt: Date.now() } + }) + ).resolves.toBe(0); + } + }); + }); + + it('returns HTTP 400 on bad input', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler: authUnbanHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect((await fetch({ method: 'DELETE' })).status).toBe(400); + } + }); + + await testApiHandler({ + handler: authUnbanHandler, + requestPatcher(req) { + req.headers = { ...req.headers, ...headerOverrides }; + }, + async test({ fetch }) { + expect( + (await fetch({ method: 'DELETE', body: JSON.stringify({}) })).status + ).toBe(400); + } + }); + }); + }); + }); +}); diff --git a/test/api/unit-sys-ping.test.ts b/test/api/unit-sys-ping.test.ts new file mode 100644 index 0000000..752e0d6 --- /dev/null +++ b/test/api/unit-sys-ping.test.ts @@ -0,0 +1,124 @@ +/* eslint-disable no-global-assign */ +import { useMockDateNow } from 'multiverse/jest-mock-date'; +import { getDb } from 'multiverse/mongo-schema'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { BANNED_BEARER_TOKEN, DUMMY_BEARER_TOKEN } from 'multiverse/next-auth'; +import { testApiHandler } from 'next-test-api-route-handler'; + +import Endpoint, { config as Config } from 'universe/pages/api/sys/ping'; + +import type { InternalAuthBearerEntry } from 'multiverse/next-auth'; + +const handler = Endpoint as typeof Endpoint & { config?: typeof Config }; +handler.config = Config; + +setupMemoryServerOverride(); +useMockDateNow(); + +// * This suite blurs the line between unit and integration tests for portability +// * reasons. +// TODO: replace with next-fable (formerly / in addition to: @xunnamius/fable) + +describe('middleware correctness tests', () => { + it('endpoints is not authenticated', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + await expect(fetch().then((r) => r.status)).resolves.toBe(200); + } + }); + }); + + it('endpoints ignores authentication and authorization header', async () => { + expect.hasAssertions(); + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${DUMMY_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(200); + } + }); + }); + + it('endpoint fails if req is rate limited', async () => { + expect.hasAssertions(); + + await (await getDb({ name: 'root' })) + .collection('auth') + .updateOne( + { token: { bearer: BANNED_BEARER_TOKEN } }, + { $set: { 'attributes.isGlobalAdmin': true } } + ); + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + await expect( + fetch({ + headers: { Authorization: `bearer ${BANNED_BEARER_TOKEN}` } + }).then((r) => r.status) + ).resolves.toBe(429); + } + }); + }); +}); + +describe('api/sys/ping', () => { + it('pongs when we ping', async () => { + expect.hasAssertions(); + + const oldDate = Date; + + try { + // @ts-expect-error: overriding Date is tough stuff + Date = class extends Date { + constructor(...args: Parameters) { + super(...args); + } + + toLocaleString(): string; + toLocaleString( + locales?: string | string[], + options?: Intl.DateTimeFormatOptions + ): string; + toLocaleString(locales?: unknown, options?: unknown): string { + void locales, options; + return 'fake date, fake time'; + } + }; + + await testApiHandler({ + handler, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toStrictEqual({ + success: true, + message: 'Hello to Mr. World at fake date, fake time' + }); + } + }); + + await testApiHandler({ + handler, + params: { name: 'Ms. Universe' }, + test: async ({ fetch }) => { + const res = await fetch(); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toStrictEqual({ + success: true, + message: 'Hello to Ms. Universe at fake date, fake time' + }); + } + }); + } finally { + Date = oldDate; + } + }); +}); diff --git a/test/db.ts b/test/db.ts new file mode 100644 index 0000000..a6775b4 --- /dev/null +++ b/test/db.ts @@ -0,0 +1,427 @@ +import { ObjectId } from 'mongodb'; +import { getCommonDummyData, mockDateNowMs } from 'multiverse/mongo-common'; + +import type { DummyData } from 'multiverse/mongo-test'; + +import type { + InternalUser, + InternalMail, + InternalQuestion +} from 'universe/backend/db'; + +/** + * Returns data used to hydrate databases and their collections. + */ +export function getDummyData(): DummyData { + return getCommonDummyData({ app: dummyAppData }); +} + +/** + * The shape of the application database's test data. + */ +export type DummyAppData = { + _generatedAt: number; + users: InternalUser[]; + mail: InternalMail[]; + questions: InternalQuestion[]; +}; + +// ! Order matters in unit and integration tests, so APPEND ONLY +const users: InternalUser[] = [ + // ? Dummy users' passwords are the same as their usernames + { + _id: new ObjectId(), + username: 'User1', + salt: '91db41c494502f9ebb6217e4590cccc2', + key: '17660270f4c4c1741ab9d43e6fb800bc784f0a3bc2f4cd31f0e26bf821ef2ae788f83af134d8c3824f5e0552f8cd432d6b23963d2ffbceb6a7c91b0f59533206', + email: 'user1@fake-email.com', + points: 1, + questionIds: [], + answerIds: [] + }, + { + _id: new ObjectId(), + username: 'User2', + salt: 'bfe69b665a1ae64bb7d76c32347adecb', + key: 'e71e8bbd23df52bec8af8280ad7901ddd0ecd5cc43371915f7a95cd17ce0a8515127bfcd433435425c4d245f4a18efcb08e4484682aeb53fcfce5b536d79e4e4', + email: 'user2@fake-email.com', + points: 1000, + questionIds: [], + answerIds: [] + }, + { + _id: new ObjectId(), + username: 'User3', + salt: '12ef85b518da764294abf0a2095bb5ec', + key: 'e745893e064e26d4349b1639b1596c14bc9b5d050b56bf31ff3ef0dfce6f959aef8a3722a35bc35b2d142169e75ca3e1967cd6ee4818af0813d8396a724fdd22', + email: 'user3@fake-email.com', + points: 100000, + questionIds: [], + answerIds: [] + }, + { + _id: new ObjectId(), + username: 'User4', + salt: '12ef85b518da764294abf0a2095bb5ec', + key: 'e745893e064e26d4349b1639b1596c14bc9b5d050b56bf31ff3ef0dfce6f959aef8a3722a35bc35b2d142169e75ca3e1967cd6ee4818af0813d8396a724fdd22', + email: 'user4@fake-email.com', + points: 100, + questionIds: [], + answerIds: [] + } +]; + +const mail: InternalMail[] = [ + { + _id: new ObjectId(), + createdAt: mockDateNowMs - 10000, + receiver: 'User1', + sender: 'User2', + subject: "You've got mail!", + text: 'This is a message for **User1**.\n\nBest,\nUser2' + }, + { + _id: new ObjectId(), + createdAt: mockDateNowMs - 1000, + receiver: 'User1', + sender: 'User1', + subject: 'Self-mail', + text: 'Me? Yes. Hello. Hi.' + }, + { + _id: new ObjectId(), + createdAt: mockDateNowMs - 100000, + receiver: 'User2', + sender: 'User3', + subject: 'Use snail mail lately?', + text: 'I think I will continue our correspondence over physical mail now.' + } +]; + +const questions: InternalQuestion[] = [ + { + _id: new ObjectId(), + creator: 'User1', + title: 'What is the best server-side language in 2022?', + 'title-lowercase': 'what is the best server-side language in 2022?', + createdAt: mockDateNowMs - 123456, + text: 'There are a lot of popular server-side languages these days. Which one is the best do you think?', + status: 'open', + hasAcceptedAnswer: false, + upvotes: 25, + upvoterUsernames: [users[1].username], + downvotes: 6, + downvoterUsernames: [users[2].username], + views: 1024, + answers: 3, + answerItems: [ + { + _id: new ObjectId(), + creator: 'User2', + createdAt: mockDateNowMs - 109584, + text: 'It has got to be Java.', + accepted: false, + upvotes: 4, + upvoterUsernames: [users[0].username], + downvotes: 1, + downvoterUsernames: [], + commentItems: [ + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 108584, + text: "It's been around forever though!", + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [] + } + ] + }, + { + _id: new ObjectId(), + creator: 'User3', + createdAt: mockDateNowMs - 107584, + text: '**Fullstack JavaScript** is da way.', + accepted: false, + upvotes: 14, + upvoterUsernames: [users[0].username], + downvotes: 0, + downvoterUsernames: [], + commentItems: [ + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 106584, + text: 'This was going to be my answer', + upvotes: 1, + upvoterUsernames: [users[1].username], + downvotes: 0, + downvoterUsernames: [] + }, + { + _id: new ObjectId(), + creator: 'User2', + createdAt: mockDateNowMs - 105584, + text: 'JavaScript is just Java + carpet tho', + upvotes: 0, + upvoterUsernames: [], + downvotes: 1, + downvoterUsernames: [users[2].username] + }, + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 104584, + text: "@User2 You mean they're not related?", + upvotes: 0, + upvoterUsernames: [users[0].username], + downvotes: 0, + downvoterUsernames: [] + } + ] + }, + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 19999, + text: 'Just to provide a potential answer to my own question here:\n\nRust, maybe? Or perhaps I should give Python or Ruby a shot. C# is pretty nice too.', + accepted: false, + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + commentItems: [] + } + ], + comments: 2, + commentItems: [ + { + _id: new ObjectId(), + creator: 'User3', + createdAt: mockDateNowMs - 103584, + text: 'Hmm. I might abuse my points powers and close this question as off topic.', + upvotes: 0, + upvoterUsernames: [], + downvotes: 43, + downvoterUsernames: [users[0].username] + }, + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 102584, + text: 'Please do not close my question :(', + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [] + } + ], + sorter: { + uvc: 1051, + uvac: 1054 + } + }, + { + _id: new ObjectId(), + creator: 'User1', + title: 'How do you (as a team) register for the conference?', + 'title-lowercase': 'how do you (as a team) register for the conference?', + createdAt: mockDateNowMs - 98765, + text: 'Hello. I am trying to register for the conference but I am not sure where exactly to go (on the web) or who to talk to. So: how do you register for the conference?', + status: 'protected', + hasAcceptedAnswer: true, + upvotes: 2504, + upvoterUsernames: [users[1].username, users[2].username], + downvotes: 11, + downvoterUsernames: [], + views: 8192, + answers: 1, + answerItems: [ + { + _id: new ObjectId(), + creator: 'User2', + createdAt: mockDateNowMs - 96765, + text: 'Follow [this link](https://bdpa.org/event/bdpacon2022) to the BDPA conference website and you should see all the information you need to register. Is there anything else you were looking for specifically?', + accepted: true, + upvotes: 0, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + commentItems: [] + } + ], + comments: 1, + commentItems: [ + { + _id: new ObjectId(), + creator: 'User2', + createdAt: mockDateNowMs - 97765, + text: "Make sure to pay attention at the next coordinator's meeting!", + upvotes: 2, + upvoterUsernames: [users[2].username], + downvotes: 0, + downvoterUsernames: [] + } + ], + sorter: { + uvc: 10697, + uvac: 10698 + } + }, + { + _id: new ObjectId(), + creator: 'User2', + title: 'Where is the NHSCC GitHub page?', + 'title-lowercase': 'where is the nhscc github page?', + createdAt: mockDateNowMs - 5000, + text: `As the title says, I'm looking for the BDPA NHSCC GitHub page, but I can't seem to find it. Any help would be appreciated.\n\nAlso: ![XSS attack!]("onerror="alert('your app has been hacked'))`, + status: 'open', + hasAcceptedAnswer: false, + upvotes: 0, // ! For testing purposes, at least 3 questions need 0 upvotes + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + views: 1, + answers: 0, + answerItems: [], + comments: 0, + commentItems: [], + sorter: { + uvc: 1, + uvac: 1 + } + }, + { + _id: new ObjectId(), + creator: 'User3', + title: 'Am I in the future?', + 'title-lowercase': 'am I in the future?', + createdAt: mockDateNowMs + 60000, + text: `For some reason I've created this question a few seconds from now. How is that possible? **HELP ME!**`, + status: 'closed', + hasAcceptedAnswer: false, + upvotes: 0, // ! For testing purposes, at least 3 questions need 0 upvotes + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + views: 12, + answers: 0, + answerItems: [], + comments: 0, + commentItems: [], + sorter: { + uvc: 12, + uvac: 12 + } + }, + { + _id: new ObjectId(), + creator: 'User3', + title: 'Am I still in the future?', + 'title-lowercase': 'am I still in the future?', + createdAt: mockDateNowMs + 1234567, + text: `### HELP ME I CAN'T GET BACK!`, + status: 'open', + hasAcceptedAnswer: true, + upvotes: 2, + upvoterUsernames: [users[0].username, users[1].username], + downvotes: 1, + downvoterUsernames: [], + views: 8, + answers: 2, + answerItems: [ + { + _id: new ObjectId(), + creator: 'User2', + createdAt: mockDateNowMs - 1000, + text: 'You need to go back to the future, bro.', + accepted: true, + upvotes: 1, + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + commentItems: [] + }, + { + _id: new ObjectId(), + creator: 'User1', + createdAt: mockDateNowMs - 900, + text: 'When are you?!', + accepted: true, + upvotes: 0, + upvoterUsernames: [], + downvotes: 1, + downvoterUsernames: [], + commentItems: [ + { + _id: new ObjectId(), + creator: 'User3', + createdAt: mockDateNowMs, + text: "I'm not sure... HALP!", + upvotes: 0, + upvoterUsernames: [], + downvotes: 1, + downvoterUsernames: [] + } + ] + } + ], + comments: 0, + commentItems: [], + sorter: { + uvc: 11, + uvac: 13 + } + }, + { + _id: new ObjectId(), + creator: 'User2', + title: 'How to export functions from a custom hook', + 'title-lowercase': 'how to export functions from a custom hook', + createdAt: mockDateNowMs + 65432, + text: 'When running `yarn build`, I am faced with this error:\n\n`Objects are not valid as a React child (found: object with keys {func1, func2, func3}). If you meant to render a collection of children, use an array instead.`\n\nMy attempt at solving this was to export my custom hook functions in an array, as the error seemed to suggest.', + status: 'open', + hasAcceptedAnswer: false, + upvotes: 0, // ! For testing purposes, at least 3 questions need 0 upvotes + upvoterUsernames: [], + downvotes: 0, + downvoterUsernames: [], + views: 24, + answers: 0, + answerItems: [], + comments: 0, + commentItems: [], + sorter: { + uvc: 24, + uvac: 24 + } + } +]; + +users[0].questionIds.push(questions[0]._id, questions[1]._id); +users[1].questionIds.push(questions[2]._id, questions[5]._id); +users[2].questionIds.push(questions[3]._id, questions[4]._id); + +users[0].answerIds.push( + [questions[0]._id, questions[0].answerItems[2]._id], + [questions[4]._id, questions[4].answerItems[1]._id] +); + +users[1].answerIds.push( + [questions[0]._id, questions[0].answerItems[0]._id], + [questions[1]._id, questions[1].answerItems[0]._id], + [questions[4]._id, questions[4].answerItems[0]._id] +); + +users[2].answerIds.push([questions[0]._id, questions[0].answerItems[1]._id]); + +/** + * Test data for the application database. + */ +export const dummyAppData: DummyAppData = { + _generatedAt: mockDateNowMs, + users, + mail, + questions +}; diff --git a/test/externals/integration-initialize.test.ts b/test/externals/integration-initialize.test.ts new file mode 100644 index 0000000..ecea4e4 --- /dev/null +++ b/test/externals/integration-initialize.test.ts @@ -0,0 +1,223 @@ +import { ResponseTransformer, rest } from 'msw'; +import { setupServer } from 'msw/node'; + +import { debugNamespace as namespace } from 'universe/constants'; + +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; + +import { + getDummyQuestions, + getDummyQuestionAnswers, + getDummyAnswers, + getDummyAnswerComments, + getDummyComments, + getDummyQuestionComments +} from 'externals/initialize-data/api-test'; + +import { mockEnvFactory, protectedImportFactory } from 'testverse/setup'; + +import type { RestContext } from 'msw'; +import type { + SecondsFromNow, + StackExchangeApiResponse +} from 'types/stackexchange-api'; + +void namespace; + +// ? Ensure the isolated external picks up the memory server override +jest.mock('multiverse/mongo-schema', () => { + return jest.requireActual('multiverse/mongo-schema'); +}); + +const totalGeneratedQuestions = 100; +const intervalPeriodMs = 1000; + +const withMockedEnv = mockEnvFactory({ + // ! For max test perf, ensure this next line is commented out unless needed + //DEBUG: `throttled-fetch:*,${namespace}:initialize-data,${namespace}:initialize-data:*`, + //DEBUG: `${namespace}:initialize-data,${namespace}:initialize-data:*`, + + // ? Use these to control the options auto-selected for inquirer. Note that + // ? these values must either be empty/undefined or a valid URL query string. + TEST_PROMPTER_INITIALIZER: 'action=hit-ignore', + TEST_PROMPTER_ERRORHANDLER: 'action=exit', + TEST_PROMPTER_FINALIZER: 'action=commit', + + NODE_ENV: 'test', + MONGODB_URI: 'fake', + MAX_ANSWER_BODY_LENGTH_BYTES: '100', + MAX_COMMENT_LENGTH: '100', + MAX_QUESTION_BODY_LENGTH_BYTES: '100', + STACKAPPS_INTERVAL_PERIOD_MS: intervalPeriodMs.toString(), + STACKAPPS_MAX_REQUESTS_PER_INTERVAL: '25', + STACKAPPS_TOTAL_API_GENERATED_QUESTIONS: totalGeneratedQuestions.toString(), + STACKAPPS_COLLECTALL_QUESTION_ANSWERS: '4', + STACKAPPS_COLLECTALL_QUESTION_COMMENTS: '3', + STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS: '2', + STACKAPPS_MAX_PAGE_SIZE: '2', // * Should be <= half of the above constants + STACKAPPS_AUTH_KEY: 'special-stack-exchange-key' +}); + +const importInitializeData = protectedImportFactory< + typeof import('externals/initialize-data').default +>({ + path: 'externals/initialize-data', + useDefault: true +}); + +let counter = 0; +const mockedResponseJson: Partial> = {}; + +const calcBackoffModulo = Math.ceil(5.49 * totalGeneratedQuestions); +const calcError500Modulo = + totalGeneratedQuestions + Math.max(2, Math.ceil(0.1 * totalGeneratedQuestions)); +const calcError502Modulo = calcError500Modulo + 1; +const calcError503Modulo = calcError500Modulo * 2; +const calcError429Modulo = calcError500Modulo * 2 + 1; + +const maybeErrorResponse = ( + ctx: RestContext, + { okTransformers }: { okTransformers: ResponseTransformer[] } +) => { + delete mockedResponseJson.backoff; + + if (counter) { + if (counter % calcBackoffModulo == 0) { + mockedResponseJson.backoff = Math.max( + 0.1, + intervalPeriodMs / 200 + ) as SecondsFromNow; + } + + const results = + counter % calcError500Modulo == 0 + ? [ + ctx.status(500), + ctx.json({ + error_id: 123, + error_message: 'fake 500 error', + error_name: 'fake_500' + }) + ] + : counter % calcError503Modulo == 0 + ? [ + ctx.status(503), + ctx.json({ + error_id: 123, + error_message: 'fake 503 error', + error_name: 'fake_503' + }) + ] + : counter % calcError502Modulo == 0 + ? [ + ctx.status(502), + ctx.json({ + error_id: 123, + error_message: 'fake 502 error', + error_name: 'fake_502' + }) + ] + : counter % calcError429Modulo == 0 + ? [ctx.status(429)] + : okTransformers; + + counter++; + return results; + } else { + counter++; + return okTransformers; + } +}; + +const server = setupServer( + rest.get('*/questions/:question_id/answers', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestionAnswers(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/questions/:question_id/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestionComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/answers/:answer_id/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyAnswerComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/questions', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestions(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/answers', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyAnswers(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }) +); + +setupMemoryServerOverride(); + +beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +it('works', async () => { + expect.hasAssertions(); + await withMockedEnv(() => importInitializeData({ expectedExitCode: 0 })); + // TODO: more testing +}); diff --git a/test/externals/unit-ban.test.ts b/test/externals/unit-ban.test.ts new file mode 100644 index 0000000..b133970 --- /dev/null +++ b/test/externals/unit-ban.test.ts @@ -0,0 +1,329 @@ +import { BANNED_BEARER_TOKEN } from 'multiverse/next-auth'; +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { GuruMeditationError } from 'universe/error'; +import { getDb } from 'multiverse/mongo-schema'; +import { mockDateNowMs, useMockDateNow } from 'multiverse/mongo-common'; + +import { + mockEnvFactory, + protectedImportFactory, + withMockedOutput +} from 'testverse/setup'; + +import type { InternalLimitedLogEntry } from 'multiverse/next-limit'; +import type { InternalRequestLogEntry } from 'multiverse/next-log'; +import { ObjectId } from 'mongodb'; + +// ? Ensure the isolated external picks up the memory server override +jest.mock('multiverse/mongo-schema', () => { + return jest.requireActual('multiverse/mongo-schema'); +}); + +const TEST_MARGIN_MS = 1000; +const TEN_MINUTES_MS = 10 * 60 * 1000; + +const withMockedEnv = mockEnvFactory({ + NODE_ENV: 'test', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '60', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '100', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '1', + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '5', + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '4' +}); + +const importBanHammer = protectedImportFactory< + typeof import('externals/ban-hammer').default +>({ + path: 'externals/ban-hammer', + useDefault: true +}); + +setupMemoryServerOverride(); +useMockDateNow(); + +const getRequestLogCollection = async () => { + return (await getDb({ name: 'root' })).collection( + 'request-log' + ); +}; + +const getRateLimitsCollection = async () => { + return (await getDb({ name: 'root' })).collection( + 'limited-log' + ); +}; + +const getRateLimits = async () => { + return (await getRateLimitsCollection()) + .find() + .project({ _id: 0, ip: 1, header: 1 }) + .toArray(); +}; + +const getRateLimitUntils = async () => { + return (await getRateLimitsCollection()) + .find() + .project({ _id: 0, until: 1 }) + .toArray(); +}; + +it('is verbose when no DEBUG environment variable set and compiled NODE_ENV is not test', async () => { + expect.hasAssertions(); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + DEBUG: undefined, + NODE_ENV: 'something-else', + OVERRIDE_EXPECT_ENV: 'force-no-check' + }); + + expect(infoSpy).toBeCalledWith(expect.stringContaining('execution complete')); + }); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 })); + expect(infoSpy).not.toBeCalled(); + }); +}); + +it('rejects on bad environment', async () => { + expect.hasAssertions(); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 2 }), { + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '' + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 2 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '' + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 2 }), { + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '' + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 2 }), { + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '' + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 2 }), { + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '' + }); +}); + +it('rate limits only those ips and auth headers that exceed limits', async () => { + expect.hasAssertions(); + + const now = ((n: number) => n - (n % 5000) - 1000)(mockDateNowMs); + + await (await getRateLimitsCollection()).deleteMany({}); + await ( + await getRequestLogCollection() + ).updateMany({}, { $set: { createdAt: now } }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '1' + }); + + await expect(getRateLimits()).resolves.toIncludeSameMembers([ + { ip: '1.2.3.4' }, + { header: `bearer ${BANNED_BEARER_TOKEN}` } + ]); + + await (await getRateLimitsCollection()).deleteMany({}); + await ( + await getRequestLogCollection() + ).updateMany( + { header: `bearer ${BANNED_BEARER_TOKEN}` }, + { $set: { ip: '9.8.7.6' } } + ); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '1' + }); + + await expect(getRateLimits()).resolves.toIncludeSameMembers([ + { ip: '1.2.3.4' }, + { ip: '9.8.7.6' }, + { header: `bearer ${BANNED_BEARER_TOKEN}` } + ]); + + await (await getRateLimitsCollection()).deleteMany({}); + await ( + await getRequestLogCollection() + ).insertOne({ + _id: new ObjectId(), + ip: '1.2.3.4', + header: `bearer ${BANNED_BEARER_TOKEN}`, + method: 'PUT', + resStatusCode: 200, + route: 'jest/test', + endpoint: '/fake/:jest', + createdAt: now - 1000, + durationMs: 1234 + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '11', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '1' + }); + + await expect(getRateLimits()).resolves.toBeArrayOfSize(0); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '11', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '5' + }); + + await expect(getRateLimits()).resolves.toIncludeSameMembers([ + { ip: '1.2.3.4' }, + { header: `bearer ${BANNED_BEARER_TOKEN}` } + ]); + + await (await getRateLimitsCollection()).deleteMany({}); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '11', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '1' + }); + + await expect(getRateLimits()).resolves.toBeArrayOfSize(0); +}); + +it('rate limits with respect to invocation interval', async () => { + expect.hasAssertions(); + + await (await getRateLimitsCollection()).deleteMany({}); + + const requestLogDb = await getRequestLogCollection(); + + if (!(await requestLogDb.countDocuments({}))) { + throw new GuruMeditationError('No request-log entry found?!'); + } + + const now = ((_now: number) => _now - (_now % 5000) - 2000)(mockDateNowMs); + + await requestLogDb.updateMany( + { header: `bearer ${BANNED_BEARER_TOKEN}` }, + { $set: { ip: '9.8.7.6' } } + ); + await requestLogDb.updateMany({}, { $set: { createdAt: now } }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '5', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '1' + }); + + await expect(getRateLimits()).resolves.toBeArrayOfSize(0); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10', + BAN_HAMMER_RESOLUTION_WINDOW_SECONDS: '5', + BAN_HAMMER_WILL_BE_CALLED_EVERY_SECONDS: '8' + }); + + await expect(getRateLimits()).resolves.toIncludeSameMembers([ + { header: `bearer ${BANNED_BEARER_TOKEN}` }, + { ip: '9.8.7.6' }, + { ip: '1.2.3.4' } + ]); +}); + +it('repeat offenders are punished to the maximum extent', async () => { + expect.hasAssertions(); + + await (await getRateLimitsCollection()).deleteMany({}); + await ( + await getRequestLogCollection() + ).updateMany( + { header: `bearer ${BANNED_BEARER_TOKEN}` }, + { $set: { ip: '9.8.7.6' } } + ); + + const now = mockDateNowMs; + let untils; + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '10', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10' + }); + + untils = await getRateLimitUntils(); + expect(untils).toBeArrayOfSize(3); + + untils.forEach((u) => { + expect(u.until).toBeWithin( + now + TEN_MINUTES_MS - TEST_MARGIN_MS, + now + TEN_MINUTES_MS + TEST_MARGIN_MS + ); + }); + + await (await getRateLimitsCollection()).deleteMany({}); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '20', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10' + }); + + untils = await getRateLimitUntils(); + expect(untils).toBeArrayOfSize(3); + + untils.forEach((u) => { + expect(u.until).toBeWithin( + now + 2 * TEN_MINUTES_MS - TEST_MARGIN_MS, + now + 2 * TEN_MINUTES_MS + TEST_MARGIN_MS + ); + }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 }), { + BAN_HAMMER_DEFAULT_BAN_TIME_MINUTES: '20', + BAN_HAMMER_MAX_REQUESTS_PER_WINDOW: '10', + BAN_HAMMER_RECIDIVISM_PUNISH_MULTIPLIER: '5' + }); + + untils = await getRateLimitUntils(); + expect(untils).toBeArrayOfSize(3); + + untils.forEach((u) => { + expect(u.until).toBeWithin( + now + 10 * TEN_MINUTES_MS - TEST_MARGIN_MS, + now + 10 * TEN_MINUTES_MS + TEST_MARGIN_MS + ); + }); +}); + +it('does not replace longer bans with shorter bans', async () => { + expect.hasAssertions(); + + await expect(getRateLimits()).resolves.toBeArrayOfSize(3); + + await ( + await getRateLimitsCollection() + ).updateMany({ ip: { $ne: '5.6.7.8' } }, { $set: { until: 9998784552826 } }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 })); + + let saw = 0; + (await getRateLimitUntils()).forEach((u) => u.until == 9998784552826 && saw++); + + expect(saw).toBe(2); +}); + +it('deletes outdated entries outside the punishment period', async () => { + expect.hasAssertions(); + + await expect(getRateLimits()).resolves.toBeArrayOfSize(3); + + await ( + await getRateLimitsCollection() + ).updateMany({ ip: '5.6.7.8' }, { $set: { until: 0 } }); + + await withMockedEnv(() => importBanHammer({ expectedExitCode: 0 })); + + await expect(getRateLimits()).resolves.toIncludeSameMembers([ + { ip: '1.2.3.4' }, + { header: `bearer ${BANNED_BEARER_TOKEN}` } + ]); +}); diff --git a/test/externals/unit-initialize.test.ts b/test/externals/unit-initialize.test.ts new file mode 100644 index 0000000..cac7d53 --- /dev/null +++ b/test/externals/unit-initialize.test.ts @@ -0,0 +1,287 @@ +import { ResponseTransformer, rest } from 'msw'; +import { setupServer } from 'msw/node'; + +import { debugNamespace as namespace } from 'universe/constants'; + +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; + +import { + getDummyQuestions, + getDummyQuestionAnswers, + getDummyAnswers, + getDummyAnswerComments, + getDummyComments, + getDummyQuestionComments +} from 'externals/initialize-data/api-test'; + +import { + mockEnvFactory, + protectedImportFactory, + withMockedOutput +} from 'testverse/setup'; + +import type { RestContext } from 'msw'; +import type { + SecondsFromNow, + StackExchangeApiResponse +} from 'types/stackexchange-api'; + +void namespace; + +// ? Ensure the isolated external picks up the memory server override +jest.mock('multiverse/mongo-schema', () => { + return jest.requireActual('multiverse/mongo-schema'); +}); + +const totalGeneratedQuestions = 5; +const intervalPeriodMs = 0; + +const withMockedEnv = mockEnvFactory({ + // ! For max test perf, ensure this next line is commented out unless needed + //DEBUG: `throttled-fetch:*,${namespace}:initialize-data,${namespace}:initialize-data:*`, + //DEBUG: `${namespace}:initialize-data,${namespace}:initialize-data:*`, + + // ? Use these to control the options auto-selected for inquirer. Note that + // ? these values must either be empty/undefined or a valid URL query string. + TEST_PROMPTER_INITIALIZER: 'action=hit-ignore', + TEST_PROMPTER_ERRORHANDLER: 'action=exit', + TEST_PROMPTER_FINALIZER: 'action=commit', + + NODE_ENV: 'test', + MONGODB_URI: 'fake', + MAX_ANSWER_BODY_LENGTH_BYTES: '100', + MAX_COMMENT_LENGTH: '100', + MAX_QUESTION_BODY_LENGTH_BYTES: '100', + STACKAPPS_INTERVAL_PERIOD_MS: intervalPeriodMs.toString(), + STACKAPPS_MAX_REQUESTS_PER_INTERVAL: '25', + STACKAPPS_TOTAL_API_GENERATED_QUESTIONS: totalGeneratedQuestions.toString(), + STACKAPPS_COLLECTALL_QUESTION_ANSWERS: '4', + STACKAPPS_COLLECTALL_QUESTION_COMMENTS: '3', + STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS: '2', + STACKAPPS_MAX_PAGE_SIZE: '2', // * Should be <= half of the above constants + STACKAPPS_AUTH_KEY: 'special-stack-exchange-key' +}); + +const importInitializeData = protectedImportFactory< + typeof import('externals/initialize-data').default +>({ + path: 'externals/initialize-data', + useDefault: true +}); + +let counter = 0; +const mockedResponseJson: Partial> = {}; + +const calcBackoffModulo = Math.ceil(5.49 * totalGeneratedQuestions); +const calcError500Modulo = + totalGeneratedQuestions + Math.max(2, Math.ceil(0.1 * totalGeneratedQuestions)); +const calcError502Modulo = calcError500Modulo + 1; +const calcError503Modulo = calcError500Modulo * 2; +const calcError429Modulo = calcError500Modulo * 2 + 1; + +const maybeErrorResponse = ( + ctx: RestContext, + { okTransformers }: { okTransformers: ResponseTransformer[] } +) => { + delete mockedResponseJson.backoff; + + if (counter) { + if (counter % calcBackoffModulo == 0) { + mockedResponseJson.backoff = Math.max( + 0.1, + intervalPeriodMs / 200 + ) as SecondsFromNow; + } + + const results = + counter % calcError500Modulo == 0 + ? [ + ctx.status(500), + ctx.json({ + error_id: 123, + error_message: 'fake 500 error', + error_name: 'fake_500' + }) + ] + : counter % calcError503Modulo == 0 + ? [ + ctx.status(503), + ctx.json({ + error_id: 123, + error_message: 'fake 503 error', + error_name: 'fake_503' + }) + ] + : counter % calcError502Modulo == 0 + ? [ + ctx.status(502), + ctx.json({ + error_id: 123, + error_message: 'fake 502 error', + error_name: 'fake_502' + }) + ] + : counter % calcError429Modulo == 0 + ? [ctx.status(429)] + : okTransformers; + + counter++; + return results; + } else { + counter++; + return okTransformers; + } +}; + +const server = setupServer( + rest.get('*/questions/:question_id/answers', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestionAnswers(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/questions/:question_id/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestionComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/answers/:answer_id/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyAnswerComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/questions', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyQuestions(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/answers', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyAnswers(req), + ...mockedResponseJson + }) + ] + }) + ); + }), + rest.get('*/comments', async (req, res, ctx) => { + return res( + ...maybeErrorResponse(ctx, { + okTransformers: [ + ctx.status(200), + ctx.json({ + ...getDummyComments(req), + ...mockedResponseJson + }) + ] + }) + ); + }) +); + +setupMemoryServerOverride(); + +beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +it('is verbose when no DEBUG environment variable set and compiled NODE_ENV is not test', async () => { + expect.hasAssertions(); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importInitializeData({ expectedExitCode: 0 }), { + DEBUG: undefined, + NODE_ENV: 'something-else', + OVERRIDE_EXPECT_ENV: 'force-no-check', + TEST_SKIP_REQUESTS: 'true' + }); + + expect(infoSpy).toBeCalledWith(expect.stringContaining('execution complete')); + }); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importInitializeData({ expectedExitCode: 0 }), { + TEST_SKIP_REQUESTS: 'true' + }); + expect(infoSpy).not.toBeCalled(); + }); +}); + +it('rejects on bad environment', async () => { + expect.hasAssertions(); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_INTERVAL_PERIOD_MS: 'bad', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_MAX_REQUESTS_PER_INTERVAL: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_TOTAL_API_GENERATED_QUESTIONS: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_COLLECTALL_QUESTION_ANSWERS: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_COLLECTALL_QUESTION_COMMENTS: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_COLLECTALL_FIRST_ANSWER_COMMENTS: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_MAX_PAGE_SIZE: '', + TEST_SKIP_REQUESTS: 'true' + }); + + await withMockedEnv(() => importInitializeData({ expectedExitCode: 2 }), { + STACKAPPS_AUTH_KEY: '', + TEST_SKIP_REQUESTS: 'true' + }); +}); diff --git a/test/externals/unit-prune.test.ts b/test/externals/unit-prune.test.ts new file mode 100644 index 0000000..7b7c41d --- /dev/null +++ b/test/externals/unit-prune.test.ts @@ -0,0 +1,264 @@ +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { getDb } from 'multiverse/mongo-schema'; +import { useMockDateNow } from 'multiverse/mongo-common'; + +import { + mockEnvFactory, + protectedImportFactory, + withMockedOutput +} from 'testverse/setup'; + +import { GuruMeditationError, TrialError } from 'universe/error'; + +// * Follow the steps (below) to tailor these tests to this specific project 😉 + +// ? Ensure the isolated external picks up the memory server override +jest.mock('multiverse/mongo-schema', () => { + return jest.requireActual('multiverse/mongo-schema'); +}); + +// // * Step 1: Add new collections here w/ keys of form: database.collection +// ! Only do step 1 if you're using count-based limits and NOT byte-based! +// const testCollectionsMap = { +// 'root.request-log': dummyRootData['request-log'].length, +// 'root.limited-log': dummyRootData['limited-log'].length, +// 'app.mail': dummyAppData['mail'].length, +// 'app.questions': dummyAppData['questions'].length, +// 'app.users': dummyAppData['users'].length +// }; + +const testCollections = [ + 'root.request-log', + 'root.limited-log', + 'app.mail', + 'app.questions', + 'app.users' +] as const; + +// TODO: replace with byte versions +const withMockedEnv = mockEnvFactory({ + NODE_ENV: 'test', + PRUNE_DATA_MAX_LOGS_BYTES: '50mb', + PRUNE_DATA_MAX_BANNED_BYTES: '10mb', + // * Step 2: Add new env var default values here + PRUNE_DATA_MAX_MAIL_BYTES: '50mb', + PRUNE_DATA_MAX_QUESTIONS_BYTES: '250mb', + PRUNE_DATA_MAX_USERS_BYTES: '75mb' +}); + +const importPruneData = protectedImportFactory< + typeof import('externals/prune-data').default +>({ + path: 'externals/prune-data', + useDefault: true +}); + +setupMemoryServerOverride(); +useMockDateNow(); + +/** + * Accepts one or more database and collection names in the form + * `database.collection` and returns the number of documents contained in each + * collection or the size of each collection in bytes depending on the value of + * `metric`. + */ +async function getCollectionSize( + collection: string, + { metric }: { metric: 'count' | 'bytes' } +): Promise; +async function getCollectionSize( + collections: readonly string[], + { metric }: { metric: 'count' | 'bytes' } +): Promise>; +async function getCollectionSize( + collections: string | readonly string[], + { metric }: { metric: 'count' | 'bytes' } +): Promise> { + const targetCollections = [collections].flat(); + const result = Object.assign( + {}, + ...(await Promise.all( + targetCollections.map(async (dbCollection) => { + const [dbName, ...rawCollectionName] = dbCollection.split('.'); + + if (!dbName || rawCollectionName.length != 1) { + throw new TrialError(`invalid input "${dbCollection}" to countCollection`); + } + + const colDb = (await getDb({ name: dbName })).collection( + rawCollectionName[0] + ); + + if (metric == 'count') { + return colDb.countDocuments().then((count) => ({ [dbCollection]: count })); + } else if (metric == 'bytes') { + return colDb + .aggregate<{ size: number }>([ + { + $group: { + _id: null, + size: { $sum: { $bsonSize: '$$ROOT' } } + } + } + ]) + .next() + .then((r) => ({ + [dbCollection]: r?.size ?? 0 + })); + } else { + throw new GuruMeditationError(`unknown metric "${metric}"`); + } + }) + )) + ); + + const resultLength = Object.keys(result).length; + + if (resultLength != targetCollections.length) { + throw new TrialError('invalid output from countCollection'); + } + + return resultLength == 1 ? result[collections.toString()] : result; +} + +it('is verbose when no DEBUG environment variable set and compiled NODE_ENV is not test', async () => { + expect.hasAssertions(); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importPruneData({ expectedExitCode: 0 }), { + DEBUG: undefined, + NODE_ENV: 'something-else', + OVERRIDE_EXPECT_ENV: 'force-no-check' + }); + + expect(infoSpy).toBeCalledWith(expect.stringContaining('execution complete')); + }); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importPruneData({ expectedExitCode: 0 })); + expect(infoSpy).not.toBeCalled(); + }); +}); + +it('rejects on bad environment', async () => { + expect.hasAssertions(); + + // * Step 3: Add new env vars emptiness tests below + + // ? Remember that withMockedEnv is the result of calling a factory function + // ? with all the PRUNE_DATA_MAX_X env vars already defined. + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_LOGS_BYTES: '', + PRUNE_DATA_MAX_BANNED_BYTES: '', + PRUNE_DATA_MAX_MAIL_BYTES: '', + PRUNE_DATA_MAX_QUESTIONS_BYTES: '', + PRUNE_DATA_MAX_USERS_BYTES: '' + }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_LOGS_BYTES: '' + }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_BANNED_BYTES: '' + }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_MAIL_BYTES: '' + }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_QUESTIONS_BYTES: '' + }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 2 }), { + PRUNE_DATA_MAX_USERS_BYTES: '' + }); +}); + +// ? This is a bytes-based test. Look elsewhere for the old count-based tests! +it('respects the limits imposed by PRUNE_DATA_MAX_X environment variables', async () => { + expect.hasAssertions(); + + const initialSizes = await getCollectionSize(testCollections, { metric: 'bytes' }); + + // * Step 4: Add new env vars low-prune-threshold tests below. + + const expectedSizes = { + 'root.request-log': initialSizes['root.request-log'] / 2, + 'root.limited-log': initialSizes['root.limited-log'] / 2, + 'app.mail': initialSizes['app.mail'] / 2, + 'app.questions': initialSizes['app.questions'] / 2, + 'app.users': initialSizes['app.users'] / 2 + }; + + await withMockedEnv(() => importPruneData({ expectedExitCode: 0 }), { + PRUNE_DATA_MAX_LOGS_BYTES: String(expectedSizes['root.request-log']), + PRUNE_DATA_MAX_BANNED_BYTES: String(expectedSizes['root.limited-log']), + PRUNE_DATA_MAX_MAIL_BYTES: String(expectedSizes['app.mail']), + PRUNE_DATA_MAX_QUESTIONS_BYTES: String(expectedSizes['app.questions']), + PRUNE_DATA_MAX_USERS_BYTES: String(expectedSizes['app.users']) + }); + + const newSizes = await getCollectionSize(testCollections, { metric: 'bytes' }); + + expect(newSizes['root.request-log']).toBeLessThanOrEqual( + expectedSizes['root.request-log'] + ); + + expect(newSizes['root.limited-log']).toBeLessThanOrEqual( + expectedSizes['root.limited-log'] + ); + + expect(newSizes['app.mail']).toBeLessThanOrEqual(expectedSizes['app.mail']); + + expect(newSizes['app.questions']).toBeLessThanOrEqual( + expectedSizes['app.questions'] + ); + + expect(newSizes['app.users']).toBeLessThanOrEqual(expectedSizes['app.users']); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 0 }), { + PRUNE_DATA_MAX_LOGS_BYTES: '1', + PRUNE_DATA_MAX_BANNED_BYTES: '1', + PRUNE_DATA_MAX_MAIL_BYTES: '1', + PRUNE_DATA_MAX_QUESTIONS_BYTES: '1', + PRUNE_DATA_MAX_USERS_BYTES: '1' + }); + + const latestSizes = await getCollectionSize(testCollections, { metric: 'bytes' }); + + expect(latestSizes['root.request-log']).toBe(0); + expect(latestSizes['root.limited-log']).toBe(0); + expect(latestSizes['app.mail']).toBe(0); + expect(latestSizes['app.questions']).toBe(0); + expect(latestSizes['app.users']).toBe(0); +}); + +// ? This is a bytes-based test. Look elsewhere for the old count-based tests! +it('only deletes entries if necessary', async () => { + expect.hasAssertions(); + + const initialSizes = await getCollectionSize(testCollections, { metric: 'bytes' }); + + await withMockedEnv(() => importPruneData({ expectedExitCode: 0 }), { + PRUNE_DATA_MAX_LOGS_BYTES: '100gb', + PRUNE_DATA_MAX_BANNED_BYTES: '100gb', + // * Step 5: Add new env vars high-prune-threshold values here + PRUNE_DATA_MAX_MAIL_BYTES: '100gb', + PRUNE_DATA_MAX_QUESTIONS_BYTES: '100gb', + PRUNE_DATA_MAX_USERS_BYTES: '100gb' + }); + + const newSizes = await getCollectionSize(testCollections, { metric: 'bytes' }); + + expect(newSizes['root.request-log']).toBe(initialSizes['root.request-log']); + expect(newSizes['root.limited-log']).toBe(initialSizes['root.limited-log']); + + expect(newSizes['app.mail']).toBe(initialSizes['app.mail']); + + expect(newSizes['app.questions']).toBe(initialSizes['app.questions']); + + expect(newSizes['app.users']).toBe(initialSizes['app.users']); +}); diff --git a/test/externals/unit-stats.test.ts b/test/externals/unit-stats.test.ts new file mode 100644 index 0000000..15884d1 --- /dev/null +++ b/test/externals/unit-stats.test.ts @@ -0,0 +1,60 @@ +import { setupMemoryServerOverride } from 'multiverse/mongo-test'; +import { useMockDateNow } from 'multiverse/mongo-common'; + +import { + mockEnvFactory, + protectedImportFactory, + withMockedOutput +} from 'testverse/setup'; + +// ? Ensure the isolated external picks up the memory server override +jest.mock('multiverse/mongo-schema', () => { + return jest.requireActual('multiverse/mongo-schema'); +}); + +jest.mock('jsonfile', () => ({ + readFile: jest.fn().mockReturnValue({}), + writeFile: jest.fn() +})); + +const withMockedEnv = mockEnvFactory({ + NODE_ENV: 'test', + MONGODB_URI: 'fake' +}); + +const importLogStats = protectedImportFactory< + typeof import('externals/log-stats').default +>({ + path: 'externals/log-stats', + useDefault: true +}); + +setupMemoryServerOverride(); +useMockDateNow(); + +it('is verbose when no DEBUG environment variable set and compiled NODE_ENV is not test', async () => { + expect.hasAssertions(); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importLogStats({ expectedExitCode: 0 }), { + DEBUG: undefined, + NODE_ENV: 'something-else', + OVERRIDE_EXPECT_ENV: 'force-no-check' + }); + + expect(infoSpy).toBeCalledWith(expect.stringContaining('execution complete')); + }); + + await withMockedOutput(async ({ infoSpy }) => { + await withMockedEnv(() => importLogStats({ expectedExitCode: 0 })); + expect(infoSpy).not.toBeCalled(); + }); +}); + +it('rejects on bad environment', async () => { + expect.hasAssertions(); + + await withMockedEnv(() => importLogStats({ expectedExitCode: 2 }), { + MONGODB_URI: '' + }); +}); diff --git a/test/fixtures/index.ts b/test/fixtures/index.ts new file mode 100644 index 0000000..05d2daf --- /dev/null +++ b/test/fixtures/index.ts @@ -0,0 +1,340 @@ +import { asMockedFunction } from '@xunnamius/jest-types'; + +import { + applyVotesUpdateOperation, + authAppUser, + createAnswer, + createComment, + createMessage, + createQuestion, + createUser, + deleteComment, + deleteUser, + getAllUsers, + getAnswers, + getComments, + getQuestion, + getUser, + getUserAnswers, + getUserMessages, + getUserQuestions, + searchQuestions, + updateAnswer, + updateQuestion, + updateUser, + deleteAnswer, + deleteMessage, + deleteQuestion, + getHowUserVoted +} from 'universe/backend'; + +import V1EndpointUsers, { + config as V1ConfigUsers, + metadata as V1MetadataUsers +} from 'universe/pages/api/v1/users'; + +import V1EndpointUsersUsername, { + config as V1ConfigUsersUsername, + metadata as V1MetadataUsersUsername +} from 'universe/pages/api/v1/users/[username]'; + +import V1EndpointUsersUsernameAuth, { + config as V1ConfigUsersUsernameAuth, + metadata as V1MetadataUsersUsernameAuth +} from 'universe/pages/api/v1/users/[username]/auth'; + +import V1EndpointUsersUsernameQuestions, { + config as V1ConfigUsersUsernameQuestions, + metadata as V1MetadataUsersUsernameQuestions +} from 'universe/pages/api/v1/users/[username]/questions'; + +import V1EndpointUsersUsernameAnswers, { + config as V1ConfigUsersUsernameAnswers, + metadata as V1MetadataUsersUsernameAnswers +} from 'universe/pages/api/v1/users/[username]/answers'; + +import V1EndpointUsersUsernamePoints, { + config as V1ConfigUsersUsernamePoints, + metadata as V1MetadataUsersUsernamePoints +} from 'universe/pages/api/v1/users/[username]/points'; + +import V1EndpointMail, { + config as V1ConfigMail, + metadata as V1MetadataMail +} from 'universe/pages/api/v1/mail'; + +import V1EndpointMailUsername, { + config as V1ConfigMailUsername, + metadata as V1MetadataMailUsername +} from 'universe/pages/api/v1/mail/[username]'; + +import V1EndpointQuestionsSearch, { + config as V1ConfigQuestionsSearch, + metadata as V1MetadataQuestionsSearch +} from 'universe/pages/api/v1/questions/search'; + +import V1EndpointQuestions, { + config as V1ConfigQuestions, + metadata as V1MetadataQuestions +} from 'universe/pages/api/v1/questions'; + +import V1EndpointQuestionsQuestionId, { + config as V1ConfigQuestionsQuestionId, + metadata as V1MetadataQuestionsQuestionId +} from 'universe/pages/api/v1/questions/[question_id]'; + +import V1EndpointQuestionsQuestionIdVoteUsername, { + config as V1ConfigQuestionsQuestionIdVoteUsername, + metadata as V1MetadataQuestionsQuestionIdVoteUsername +} from 'universe/pages/api/v1/questions/[question_id]/vote/[username]'; + +import V1EndpointQuestionsQuestionIdComments, { + config as V1ConfigQuestionsQuestionIdComments, + metadata as V1MetadataQuestionsQuestionIdComments +} from 'universe/pages/api/v1/questions/[question_id]/comments'; + +import V1EndpointQuestionsQuestionIdCommentsCommentId, { + config as V1ConfigQuestionsQuestionIdCommentsCommentId, + metadata as V1MetadataQuestionsQuestionIdCommentsCommentId +} from 'universe/pages/api/v1/questions/[question_id]/comments/[comment_id]'; + +import V1EndpointQuestionsQuestionIdCommentsCommentIdVoteUsername, { + config as V1ConfigQuestionsQuestionIdCommentsCommentIdVoteUsername, + metadata as V1MetadataQuestionsQuestionIdCommentsCommentIdVoteUsername +} from 'universe/pages/api/v1/questions/[question_id]/comments/[comment_id]/vote/[username]'; + +import V1EndpointQuestionsQuestionIdAnswers, { + config as V1ConfigQuestionsQuestionIdAnswers, + metadata as V1MetadataQuestionsQuestionIdAnswers +} from 'universe/pages/api/v1/questions/[question_id]/answers'; + +import V1EndpointQuestionsQuestionIdAnswersAnswerId, { + config as V1ConfigQuestionsQuestionIdAnswersAnswerId, + metadata as V1MetadataQuestionsQuestionIdAnswersAnswerId +} from 'universe/pages/api/v1/questions/[question_id]/answers/[answer_id]'; + +import V1EndpointQuestionsQuestionIdAnswersAnswerIdVoteUsername, { + config as V1ConfigQuestionsQuestionIdAnswersAnswerIdVoteUsername, + metadata as V1MetadataQuestionsQuestionIdAnswersAnswerIdVoteUsername +} from 'universe/pages/api/v1/questions/[question_id]/answers/[answer_id]/vote/[username]'; + +import V1EndpointQuestionsQuestionIdAnswersAnswerIdComments, { + config as V1ConfigQuestionsQuestionIdAnswersAnswerIdComments, + metadata as V1MetadataQuestionsQuestionIdAnswersAnswerIdComments +} from 'universe/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments'; + +import V1EndpointQuestionsQuestionIdAnswersAnswerIdCommentsCommentId, { + config as V1ConfigQuestionsQuestionIdAnswersAnswerIdCommentsCommentId, + metadata as V1MetadataQuestionsQuestionIdAnswersAnswerIdCommentsCommentId +} from 'universe/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]'; + +import V1EndpointQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername, { + config as V1ConfigQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername, + metadata as V1MetadataQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername +} from 'universe/pages/api/v1/questions/[question_id]/answers/[answer_id]/comments/[comment_id]/vote/[username]'; + +import type { NextApiHandler, PageConfig } from 'next'; +import { + PublicAnswer, + PublicComment, + PublicMail, + PublicQuestion, + PublicUser +} from 'universe/backend/db'; + +export type NextApiHandlerMixin = NextApiHandler & { + config?: PageConfig; + uri: string; +}; + +/** + * The entire live API topology gathered together into one convenient object. + */ +export const api = { + v1: { + users: V1EndpointUsers as NextApiHandlerMixin, + usersUsername: V1EndpointUsersUsername as NextApiHandlerMixin, + usersUsernameAuth: V1EndpointUsersUsernameAuth as NextApiHandlerMixin, + usersUsernameQuestions: V1EndpointUsersUsernameQuestions as NextApiHandlerMixin, + usersUsernameAnswers: V1EndpointUsersUsernameAnswers as NextApiHandlerMixin, + usersUsernamePoints: V1EndpointUsersUsernamePoints as NextApiHandlerMixin, + mail: V1EndpointMail as NextApiHandlerMixin, + mailUsername: V1EndpointMailUsername as NextApiHandlerMixin, + questions: V1EndpointQuestions as NextApiHandlerMixin, + questionsSearch: V1EndpointQuestionsSearch as NextApiHandlerMixin, + questionsQuestionId: V1EndpointQuestionsQuestionId as NextApiHandlerMixin, + questionsQuestionIdVoteUsername: + V1EndpointQuestionsQuestionIdVoteUsername as NextApiHandlerMixin, + questionsQuestionIdComments: + V1EndpointQuestionsQuestionIdComments as NextApiHandlerMixin, + questionsQuestionIdCommentsCommentId: + V1EndpointQuestionsQuestionIdCommentsCommentId as NextApiHandlerMixin, + questionsQuestionIdCommentsCommentIdVoteUsername: + V1EndpointQuestionsQuestionIdCommentsCommentIdVoteUsername as NextApiHandlerMixin, + questionsQuestionIdAnswers: + V1EndpointQuestionsQuestionIdAnswers as NextApiHandlerMixin, + questionsQuestionIdAnswersAnswerId: + V1EndpointQuestionsQuestionIdAnswersAnswerId as NextApiHandlerMixin, + questionsQuestionIdAnswersAnswerIdVoteUsername: + V1EndpointQuestionsQuestionIdAnswersAnswerIdVoteUsername as NextApiHandlerMixin, + questionsQuestionIdAnswersAnswerIdComments: + V1EndpointQuestionsQuestionIdAnswersAnswerIdComments as NextApiHandlerMixin, + questionsQuestionIdAnswersAnswerIdCommentsCommentId: + V1EndpointQuestionsQuestionIdAnswersAnswerIdCommentsCommentId as NextApiHandlerMixin, + questionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername: + V1EndpointQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername as NextApiHandlerMixin + } +}; + +api.v1.users.config = V1ConfigUsers; +api.v1.usersUsername.config = V1ConfigUsersUsername; +api.v1.usersUsernameAuth.config = V1ConfigUsersUsernameAuth; +api.v1.usersUsernameQuestions.config = V1ConfigUsersUsernameQuestions; +api.v1.usersUsernameAnswers.config = V1ConfigUsersUsernameAnswers; +api.v1.usersUsernamePoints.config = V1ConfigUsersUsernamePoints; +api.v1.mail.config = V1ConfigMail; +api.v1.mailUsername.config = V1ConfigMailUsername; +api.v1.questions.config = V1ConfigQuestions; +api.v1.questionsSearch.config = V1ConfigQuestionsSearch; +api.v1.questionsQuestionId.config = V1ConfigQuestionsQuestionId; +api.v1.questionsQuestionIdVoteUsername.config = + V1ConfigQuestionsQuestionIdVoteUsername; +api.v1.questionsQuestionIdComments.config = V1ConfigQuestionsQuestionIdComments; +api.v1.questionsQuestionIdCommentsCommentId.config = + V1ConfigQuestionsQuestionIdCommentsCommentId; +api.v1.questionsQuestionIdCommentsCommentIdVoteUsername.config = + V1ConfigQuestionsQuestionIdCommentsCommentIdVoteUsername; +api.v1.questionsQuestionIdAnswers.config = V1ConfigQuestionsQuestionIdAnswers; +api.v1.questionsQuestionIdAnswersAnswerId.config = + V1ConfigQuestionsQuestionIdAnswersAnswerId; +api.v1.questionsQuestionIdAnswersAnswerIdVoteUsername.config = + V1ConfigQuestionsQuestionIdAnswersAnswerIdVoteUsername; +api.v1.questionsQuestionIdAnswersAnswerIdComments.config = + V1ConfigQuestionsQuestionIdAnswersAnswerIdComments; +api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentId.config = + V1ConfigQuestionsQuestionIdAnswersAnswerIdCommentsCommentId; +api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername.config = + V1ConfigQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername; + +api.v1.users.uri = V1MetadataUsers.descriptor; +api.v1.usersUsername.uri = V1MetadataUsersUsername.descriptor; +api.v1.usersUsernameAuth.uri = V1MetadataUsersUsernameAuth.descriptor; +api.v1.usersUsernameQuestions.uri = V1MetadataUsersUsernameQuestions.descriptor; +api.v1.usersUsernameAnswers.uri = V1MetadataUsersUsernameAnswers.descriptor; +api.v1.usersUsernamePoints.uri = V1MetadataUsersUsernamePoints.descriptor; +api.v1.mail.uri = V1MetadataMail.descriptor; +api.v1.mailUsername.uri = V1MetadataMailUsername.descriptor; +api.v1.questions.uri = V1MetadataQuestions.descriptor; +api.v1.questionsSearch.uri = V1MetadataQuestionsSearch.descriptor; +api.v1.questionsQuestionId.uri = V1MetadataQuestionsQuestionId.descriptor; +api.v1.questionsQuestionIdVoteUsername.uri = + V1MetadataQuestionsQuestionIdVoteUsername.descriptor; +api.v1.questionsQuestionIdComments.uri = + V1MetadataQuestionsQuestionIdComments.descriptor; +api.v1.questionsQuestionIdCommentsCommentId.uri = + V1MetadataQuestionsQuestionIdCommentsCommentId.descriptor; +api.v1.questionsQuestionIdCommentsCommentIdVoteUsername.uri = + V1MetadataQuestionsQuestionIdCommentsCommentIdVoteUsername.descriptor; +api.v1.questionsQuestionIdAnswers.uri = + V1MetadataQuestionsQuestionIdAnswers.descriptor; +api.v1.questionsQuestionIdAnswersAnswerId.uri = + V1MetadataQuestionsQuestionIdAnswersAnswerId.descriptor; +api.v1.questionsQuestionIdAnswersAnswerIdVoteUsername.uri = + V1MetadataQuestionsQuestionIdAnswersAnswerIdVoteUsername.descriptor; +api.v1.questionsQuestionIdAnswersAnswerIdComments.uri = + V1MetadataQuestionsQuestionIdAnswersAnswerIdComments.descriptor; +api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentId.uri = + V1MetadataQuestionsQuestionIdAnswersAnswerIdCommentsCommentId.descriptor; +api.v1.questionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername.uri = + V1MetadataQuestionsQuestionIdAnswersAnswerIdCommentsCommentIdVoteUsername.descriptor; + +/** + * A convenience function that mocks the entire backend and returns the mock + * functions. Uses `beforeEach` under the hood. + * + * **WARNING: YOU MUST CALL `jest.mock('universe/backend')` before calling this + * function!** + */ +export function setupMockBackend() { + const mockedApplyVotesUpdateOperation = asMockedFunction(applyVotesUpdateOperation); + const mockedAuthAppUser = asMockedFunction(authAppUser); + const mockedCreateAnswer = asMockedFunction(createAnswer); + const mockedCreateComment = asMockedFunction(createComment); + const mockedCreateMessage = asMockedFunction(createMessage); + const mockedCreateQuestion = asMockedFunction(createQuestion); + const mockedCreateUser = asMockedFunction(createUser); + const mockedDeleteComment = asMockedFunction(deleteComment); + const mockedDeleteUser = asMockedFunction(deleteUser); + const mockedGetAllUsers = asMockedFunction(getAllUsers); + const mockedGetAnswers = asMockedFunction(getAnswers); + const mockedGetComments = asMockedFunction(getComments); + const mockedGetQuestion = asMockedFunction(getQuestion); + const mockedGetUser = asMockedFunction(getUser); + const mockedGetUserAnswers = asMockedFunction(getUserAnswers); + const mockedGetUserMessages = asMockedFunction(getUserMessages); + const mockedGetUserQuestions = asMockedFunction(getUserQuestions); + const mockedSearchQuestions = asMockedFunction(searchQuestions); + const mockedUpdateAnswer = asMockedFunction(updateAnswer); + const mockedUpdateQuestion = asMockedFunction(updateQuestion); + const mockedUpdateUser = asMockedFunction(updateUser); + const mockedDeleteAnswer = asMockedFunction(deleteAnswer); + const mockedDeleteMessage = asMockedFunction(deleteMessage); + const mockedDeleteQuestion = asMockedFunction(deleteQuestion); + const mockedGetHowUserVoted = asMockedFunction(getHowUserVoted); + + beforeEach(() => { + mockedApplyVotesUpdateOperation.mockReturnValue(Promise.resolve()); + mockedAuthAppUser.mockReturnValue(Promise.resolve(false)); + mockedCreateAnswer.mockReturnValue(Promise.resolve({} as PublicAnswer)); + mockedCreateComment.mockReturnValue(Promise.resolve({} as PublicComment)); + mockedCreateMessage.mockReturnValue(Promise.resolve({} as PublicMail)); + mockedCreateQuestion.mockReturnValue(Promise.resolve({} as PublicQuestion)); + mockedCreateUser.mockReturnValue(Promise.resolve({} as PublicUser)); + mockedDeleteComment.mockReturnValue(Promise.resolve()); + mockedDeleteUser.mockReturnValue(Promise.resolve()); + mockedGetAllUsers.mockReturnValue(Promise.resolve([])); + mockedGetAnswers.mockReturnValue(Promise.resolve([])); + mockedGetComments.mockReturnValue(Promise.resolve([])); + mockedGetQuestion.mockReturnValue(Promise.resolve({} as PublicQuestion)); + mockedGetUser.mockReturnValue(Promise.resolve({} as PublicUser)); + mockedGetUserAnswers.mockReturnValue(Promise.resolve([])); + mockedGetUserMessages.mockReturnValue(Promise.resolve([])); + mockedGetUserQuestions.mockReturnValue(Promise.resolve([])); + mockedSearchQuestions.mockReturnValue(Promise.resolve([])); + mockedUpdateAnswer.mockReturnValue(Promise.resolve()); + mockedUpdateQuestion.mockReturnValue(Promise.resolve()); + mockedUpdateUser.mockReturnValue(Promise.resolve()); + mockedDeleteAnswer.mockReturnValue(Promise.resolve()); + mockedDeleteMessage.mockReturnValue(Promise.resolve()); + mockedDeleteQuestion.mockReturnValue(Promise.resolve()); + mockedGetHowUserVoted.mockReturnValue(Promise.resolve(null)); + }); + + return { + mockedApplyVotesUpdateOperation, + mockedAuthAppUser, + mockedCreateAnswer, + mockedCreateComment, + mockedCreateMessage, + mockedCreateQuestion, + mockedCreateUser, + mockedDeleteComment, + mockedDeleteUser, + mockedGetAllUsers, + mockedGetAnswers, + mockedGetComments, + mockedGetQuestion, + mockedGetUser, + mockedGetUserAnswers, + mockedGetUserMessages, + mockedGetUserQuestions, + mockedSearchQuestions, + mockedUpdateAnswer, + mockedUpdateQuestion, + mockedUpdateUser, + mockedDeleteAnswer, + mockedDeleteMessage, + mockedDeleteQuestion, + mockedGetHowUserVoted + }; +} diff --git a/test/fixtures/integration.ts b/test/fixtures/integration.ts new file mode 100644 index 0000000..a97f1d1 --- /dev/null +++ b/test/fixtures/integration.ts @@ -0,0 +1,1227 @@ +import { name as pkgName } from 'package'; +import { toss } from 'toss-expression'; +import { ObjectId } from 'mongodb'; +import debugFactory from 'debug'; + +import { GuruMeditationError } from 'universe/error'; +import { getEnv } from 'universe/backend/env'; + +import { toPublicUser } from 'universe/backend/db'; + +import { dummyAppData } from 'testverse/db'; + +import type { Promisable } from 'type-fest'; + +import type { NewUser, PatchUser, PublicUser } from 'universe/backend/db'; + +import type { NextApiHandlerMixin } from 'testverse/fixtures'; + +// TODO: XXX: turn a lot of this into some kind of package; needs to be generic +// TODO: XXX: enough to handle various use cases though :) Maybe +// TODO: XXX: @xunnamius/fable for the generic version, along with +// TODO: XXX: @xunnamius/fable-next, @xunnamius/fable-next-api (below), +// TODO: XXX: @xunnamius/fable-X plugins. Initial version of @xunnamius/fable +// TODO: XXX: would just be the next API version. + +// TODO: XXX: add an `id` param that allows getResultAt using that `id` (along +// TODO: XXX: with index) + +// TODO: XXX: document functionality: RUN_ONLY='#, ##,###,...' +// TODO: XXX: "fail fast" should be optional + +const debug = debugFactory(`${pkgName}:integration-fixtures`); + +/** + * A single test result stored in `memory`. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type TestResult = { + status: number; + json: T | undefined; +}; + +/** + * Stored results from past fixtures runs made available for future fixtures + * runs via `memory`. + */ +export type TestResultset = TestResult[] & { + /** + * A property containing a mapping between optional test ids and their + * results. + */ + idMap: Record; + /** + * A property containing the most previous resultset. + */ + latest: TestResult; + /** + * Get the HTTP response status and json result from previously run tests by + * index. You can pass a negative index to begin counting backwards from the + * current test. Tests are zero-indexed, i.e. use `getResultAt(0)` to refer to + * the very first resultset. `getResultAt(1)` will return the second + * resultset. `getResultAt(-1)` will return the immediately previous resultset + * (same as what the `latest` property returns). + * + * @param index Specify a previous test result index starting at 1 (not zero!) + */ + getResultAt(index: number): TestResult; + getResultAt(index: number, prop: string): T; + getResultAt(index: string): TestResult; + getResultAt(index: string, prop: string): T; +}; + +/** + * Represents a test that executes an HTTP request and evaluate the response + * for correctness. + */ +export type TestFixture = { + /** + * An optional id that can be used to reference the result from this fixture + * directly as opposed to by index. + * + * @example getResultAt('my-id') === getResultAt(22) + */ + id?: string; + /** + * If `invisible == true`, the test is not counted when generating positional + * fixtures. + * + * @default false + */ + invisible?: boolean; + /** + * The test index X (as in "#X") that is reported to the user when a test + * fails. + */ + displayIndex: number; + /** + * A very brief couple of words added to the end of the test title. + */ + subject?: string; + /** + * The handler under test. + */ + handler?: NextApiHandlerMixin; + /** + * The method of the mock request. + */ + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; + /** + * Represents mock "processed" dynamic route components and query params. + */ + params?: + | Record + | ((prevResults: TestResultset) => Promisable>); + /** + * The body of the mock request. Automatically stringified. + */ + body?: + | Record + | ((prevResults: TestResultset) => Promisable>); + /** + * The expected shape of the HTTP response. + */ + response?: { + /** + * The expected response status. If status != 200, we expect `json.success` + * to be `false`. Otherwise, we expect it to be `true`. All status-related + * checks are skipped if a callback is provided that returns `undefined`. + */ + status?: + | number + | (( + status: number, + prevResults: TestResultset + ) => Promisable); + /** + * The expected JSON response body. No need to test for `success` as that is + * handled automatically (unless a status callback was used and it returned + * `undefined`). Jest async matchers are also supported. All json-related + * checks are skipped if a callback is provided that returns `undefined` or + * `json` itself is `undefined`. + */ + json?: + | Record + | jest.AsymmetricMatcher + | (( + json: Record | undefined, + prevResults: TestResultset + ) => Promisable< + Record | jest.AsymmetricMatcher | undefined + >); + }; +}; + +export function getFixtures( + api: typeof import('testverse/fixtures').api +): TestFixture[] { + const runOnly = process.env.RUN_ONLY?.split(',') + .flatMap((n) => { + const range = n + .split('-') + .map((m) => parseInt(m)) + .filter((m) => !Number.isNaN(m)); + + const min = Math.min(...range); + const max = Math.max(...range); + + debug(`min: ${min}`); + debug(`max: ${max}`); + debug(`range: ${range}`); + + if (!(0 < min && min <= max && max < Infinity)) { + throw new GuruMeditationError(`invalid RUN_ONLY range "${min}-${max}"`); + } else { + const finalRange = Array.from({ length: max - min + 1 }).map( + (_, ndx) => min + ndx + ); + debug(`final range: ${finalRange}`); + return finalRange; + } + }) + .sort((a, b) => a - b); + + // * Note: user passwords are their usernames + const fixtures: Omit[] = [ + // * Creating, retrieving, authenticating, and updating users + { + id: 'user-hillary', + subject: 'create new user "the-hill"', + handler: api.v1.users, + method: 'POST', + body: { + username: 'the-hill', + email: 'h@hillaryclinton.com', + key: '3ffd270e595ef1e485437d90e788d2965acb602a7412f50760140304f4b1f039998ee471de8ddb7c3115f3dee86ba487a213be9604db0ef23ccb99414e47d452', + salt: 'd63a897a76ece8b9a503913db68c95af' + } as NewUser, + response: { + status: 200, + json: { + user: { + user_id: expect.any(String), + username: 'the-hill', + email: 'h@hillaryclinton.com', + salt: 'd63a897a76ece8b9a503913db68c95af', + questions: 0, + answers: 0, + points: 1 + } as PublicUser + } + } + }, + { + subject: 'verify user the-hill can be fetched', + handler: api.v1.usersUsername, + params: { username: 'the-hill' }, + method: 'GET', + response: { + status: 200, + json: (_json, { getResultAt }) => { + return { user: getResultAt('user-hillary', 'user') }; + } + } + }, + { + subject: 'verify the-hill appears in LIFO list of all users', + handler: api.v1.users, + method: 'GET', + response: { + status: 200, + json: (_json, { getResultAt }) => { + return { + users: [ + getResultAt('user-hillary', 'user'), + ...dummyAppData.users.slice().reverse().map(toPublicUser) + ] + }; + } + } + }, + { + subject: 'update the-hill', + handler: api.v1.usersUsername, + method: 'PATCH', + params: { username: 'the-hill' }, + body: { + salt: '2a9e8128c6641c2fe7642abd14b09e14', + key: '8df1042284e5cc64ff722e473bba9deebb7ef06927c96a004faa1f4dc60f3b1c01fc42612f495cd91ac7041060860b4626e6a5af04b6e31104e6f896b4e3d153', + points: 1000 + } as PatchUser, + response: { status: 200 } + }, + { + id: 'updated-user-hillary', + subject: 'verify the-hill was updated', + handler: api.v1.usersUsername, + params: { username: 'the-hill' }, + method: 'GET', + response: { + status: 200, + json: { user: expect.objectContaining({ points: 1000 }) } + } + }, + { + subject: 'authenticate the-hill', + handler: api.v1.usersUsernameAuth, + method: 'POST', + params: { username: 'the-hill' }, + body: { + key: '8df1042284e5cc64ff722e473bba9deebb7ef06927c96a004faa1f4dc60f3b1c01fc42612f495cd91ac7041060860b4626e6a5af04b6e31104e6f896b4e3d153' + }, + response: { status: 200 } + }, + { + subject: 'authenticate the-hill case-insensitively', + handler: api.v1.usersUsernameAuth, + method: 'POST', + params: { username: 'the-hill' }, + body: { + key: '8DF1042284E5CC64FF722E473BBA9DEEBB7EF06927C96A004FAA1F4DC60F3B1C01FC42612F495CD91AC7041060860B4626E6A5AF04B6E31104E6F896B4E3D153' + }, + response: { status: 200 } + }, + { + subject: 'attempt to authenticate the-hill with bad key', + handler: api.v1.usersUsernameAuth, + method: 'POST', + params: { username: 'the-hill' }, + body: { key: 'x' }, + response: { status: 403 } + }, + { + subject: 'attempt to delete a non-existent user', + handler: api.v1.usersUsername, + method: 'DELETE', + params: { username: 'does-not-exist' }, + response: { status: 404 } + }, + { + subject: `delete ${dummyAppData.users[0].username}`, + handler: api.v1.usersUsername, + method: 'DELETE', + params: { username: dummyAppData.users[0].username }, + response: { status: 200 } + }, + { + subject: `verify ${dummyAppData.users[0].username} is not present in LIFO list of all users`, + handler: api.v1.users, + method: 'GET', + response: { + status: 200, + json: (_json, { getResultAt }) => { + return { + users: [ + getResultAt('updated-user-hillary', 'user'), + ...dummyAppData.users.slice(1).reverse().map(toPublicUser) + ] + }; + } + } + }, + { + subject: `verify ${dummyAppData.users[0].username} cannot be fetched`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[0].username }, + method: 'GET', + response: { status: 404 } + }, + { + id: 'user-obama', + subject: 'create new user "baracko"', + handler: api.v1.users, + method: 'POST', + body: { + username: 'baracko', + email: 'o@barackobama.com', + key: 'ac4ab7f9f19fb198a0e1ec3c3970d8b8a2a47e19127a988c02299807210927dfb915d66af69f4a8b53c7610b31604eed6ebe0273a9dc73831892a86250082ebf', + salt: 'e1a3593dbf0ff964292398251f3b47ad' + } as NewUser, + response: { + status: 200, + json: { + user: { + user_id: expect.any(String), + username: 'baracko', + email: 'o@barackobama.com', + salt: 'e1a3593dbf0ff964292398251f3b47ad', + questions: 0, + answers: 0, + points: 1 + } as PublicUser + } + } + }, + { + subject: 'attempt to create another user named "baracko"', + handler: api.v1.users, + method: 'POST', + body: { + username: 'baracko', + email: 'xyz@abc.def', + key: 'ac4ab7f9f19fb198a0e1ec3c3970d8b8a2a47e19127a988c02299807210927dfb915d66af69f4a8b53c7610b31604eed6ebe0273a9dc73831892a86250082ebf', + salt: 'e1a3593dbf0ff964292398251f3b47ad' + } as NewUser, + response: { status: 400 } + }, + { + subject: 'attempt to create a user with a duplicate email', + handler: api.v1.users, + method: 'POST', + body: { + username: 'xyz-abc', + email: 'o@barackobama.com', + key: 'ac4ab7f9f19fb198a0e1ec3c3970d8b8a2a47e19127a988c02299807210927dfb915d66af69f4a8b53c7610b31604eed6ebe0273a9dc73831892a86250082ebf', + salt: 'e1a3593dbf0ff964292398251f3b47ad' + } as NewUser, + response: { status: 400 } + }, + { + subject: 'verify baracko can be fetched', + handler: api.v1.usersUsername, + params: { username: 'baracko' }, + method: 'GET', + response: { + status: 200, + json: (_json, { getResultAt }) => { + return { user: getResultAt('user-obama', 'user') }; + } + } + }, + { + subject: 'verify baracko appears in LIFO list of all users', + handler: api.v1.users, + method: 'GET', + response: { + status: 200, + json: (_json, { getResultAt }) => { + return { + users: [ + getResultAt('user-obama', 'user'), + getResultAt('updated-user-hillary', 'user'), + ...dummyAppData.users.slice(1).reverse().map(toPublicUser) + ] + }; + } + } + }, + { + subject: 'attempt to update baracko with bad salt', + handler: api.v1.usersUsername, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + salt: '2', + key: 'ac4ab7f9f19fb198a0e1ec3c3970d8b8a2a47e19127a988c02299807210927dfb915d66af69f4a8b53c7610b31604eed6ebe0273a9dc73831892a86250082ebf' + } as PatchUser, + response: { status: 400 } + }, + { + subject: 'attempt to update baracko with bad key', + handler: api.v1.usersUsername, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + salt: '2a9e8128c6641c2fe7642abd14b09e14', + key: 'a' + } as PatchUser, + response: { status: 400 } + }, + { + subject: 'attempt to update baracko with no key', + handler: api.v1.usersUsername, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + salt: '2a9e8128c6641c2fe7642abd14b09e14', + key: undefined + } as PatchUser, + response: { status: 400 } + }, + { + id: 'updated-user-obama', + subject: 'verify baracko was updated', + handler: api.v1.usersUsername, + params: { username: 'baracko' }, + method: 'GET', + response: { status: 200 } + }, + { + subject: 'authenticate baracko', + handler: api.v1.usersUsernameAuth, + method: 'POST', + params: { username: 'baracko' }, + body: { + key: 'ac4ab7f9f19fb198a0e1ec3c3970d8b8a2a47e19127a988c02299807210927dfb915d66af69f4a8b53c7610b31604eed6ebe0273a9dc73831892a86250082ebf' + }, + response: { status: 200 } + }, + { + subject: 'attempt to authenticate baracko with no key', + handler: api.v1.usersUsernameAuth, + method: 'POST', + params: { username: 'baracko' }, + body: { key: undefined }, + response: { status: 403 } + }, + { + subject: 'attempt to fetch all users in LIFO order using bad after_id', + handler: api.v1.users, + method: 'GET', + params: { after: 'bad-id' }, + response: { status: 400 } + }, + { + subject: 'attempt to fetch all users in LIFO order using non-existent after_id', + handler: api.v1.users, + method: 'GET', + params: { after: new ObjectId().toString() }, + response: { status: 404 } + }, + { + subject: `attempt to update deleted ${dummyAppData.users[0].username}`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[0].username }, + method: 'PATCH', + body: { email: 'some@new.email' }, + response: { status: 404 } + }, + { + subject: `attempt to update ${dummyAppData.users[2].username} using a bad email`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[2].username }, + method: 'PATCH', + body: { email: 'bad email address' }, + response: { status: 400 } + }, + { + subject: `attempt to update ${dummyAppData.users[2].username} using a too-long email`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[2].username }, + method: 'PATCH', + body: { email: 'x'.repeat(getEnv().MAX_USER_EMAIL_LENGTH) + '@aol.com' }, + response: { status: 400 } + }, + { + subject: `attempt to update ${dummyAppData.users[2].username} using a short non-hex salt`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[2].username }, + method: 'PATCH', + body: { salt: 'xyz' }, + response: { status: 400 } + }, + { + subject: `attempt to update ${dummyAppData.users[2].username} using a short non-hex key`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[2].username }, + method: 'PATCH', + body: { key: 'xyz' }, + response: { status: 400 } + }, + { + subject: `"update" ${dummyAppData.users[2].username} with a no-op`, + handler: api.v1.usersUsername, + params: { username: dummyAppData.users[2].username }, + method: 'PATCH', + body: {}, + response: { status: 200 } + }, + { + subject: 'fetch all users in LIFO order using pagination', + handler: api.v1.users, + method: 'GET', + params: ({ getResultAt }) => { + return { after: getResultAt('updated-user-hillary', 'user.user_id') }; + }, + response: { + status: 200, + json: { users: dummyAppData.users.slice(1).reverse().map(toPublicUser) } + } + }, + { + subject: "fetch all of baracko's answers", + handler: api.v1.usersUsernameAnswers, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { answers: [] } + } + }, + { + subject: "fetch all of baracko's questions", + handler: api.v1.usersUsernameQuestions, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { questions: [] } + } + }, + + // * Incrementing/updating user points + { + subject: "increment baracko's points", + handler: api.v1.usersUsernamePoints, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + operation: 'increment', + amount: 1000 + }, + response: { status: 200 } + }, + { + subject: "verify baracko's points #1", + handler: api.v1.usersUsername, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { user: expect.objectContaining({ points: 1001 }) } + } + }, + { + subject: "increment baracko's points again", + handler: api.v1.usersUsernamePoints, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + operation: 'increment', + amount: 1000 + }, + response: { status: 200 } + }, + { + subject: "verify baracko's points #2", + handler: api.v1.usersUsername, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { user: expect.objectContaining({ points: 2001 }) } + } + }, + { + subject: "decrement baracko's points", + handler: api.v1.usersUsernamePoints, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + operation: 'decrement', + amount: 100 + }, + response: { status: 200 } + }, + { + subject: "verify baracko's points #3", + handler: api.v1.usersUsername, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { user: expect.objectContaining({ points: 1901 }) } + } + }, + { + subject: "decrement baracko's points greatly", + handler: api.v1.usersUsernamePoints, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + operation: 'decrement', + amount: 10000 + }, + response: { status: 200 } + }, + { + subject: "verify baracko's points #4", + handler: api.v1.usersUsername, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { user: expect.objectContaining({ points: -8099 }) } + } + }, + { + subject: "set baracko's points", + handler: api.v1.usersUsername, + method: 'PATCH', + params: { username: 'baracko' }, + body: { + points: 0 + }, + response: { status: 200 } + }, + { + subject: "verify baracko's points #5", + handler: api.v1.usersUsername, + method: 'GET', + params: { username: 'baracko' }, + response: { + status: 200, + json: { user: expect.objectContaining({ points: 0 }) } + } + }, + + // * Creating new questions, answers, and comments + { + subject: 'create question as baracko' + }, + { + subject: "verify new question is in baracko's questions" + }, + { + subject: 'create answer to own question as baracko' + }, + { + subject: "verify new answer is in baracko's answers" + }, + { + subject: "verify new answer is in question's answers" + }, + { + subject: 'create comment to own question as baracko' + }, + { + subject: "verify new comment is in question's comments" + }, + { + subject: 'create comment to own answer as baracko' + }, + { + subject: 'create second comment to own answer as baracko' + }, + { + subject: "verify new comments are in answer's comments" + }, + + // * Voting on own questions, answers, and comments + { + subject: 'attempt to upvote new question as baracko' + }, + { + subject: 'attempt to upvote new answer as baracko' + }, + { + subject: 'attempt to downvote new question as baracko' + }, + { + subject: 'attempt to downvote new answer as baracko' + }, + { + subject: 'attempt to upvote new question comment as baracko' + }, + { + subject: 'attempt to upvote new answer comment as baracko' + }, + { + subject: 'attempt to downvote new question comment as baracko' + }, + { + subject: 'attempt to downvote new answer comment as baracko' + }, + + // * Deleting comments + { + subject: 'delete new question comment' + }, + { + subject: "verify new question's comments no longer include deleted comment" + }, + { + subject: 'delete new answer comment' + }, + { + subject: "verify new answer's comments no longer include deleted comment" + }, + { + subject: 'attempt to delete already-deleted comment' + }, + + // * Upvoting/downvoting on questions + { + subject: "upvote baracko's question as the-hill" + }, + { + subject: "verify the metadata of baracko's question #1" + }, + { + subject: "verify the-hill's vote on baracko's question #1" + }, + { + subject: "attempt to upvote baracko's question as the-hill again" + }, + { + subject: "attempt to downvote baracko's question as the-hill" + }, + { + subject: "undo upvote on baracko's question as the-hill" + }, + { + subject: "verify the metadata of baracko's question #2" + }, + { + subject: "verify the-hill's vote on baracko's question #2" + }, + { + subject: "attempt to undo upvote on baracko's question as the-hill again" + }, + { + subject: "downvote baracko's question as the-hill" + }, + { + subject: "verify the metadata of baracko's question #3" + }, + { + subject: "verify the-hill's vote on baracko's question #3" + }, + { + subject: "attempt to downvote baracko's question as the-hill again" + }, + { + subject: "attempt to upvote baracko's question as the-hill" + }, + { + subject: "undo downvote on baracko's question as the-hill" + }, + { + subject: "verify the metadata of baracko's question #4" + }, + { + subject: "verify the-hill's vote on baracko's question #4" + }, + { + subject: "attempt to undo downvote on baracko's question as the-hill again" + }, + { + subject: "re-upvote baracko's question as the-hill" + }, + + // * Upvoting/downvoting on answers + { + subject: "upvote baracko's answer as the-hill" + }, + { + subject: "verify the metadata of baracko's answer #1" + }, + { + subject: "verify the-hill's vote on baracko's answer #1" + }, + { + subject: "attempt to upvote baracko's answer as the-hill again" + }, + { + subject: "attempt to downvote baracko's answer as the-hill" + }, + { + subject: "undo upvote on baracko's answer as the-hill" + }, + { + subject: "verify the metadata of baracko's answer #2" + }, + { + subject: "verify the-hill's vote on baracko's answer #2" + }, + { + subject: "attempt to undo upvote on baracko's answer as the-hill again" + }, + { + subject: "downvote baracko's answer as the-hill" + }, + { + subject: "verify the metadata of baracko's answer #3" + }, + { + subject: "verify the-hill's vote on baracko's answer #3" + }, + { + subject: "attempt to downvote baracko's answer as the-hill again" + }, + { + subject: "attempt to upvote baracko's answer as the-hill" + }, + { + subject: "undo downvote on baracko's answer as the-hill" + }, + { + subject: "verify the metadata of baracko's answer #4" + }, + { + subject: "verify the-hill's vote on baracko's answer #4" + }, + { + subject: "attempt to undo downvote on baracko's answer as the-hill again" + }, + { + subject: "re-upvote baracko's answer as the-hill" + }, + + // * Upvoting/downvoting on question comments + { + subject: "upvote baracko's question comment as the-hill" + }, + { + subject: "verify the metadata of baracko's question comment #1" + }, + { + subject: "verify the-hill's vote on baracko's question comment #1" + }, + { + subject: "attempt to upvote baracko's question comment as the-hill again" + }, + { + subject: "attempt to downvote baracko's question comment as the-hill" + }, + { + subject: "undo upvote on baracko's question comment as the-hill" + }, + { + subject: "verify the metadata of baracko's question comment #2" + }, + { + subject: "verify the-hill's vote on baracko's question comment #2" + }, + { + subject: + "attempt to undo upvote on baracko's question comment as the-hill again" + }, + { + subject: "downvote baracko's question comment as the-hill" + }, + { + subject: "verify the metadata of baracko's question comment #3" + }, + { + subject: "verify the-hill's vote on baracko's question comment #3" + }, + { + subject: "attempt to downvote baracko's question comment as the-hill again" + }, + { + subject: "attempt to upvote baracko's question comment as the-hill" + }, + { + subject: "undo downvote on baracko's question comment as the-hill" + }, + { + subject: "verify the metadata of baracko's question comment #4" + }, + { + subject: "verify the-hill's vote on baracko's question comment #4" + }, + { + subject: + "attempt to undo downvote on baracko's question comment as the-hill again" + }, + { + subject: "re-upvote baracko's question comment as the-hill" + }, + + // * Upvoting/downvoting on answer comments + { + subject: "upvote baracko's answer comment as the-hill" + }, + { + subject: "verify the metadata of baracko's answer comment #1" + }, + { + subject: "verify the-hill's vote on baracko's answer comment #1" + }, + { + subject: "attempt to upvote baracko's answer comment as the-hill again" + }, + { + subject: "attempt to downvote baracko's answer comment as the-hill" + }, + { + subject: "undo upvote on baracko's answer comment as the-hill" + }, + { + subject: "verify the metadata of baracko's answer comment #2" + }, + { + subject: "verify the-hill's vote on baracko's answer comment #2" + }, + { + subject: "attempt to undo upvote on baracko's answer comment as the-hill again" + }, + { + subject: "downvote baracko's answer comment as the-hill" + }, + { + subject: "verify the metadata of baracko's answer comment #3" + }, + { + subject: "verify the-hill's vote on baracko's answer comment #3" + }, + { + subject: "attempt to downvote baracko's answer comment as the-hill again" + }, + { + subject: "attempt to upvote baracko's answer comment as the-hill" + }, + { + subject: "undo downvote on baracko's answer comment as the-hill" + }, + { + subject: "verify the metadata of baracko's answer comment #4" + }, + { + subject: "verify the-hill's vote on baracko's answer comment #4" + }, + { + subject: + "attempt to undo downvote on baracko's answer comment as the-hill again" + }, + { + subject: "re-upvote baracko's answer comment as the-hill" + }, + + // * Answer duplication + { + subject: "create answer to baracko's question as the-hill" + }, + { + subject: 'attempt to upvote new answer as the-hill' + }, + { + subject: 'upvote new answer as baracko' + }, + { + subject: "verify new answer is in baracko's question's answers" + }, + { + subject: "attempt to create another answer to baracko's question as the-hill" + }, + + // * View manipulation + { + subject: "increment the views of baracko's question" + }, + { + subject: "verify baracko's question has 1 views" + }, + { + subject: "set views of baracko's question to 0" + }, + { + subject: "verify baracko's question has 0 views" + }, + + // * Searching + { + subject: 'verify parameter-less search returns latest questions in LIFO order' + }, + { + subject: 'search for questions sorted by the highest upvotes' + }, + { + subject: 'search for unanswered questions sorted by uvc' + }, + { + subject: 'search for questions without an accepted answer sorted by uvac' + }, + { + subject: 'search for questions matching a title case-insensitively' + }, + { + subject: + 'search for questions regex-matching a text fragment case-insensitively' + }, + { + subject: 'search for questions matching a specific creator' + }, + { + subject: 'search for questions created after a specific point in time' + }, + { + subject: 'search for questions created between two specific points in time' + }, + { + subject: + 'search for questions that regex-match, match, and complex match simultaneously' + // TODO: search for questions that regex-match a text and a title + // TODO: fragment, match a specific creator, and were created between two + // TODO: specific points in time + }, + { + subject: + 'verify "after" parameter returns expected questions given previous result' + // TODO: search for questions that regex-match a text and a title + // TODO: fragment, match a specific creator, and were created between two + // TODO: specific points in time + }, + + // * Limits and checks + { + subject: 'attempt to create a question with an empty title' + }, + { + subject: 'attempt to create a question with a title that is too large' + }, + { + subject: 'attempt to create a question with an empty body' + }, + { + subject: 'attempt to create a question with a body that is too large' + }, + { + subject: 'attempt to create an answer with an empty body' + }, + { + subject: 'attempt to create an answer with a body that is too large' + }, + { + subject: 'attempt to create an empty comment on a question' + }, + { + subject: 'attempt to create a comment on a question that is too large' + }, + { + subject: 'attempt to create an empty comment on an answer' + }, + { + subject: 'attempt to create a comment on an answer that is too large' + }, + { + subject: 'attempt to create a message with an empty body' + }, + { + subject: 'attempt to create a message with a body that is too large' + }, + { + subject: 'attempt to create a message with an empty subject' + }, + { + subject: 'attempt to create a message with a subject that is too large' + }, + { + subject: 'attempt to create a message with a non-existent sender' + }, + { + subject: 'attempt to create a message with a non-existent receiver' + }, + + // * Accepting an answer + { + subject: "verify baracko's question does not have an accepted answer" + }, + { + subject: "accept an answer on baracko's question" + }, + { + subject: "verify baracko's question has an accepted answer" + }, + { + subject: "verify accepted answer's metadata" + }, + { + subject: "attempt to accept another answer on baracko's question" + }, + + // * Update question status + { + subject: `set baracko's question's status to "protected"` + }, + { + subject: `verify baracko's question is protected` + }, + + // * Transacting mail + { + subject: 'verify the-hill has an empty mailbox' + }, + { + subject: 'send a message from baracko to the-hill' + }, + { + subject: 'send another message from baracko to the-hill' + }, + { + subject: 'send a message from the-hill to baracko' + }, + { + subject: 'verify baracko sees expected messages in LIFO order' + }, + { + subject: 'verify the-hill sees expected messages in LIFO order' + }, + + // * Updating question and answer title/body + { + subject: "update baracko's question's title and body" + }, + { + subject: "verify baracko's question's has updated title and body" + }, + { + subject: "update baracko's answer's body" + }, + { + subject: "verify baracko's answer's has updated body" + } + ]; + + // TODO: XXX: ability to specify "depends" via index or name/id + + const willSkipFixture = (fixture: typeof fixtures[number]) => { + const shouldSkip = + !fixture.subject || + !fixture.handler || + !fixture.method || + (!fixture.invisible && + (!fixture.response || + !['number', 'function'].includes(typeof fixture.response.status))); + + return shouldSkip; + }; + + const filteredFixtures = fixtures.filter( + (fixture, ndx): fixture is TestFixture => { + const displayIndex = ndx + 1; + + if (runOnly && !runOnly.includes(displayIndex)) { + return false; + } + + (fixture as TestFixture).displayIndex = !runOnly + ? displayIndex + : runOnly.shift() ?? + toss(new GuruMeditationError('ran out of RUN_ONLY indices')); + + return true; + } + ); + + // TODO: XXX: add ability to capture/suppress output via fixture option (even better: selectively use mock plugins like withMockEnv and withMockOutput via config options) + + // TODO: XXX: with @xunnamius/fable, have an "every X" type construct (the below is "every reqPerContrived") + // TODO: XXX: also allow middleware + // TODO: XXX: also custom props for fixtures + + const reqPerContrived = getEnv().REQUESTS_PER_CONTRIVED_ERROR; + + if (reqPerContrived) { + for (let i = 0, noSkipCount = 0; i < filteredFixtures.length; i++) { + if (!willSkipFixture(filteredFixtures[i])) { + if (noSkipCount++ % reqPerContrived == 0) { + filteredFixtures.splice(i, 0, { + displayIndex: -1, + subject: 'handle contrived', + handler: api.v1.users, + method: 'POST', + body: {}, + response: { + status: 555, + json: { error: expect.stringContaining('contrived') } + } + }); + } + } + } + } + + return filteredFixtures; +} diff --git a/test/pages/unit-app.test.tsx b/test/pages/unit-app.test.tsx new file mode 100644 index 0000000..b0e2e55 --- /dev/null +++ b/test/pages/unit-app.test.tsx @@ -0,0 +1,19 @@ +/** + * @jest-environment jsdom + */ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import App from 'universe/pages/_app'; +import { AppProps } from 'next/app'; + +it('renders without crashing', async () => { + expect.hasAssertions(); + + render( +
Hello, world!
} as unknown as AppProps)} + /> + ); + + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); +}); diff --git a/test/pages/unit-index.test.tsx b/test/pages/unit-index.test.tsx new file mode 100644 index 0000000..ae8e6e3 --- /dev/null +++ b/test/pages/unit-index.test.tsx @@ -0,0 +1,18 @@ +/** + * @jest-environment jsdom + */ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import IndexPage, { getServerSideProps } from 'universe/pages/index'; + +it('renders without crashing', async () => { + expect.hasAssertions(); + + const serverSideProps = (await getServerSideProps()).props; + + render(); + expect(screen.getByText('no')).toBeInTheDocument(); + + render(); + expect(screen.getByText('yes')).toBeInTheDocument(); +}); diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000..4dd924f --- /dev/null +++ b/test/setup.ts @@ -0,0 +1,1198 @@ +import { debugNamespace } from 'universe/constants'; +import { name as pkgName, version as pkgVersion } from 'package'; +import { verifyEnvironment } from '../expect-env'; +import { TrialError, GuruMeditationError, makeNamedError } from 'universe/error'; +import { tmpdir } from 'os'; +import { promises as fs } from 'fs'; +import { resolve } from 'path'; +import { toss } from 'toss-expression'; +import { defaultConfig } from 'universe/backend/api'; +import execa from 'execa'; +import uniqueFilename from 'unique-filename'; +import { debugFactory } from 'multiverse/debug-extended'; +import gitFactory from 'simple-git'; +// ? See: https://github.com/jest-community/jest-extended#setup +import 'jest-extended/all'; +import 'jest-extended'; +import '@testing-library/jest-dom/extend-expect'; + +import type { Debugger } from 'multiverse/debug-extended'; +import type { SimpleGit } from 'simple-git'; +import type { + NextApiHandler, + NextApiRequest, + NextApiResponse, + PageConfig +} from 'next'; +import type { Promisable } from 'type-fest'; + +const { writeFile, access: accessFile } = fs; +const debug = debugFactory(`${debugNamespace}:jest-setup`); + +debug(`pkgName: "${pkgName}"`); +debug(`pkgVersion: "${pkgVersion}"`); + +let env = {}; + +try { + require('fs').accessSync('.env'); + env = require('dotenv').config().parsed; + debug('.env vars: %O', env); +} catch (e) { + debug(`.env support disabled; reason: ${e}`); +} + +verifyEnvironment(); + +/** + * A mock Next.js API handler that sends an empty object Reponse with a 200 + * status code. + */ +export const noopHandler = async (_req: NextApiRequest, res: NextApiResponse) => { + res.status(200).send({}); +}; + +/** + * This function wraps mock Next.js API handler functions so that they provide + * the default (or a custom) API configuration object. + */ +export const wrapHandler = (handler: NextApiHandler, config?: PageConfig) => { + const api = async (req: NextApiRequest, res: NextApiResponse) => handler(req, res); + api.config = config || defaultConfig; + return api; +}; + +/** + * Contains the expected shapes of the gzipped tar archives under + * `test/fixtures`. + */ +export const expectedEntries = { + monorepo: [ + { + headers: expect.objectContaining({ name: 'monorepo/' }), + data: '' + }, + { + headers: expect.objectContaining({ name: 'monorepo/package.json' }), + data: + '{\n' + + ' "name": "dummy-monorepo",\n' + + ' "workspaces": [\n' + + ' "packages/pkg-1",\n' + + ' "packages/pkg-2"\n' + + ' ]\n' + + '}\n' + }, + { + headers: expect.objectContaining({ name: 'monorepo/packages/' }), + data: '' + }, + { + headers: expect.objectContaining({ name: 'monorepo/packages/pkg-1/' }), + data: '' + }, + { + headers: expect.objectContaining({ + name: 'monorepo/packages/pkg-1/package.json' + }), + data: + '{\n' + + ' "name": "dummy-monorepo-pkg-2",\n' + + ' "version": "1.0.0",\n' + + ' "main": "index.js"\n' + + '}\n' + }, + { + headers: expect.objectContaining({ name: 'monorepo/packages/pkg-1/index.js' }), + data: "console.log('dummy monorepo pkg-1 test');\n" + }, + { + headers: expect.objectContaining({ name: 'monorepo/packages/pkg-2/' }), + data: '' + }, + { + headers: expect.objectContaining({ + name: 'monorepo/packages/pkg-2/package.json' + }), + data: + '{\n' + + ' "name": "dummy-monorepo-pkg-2",\n' + + ' "version": "1.0.0",\n' + + ' "main": "index.js"\n' + + '}\n' + }, + { + headers: expect.objectContaining({ name: 'monorepo/packages/pkg-2/index.js' }), + data: "console.log('dummy monorepo pkg-2 test');\n" + } + ], + pkg1: [ + { + headers: expect.objectContaining({ name: 'pkg-1/' }), + data: '' + }, + { + headers: expect.objectContaining({ name: 'pkg-1/package.json' }), + data: + '{\n' + + // ? Oops... too late now + ' "name": "dummy-monorepo-pkg-2",\n' + + ' "version": "1.0.0",\n' + + ' "main": "index.js"\n' + + '}\n' + }, + { + headers: expect.objectContaining({ name: 'pkg-1/index.js' }), + data: "console.log('dummy monorepo pkg-1 test');\n" + } + ], + pkg2: [ + { + headers: expect.objectContaining({ name: 'pkg-2/' }), + data: '' + }, + { + headers: expect.objectContaining({ name: 'pkg-2/package.json' }), + data: + '{\n' + + ' "name": "dummy-monorepo-pkg-2",\n' + + ' "version": "1.0.0",\n' + + ' "main": "index.js"\n' + + '}\n' + }, + { + headers: expect.objectContaining({ name: 'pkg-2/index.js' }), + data: "console.log('dummy monorepo pkg-2 test');\n" + } + ] +}; + +// TODO: XXX: add these brand new tools to where they're supposed to be! + +export class FactoryExhaustionError extends TrialError {} +export function itemFactory(testItems: T[]) { + const nextItem = Object.assign( + () => { + const next = nextItem['$iter'].next() as IteratorResult; + if (next.done) { + throw new FactoryExhaustionError( + 'item factory iterator exhausted unexpectedly' + ); + } else return next.value; + }, + { + items: testItems, + count: testItems.length, + $iter: testItems.values(), + *[Symbol.iterator]() { + while (true) { + try { + yield nextItem(); + } catch (e) { + if (e instanceof FactoryExhaustionError) return; + else throw e; + } + } + }, + async *[Symbol.asyncIterator]() { + while (true) { + try { + // eslint-disable-next-line no-await-in-loop + yield await nextItem(); + } catch (e) { + if (e instanceof FactoryExhaustionError) return; + else throw e; + } + } + } + } + ); + + Object.defineProperty(nextItem, 'length', { + configurable: false, + enumerable: false, + set: () => + toss(new SyntaxError('did you mean to use ::count instead of ::length?')), + get: () => + toss(new SyntaxError('did you mean to use ::count instead of ::length?')) + }); + + return nextItem; +} + +// TODO: XXX: make this into a separate (mock-argv) package (along w/ the below) +export type MockArgvOptions = { + /** + * By default, the first two elements in `process.argv` are preserved. Setting + * `replace` to `true` will cause the entire process.argv array to be replaced + * @default false + */ + replace?: boolean; +}; + +// TODO: XXX: make this into a separate (mock-env) package (along w/ the below) +export type MockEnvOptions = { + /** + * By default, the `process.env` object is emptied and re-hydrated with + * `newEnv`. Setting `replace` to `false` will cause `newEnv` to be appended + * instead + * @default true + */ + replace?: boolean; +}; + +// TODO: XXX: make this into a separate (mock-argv) package +export async function withMockedArgv( + fn: () => unknown, + newArgv: string[], + options: MockArgvOptions = { replace: false } +) { + // ? Take care to preserve the original argv array reference in memory + const prevArgv = process.argv.splice(options?.replace ? 0 : 2, process.argv.length); + process.argv.push(...newArgv); + + try { + await fn(); + } finally { + process.argv.splice(options?.replace ? 0 : 2, process.argv.length); + process.argv.push(...prevArgv); + } +} + +// TODO: XXX: make this into a separate (mock-argv) package (along w/ the above) +export function mockArgvFactory( + newArgv: typeof process.argv, + options: MockArgvOptions = { replace: false } +) { + const factoryNewArgv = newArgv; + const factoryOptions = options; + + return (fn: () => unknown, newArgv?: string[], options?: MockArgvOptions) => { + return withMockedArgv( + fn, + [...factoryNewArgv, ...(newArgv || [])], + options || factoryOptions + ); + }; +} + +// TODO: XXX: make this into a separate (mock-env) package +export async function withMockedEnv( + fn: () => unknown, + newEnv: Record, + options: MockEnvOptions = { replace: true } +) { + const prevEnv = { ...process.env }; + const clearEnv = () => + Object.getOwnPropertyNames(process.env).forEach( + (prop) => delete process.env[prop] + ); + + // ? Take care to preserve the original env object reference in memory + if (options.replace) clearEnv(); + Object.assign(process.env, newEnv); + + try { + await fn(); + } finally { + clearEnv(); + Object.assign(process.env, prevEnv); + } +} + +// TODO: XXX: make this into a separate (mock-env) package (along w/ the above) +export function mockEnvFactory( + newEnv: Record, + options: MockEnvOptions = { replace: true } +) { + const factoryNewEnv = newEnv; + const factoryOptions = options; + + return ( + fn: () => unknown, + newEnv?: Record, + options?: MockEnvOptions + ) => { + const env = { ...factoryNewEnv, ...(newEnv || {}) }; + const opts = { ...options, ...factoryOptions }; + + // ? The process.env proxy casts undefineds to strings, so we'll delete them + Object.keys(env).forEach((key) => env[key] === undefined && delete env[key]); + return withMockedEnv(fn, env as Record, opts); + }; +} + +// TODO: XXX: make this into a separate (jest-isolated-import) package +export async function withDebugEnabled(fn: () => Promisable) { + const namespaces = debugFactory.disable(); + debugFactory.enable('*'); + + try { + await fn(); + } finally { + debugFactory.disable(); + debugFactory.enable(namespaces); + } +} + +// TODO: XXX: make this into a separate (jest-isolated-import) package + +/** + * Performs a module import as if it were being imported for the first time. + * + * Note that this function breaks the "require caching" expectation of Node.js + * modules. Problems can arise, for example, when closing an app-wide database + * connection in your test cleanup phase and expecting it to close for the + * isolated module too. In this case, the isolated module has its own isolated + * "app-wide" connection that would not actually be closed and could cause your + * test to hang unexpectedly, even when all tests pass. + */ +export function isolatedImport({ + path, + useDefault +}: { + /** + * Path to the module to import. Module resolution is handled by `require`. + */ + path: string; + /** + * By default, only if `module.__esModule === true`, the default export will + * be returned instead. Use `useDefault` to override this behavior in either + * direction. + */ + useDefault?: boolean; +}) { + let pkg: T | undefined; + + // ? Cache-busting + jest.isolateModules(() => { + debug( + `performing isolated import of ${path}${ + useDefault ? ' (returning default by force)' : '' + }` + ); + + pkg = ((r) => { + return r.default && + (useDefault === true || + (useDefault !== false && r.__esModule && Object.keys(r).length == 1)) + ? r.default + : r; + })(require(path)); + }); + + return pkg as T; +} + +// TODO: XXX: make this into a separate package (along with the above) +export function isolatedImportFactory({ + path, + useDefault +}: { + /** + * Path to the module to import. Module resolution is handled by `require`. + */ + path: string; + /** + * By default, only if `module.__esModule === true`, the default export will + * be returned instead. Use `useDefault` to override this behavior in either + * direction. + */ + useDefault?: boolean; +}) { + return () => isolatedImport({ path: path, useDefault: useDefault }); +} + +// TODO: XXX: make this into a separate package (along with the above) +/** + * While `isolatedImport` performs a module import as if it were being + * imported for the first time, `protectedImport` wraps `isolatedImport` + * with `withMockedExit`. This makes `protectedImport` useful for testing + * IIFE modules such as CLI entry points an externals. + */ +export async function protectedImport({ + path, + useDefault, + expectedExitCode +}: { + /** + * Path to the module to import. Module resolution is handled by `require`. + */ + path: string; + /** + * By default, only if `module.__esModule === true`, the default export will + * be returned instead. Use `useDefault` to override this behavior in either + * direction. + */ + useDefault?: boolean; + /** + * The code that must be passed to process.exit by the imported module. If + * `undefined` (default), then process.exit must not be called. + * + * @default undefined + */ + expectedExitCode?: number | 'non-zero' | undefined; +}) { + let pkg: unknown = undefined; + + await withMockedExit(async ({ exitSpy }) => { + try { + pkg = await isolatedImport({ path: path, useDefault: useDefault }); + } catch (e) { + if (!(e instanceof MockedProcessExit)) { + throw e; + } + } + + if (expect) { + expectedExitCode == 'non-zero' + ? expect(exitSpy).not.toBeCalledWith(0) + : expectedExitCode === undefined + ? expect(exitSpy).not.toBeCalled() + : expect(exitSpy).toBeCalledWith(expectedExitCode); + } else { + debug.warn('"expect" object not found, so exit check was skipped'); + } + }); + + return pkg as T; +} + +// TODO: XXX: make this into a separate package (along with the above) +export function protectedImportFactory({ + path, + useDefault +}: { + /** + * Path to the module to import. Module resolution is handled by `require`. + */ + path: string; + /** + * By default, only if `module.__esModule === true`, the default export will + * be returned instead. Use `useDefault` to override this behavior in either + * direction. + */ + useDefault?: boolean; + /** + * The code that must be passed to process.exit by the imported module. If + * `undefined` (default), then process.exit must not be called. + * + * @default undefined + */ + expectedExitCode?: number | 'non-zero' | undefined; +}) { + return async (params?: { + /** + * The code that must be passed to process.exit by the imported module. If + * `undefined` (default), then process.exit must not be called. + * + * @default undefined + */ + expectedExitCode?: number | 'non-zero' | undefined; + }) => { + return protectedImport({ + path: path, + useDefault: useDefault, + expectedExitCode: params?.expectedExitCode + }); + }; +} + +// TODO: XXX: make this into a separate (mock-exit) package +/** + * Represents a call to process.exit that has been mocked by `withMockedExit`. + */ +export class MockedProcessExit extends Error { + /** + * Represents a call to process.exit that has been mocked by `withMockedExit`. + */ + constructor(public readonly code?: number) { + super(`process.exit was called with code ${code}`); + } +} + +makeNamedError(MockedProcessExit, 'MockedProcessExit'); + +// TODO: XXX: make this into a separate (mock-exit) package +export async function withMockedExit( + fn: (spies: { exitSpy: jest.SpyInstance }) => unknown +) { + const exitSpy = jest.spyOn(process, 'exit').mockImplementation((code) => { + // ? Give a helping hand when debugging, just in case + if (process.env.VSCODE_INSPECTOR_OPTIONS) { + process.emitWarning( + '`process.exit` was called, but the function is mocked. A MockedProcessExit error will be thrown instead.', + 'MockedProcessExitWarning' + ); + } + + throw new MockedProcessExit(code); + }); + + try { + await fn({ exitSpy }); + } catch (e) { + if (!(e instanceof MockedProcessExit)) { + throw e; + } + } finally { + exitSpy.mockRestore(); + } +} + +// TODO: XXX: make this into a separate (mock-output) package +/** + * Any output generated within `fn` will be captured by an output spy instead of + * emitting to the console (stdout/stderr). + * + * However, not that `stdErrSpy` is set to passthrough mode by default. If + * desired, use the `passthrough` option to prevent this. + */ +export async function withMockedOutput( + fn: (spies: { + logSpy: jest.SpyInstance; + warnSpy: jest.SpyInstance; + errorSpy: jest.SpyInstance; + infoSpy: jest.SpyInstance; + stdoutSpy: jest.SpyInstance; + stdErrSpy: jest.SpyInstance; + }) => unknown, + options?: { + /** + * Determine if spies provide mock implementations for output functions, + * thus preventing any output to the terminal, or if spies should + * passthrough output as normal. + * + * Passthrough is disabled for all spies by default (except `stdErrSpy`). + * Pass `true` to enable passthrough for a specific spy. + */ + passthrough?: { + /** + * @default false + */ + logSpy?: boolean; + /** + * @default false + */ + warnSpy?: boolean; + /** + * @default false + */ + errorSpy?: boolean; + /** + * @default false + */ + infoSpy?: boolean; + /** + * @default false + */ + stdoutSpy?: boolean; + /** + * @default true + */ + stdErrSpy?: boolean; + }; + } +) { + const logSpy = jest.spyOn(console, 'log'); + const warnSpy = jest.spyOn(console, 'warn'); + const errorSpy = jest.spyOn(console, 'error'); + const infoSpy = jest.spyOn(console, 'info'); + const stdoutSpy = jest.spyOn(process.stdout, 'write'); + const stdErrSpy = jest.spyOn(process.stderr, 'write'); + + !options?.passthrough?.logSpy && logSpy.mockImplementation(() => undefined); + !options?.passthrough?.warnSpy && warnSpy.mockImplementation(() => undefined); + !options?.passthrough?.errorSpy && errorSpy.mockImplementation(() => undefined); + !options?.passthrough?.infoSpy && infoSpy.mockImplementation(() => undefined); + !options?.passthrough?.stdoutSpy && stdoutSpy.mockImplementation(() => true); + options?.passthrough?.stdErrSpy === false && + stdErrSpy.mockImplementation(() => true); + + try { + await fn({ + logSpy, + warnSpy, + errorSpy, + infoSpy, + stdoutSpy, + stdErrSpy + }); + } finally { + logSpy.mockRestore(); + warnSpy.mockRestore(); + errorSpy.mockRestore(); + infoSpy.mockRestore(); + stdoutSpy.mockRestore(); + stdErrSpy.mockRestore(); + } +} + +export function mockOutputFactory(options: { + /** + * Determine if spies provide mock implementations for output functions, + * thus preventing any output to the terminal, or if spies should + * passthrough output as normal. + * + * Passthrough is disabled for all spies by default (except `stdErrSpy`). + * Pass `true` to enable passthrough for a specific spy. + */ + passthrough?: { + /** + * @default false + */ + logSpy?: boolean; + /** + * @default false + */ + warnSpy?: boolean; + /** + * @default false + */ + errorSpy?: boolean; + /** + * @default false + */ + infoSpy?: boolean; + /** + * @default false + */ + stdoutSpy?: boolean; + /** + * @default true + */ + stdErrSpy?: boolean; + }; +}) { + const factoryOptions = options; + + return async ( + fn: (spies: { + logSpy: jest.SpyInstance; + warnSpy: jest.SpyInstance; + errorSpy: jest.SpyInstance; + infoSpy: jest.SpyInstance; + stdoutSpy: jest.SpyInstance; + stdErrSpy: jest.SpyInstance; + }) => unknown, + options?: { + /** + * Determine if spies provide mock implementations for output functions, + * thus preventing any output to the terminal, or if spies should + * passthrough output as normal. + * + * Passthrough is disabled for all spies by default (except `stdErrSpy`). + * Pass `true` to enable passthrough for a specific spy. + */ + passthrough?: { + /** + * @default false + */ + logSpy?: boolean; + /** + * @default false + */ + warnSpy?: boolean; + /** + * @default false + */ + errorSpy?: boolean; + /** + * @default false + */ + infoSpy?: boolean; + /** + * @default false + */ + stdoutSpy?: boolean; + /** + * @default true + */ + stdErrSpy?: boolean; + }; + } + ) => { + return withMockedOutput(fn, { ...factoryOptions, ...(options || {}) }); + }; +} + +// TODO: XXX: make this into a separate (run) package (along w/ below) +export interface RunOptions extends execa.Options { + /** + * Setting this to `true` rejects the promise instead of resolving it with the error. + * @default false + */ + reject?: boolean; +} + +// TODO: XXX: make this into a separate (run) package +// ! By default, does NOT reject on bad exit code (set reject: true to override) +export async function run(file: string, args?: string[], options?: RunOptions) { + debug(`executing "${file}" with:`); + debug(` args: %O`, args); + debug(` runner options %O`, options); + + const result = await execa(file, args, { reject: false, ...options }); + debug('execution result: %O', result); + + return result; +} + +// TODO: XXX: make this into a separate (run) package (along w/ above) +export function runnerFactory(file: string, args?: string[], options?: RunOptions) { + const factoryArgs = args; + const factoryOptions = options; + + return (args?: string[], options?: RunOptions) => + run(file, args || factoryArgs, { ...factoryOptions, ...options }); +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface FixtureOptions + extends Partial, + Partial, + Partial { + performCleanup: boolean; + use: MockFixture[]; + initialFileContents: { [filePath: string]: string }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface WebpackTestFixtureOptions { + webpackVersion: string; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface GitRepositoryFixtureOptions { + setupGit: (git: SimpleGit) => unknown; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface DummyDirectoriesFixtureOptions { + directoryPaths: string[]; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +// eslint-disable-next-line @typescript-eslint/ban-types +export interface FixtureContext = {}> + extends Partial, + Partial, + Partial { + root: string; + testIdentifier: string; + options: FixtureOptions & CustomOptions; + using: MockFixture[]; + fileContents: { [filePath: string]: string }; + debug: Debugger; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface TestResultProvider { + testResult: { exitCode: number; stdout: string; stderr: string }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface TreeOutputProvider { + treeOutput: string; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface GitProvider { + git: SimpleGit; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +// eslint-disable-next-line @typescript-eslint/ban-types +export type FixtureAction = ( + ctx: Context +) => Promise; + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export type ReturnsString = ( + ctx: Context +) => Promise | string; + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export interface MockFixture { + name: 'root' | 'describe-root' | string | ReturnsString | symbol; + description: string | ReturnsString; + setup?: FixtureAction; + teardown?: FixtureAction; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function rootFixture(): MockFixture { + return { + name: 'root', // ? If first isn't named root, root used automatically + description: (ctx) => + `creating a unique root directory${ + ctx.options.performCleanup && ' (will be deleted after all tests complete)' + }`, + setup: async (ctx) => { + ctx.root = uniqueFilename(tmpdir(), ctx.testIdentifier); + + await run('mkdir', ['-p', ctx.root], { reject: true }); + await run('mkdir', ['-p', 'src'], { cwd: ctx.root, reject: true }); + }, + teardown: async (ctx) => + ctx.options.performCleanup && run('rm', ['-rf', ctx.root], { reject: true }) + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function dummyNpmPackageFixture(): MockFixture { + return { + name: 'dummy-npm-package', + description: 'creating package.json file and node_modules subdirectory', + setup: async (ctx) => { + await Promise.all([ + writeFile( + `${ctx.root}/package.json`, + (ctx.fileContents['package.json'] = + ctx.fileContents['package.json'] || '{"name":"dummy-pkg"}') + ), + run('mkdir', ['-p', 'node_modules'], { cwd: ctx.root, reject: true }) + ]); + + if (pkgName.includes('/')) { + await run('mkdir', ['-p', pkgName.split('/')[0]], { + cwd: `${ctx.root}/node_modules`, + reject: true + }); + } + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function npmLinkSelfFixture(): MockFixture { + return { + name: 'npm-link-self', + description: + 'soft-linking project repo into node_modules to emulate package installation', + setup: async (ctx) => { + await run('ln', ['-s', resolve(`${__dirname}/..`), pkgName], { + cwd: `${ctx.root}/node_modules`, + reject: true + }); + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function webpackTestFixture(): MockFixture { + return { + name: 'webpack-test', + description: 'setting up webpack jest integration test', + setup: async (ctx) => { + if (typeof ctx.options.webpackVersion != 'string') { + throw new GuruMeditationError( + 'invalid or missing options.webpackVersion, expected string' + ); + } + + const indexPath = Object.keys(ctx.fileContents).find((path) => + /^src\/index\.(((c|m)?js)|ts)x?$/.test(path) + ); + + if (!indexPath) + throw new GuruMeditationError( + 'could not find initial contents for src/index file' + ); + + if (!ctx.fileContents['webpack.config.js']) + throw new GuruMeditationError( + 'could not find initial contents for webpack.config.js file' + ); + + await Promise.all([ + writeFile(`${ctx.root}/${indexPath}`, ctx.fileContents[indexPath]), + writeFile( + `${ctx.root}/webpack.config.js`, + ctx.fileContents['webpack.config.js'] + ) + ]); + + ctx.treeOutput = await getTreeOutput(ctx); + + await run( + 'npm', + ['install', `webpack@${ctx.options.webpackVersion}`, 'webpack-cli'], + { + cwd: ctx.root, + reject: true + } + ); + + await run('npx', ['webpack'], { cwd: ctx.root, reject: true }); + + const { exitCode, stdout, stderr } = await run('node', [ + `${ctx.root}/dist/index.js` + ]); + + ctx.testResult = { + exitCode, + stdout, + stderr + }; + } + }; +} + +async function getTreeOutput(ctx: FixtureContext) { + return (await execa('tree', ['-a'], { cwd: ctx.root })).stdout; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function nodeImportTestFixture(): MockFixture { + return { + name: 'node-import-test', + description: 'setting up node import jest integration test', + setup: async (ctx) => { + const indexPath = Object.keys(ctx.fileContents).find((path) => + /^src\/index\.(((c|m)?js)|ts)x?$/.test(path) + ); + + if (!indexPath) + throw new GuruMeditationError( + 'could not find initial contents for src/index file' + ); + + await writeFile(`${ctx.root}/${indexPath}`, ctx.fileContents[indexPath]); + + ctx.treeOutput = await getTreeOutput(ctx); + + const { exitCode, stdout, stderr } = await run( + 'node', + ['--experimental-json-modules', indexPath], + { cwd: ctx.root } + ); + + ctx.testResult = { + exitCode, + stdout, + stderr + }; + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function gitRepositoryFixture(): MockFixture { + return { + name: 'git-repository', + description: 'configuring fixture root to be a git repository', + setup: async (ctx) => { + if (ctx.options.setupGit && typeof ctx.options.setupGit != 'function') { + throw new GuruMeditationError( + 'invalid or missing options.setupGit, expected function' + ); + } + + ctx.git = gitFactory({ baseDir: ctx.root }); + + await (ctx.options.setupGit + ? ctx.options.setupGit(ctx.git) + : ctx.git + .init() + .addConfig('user.name', 'fake-user') + .addConfig('user.email', 'fake@email')); + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function dummyDirectoriesFixture(): MockFixture { + return { + name: 'dummy-directories', + description: 'creating dummy directories under fixture root', + setup: async (ctx) => { + if (!Array.isArray(ctx.options.directoryPaths)) { + throw new GuruMeditationError( + 'invalid or missing options.directoryPaths, expected array' + ); + } + + await Promise.all( + ctx.options.directoryPaths.map((path) => + run('mkdir', ['-p', path], { cwd: ctx.root, reject: true }) + ) + ); + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +export function dummyFilesFixture(): MockFixture { + return { + name: 'dummy-files', + description: 'creating dummy files under fixture root', + setup: async (ctx) => { + await Promise.all( + Object.entries(ctx.fileContents).map(async ([path, contents]) => { + const fullPath = `${ctx.root}/${path}`; + await accessFile(fullPath).then( + () => + debug(`skipped creating dummy file: file already exists at ${path}`), + async () => { + debug(`creating dummy file "${path}" with contents:`); + debug.extend('contents >')(contents); + await writeFile(fullPath, (ctx.fileContents[path] = contents)); + } + ); + }) + ); + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ below) +// ? If a fixture w/ this name isn't included, it's appended +// ! This fixture, when included, is always run even when errors occur! +export function describeRootFixture(): MockFixture { + return { + name: 'describe-root', + description: 'outputting debug information about environment', + setup: async (ctx) => { + ctx.debug('test identifier: %O', ctx.testIdentifier); + ctx.debug('root: %O', ctx.root); + ctx.debug(ctx.treeOutput || (await getTreeOutput(ctx))); + ctx.debug('per-file contents: %O', ctx.fileContents); + } + }; +} + +// TODO: XXX: make this into a separate (mock-fixture) package +export async function withMockedFixture< + // eslint-disable-next-line @typescript-eslint/ban-types + CustomOptions extends Record = {}, + // eslint-disable-next-line @typescript-eslint/ban-types + CustomContext extends Record = {} +>({ + fn, + testIdentifier, + options +}: { + fn: FixtureAction< + FixtureContext< + FixtureOptions & Partial & CustomOptions> + > & + CustomContext + >; + testIdentifier: string; + options?: Partial; +}) { + type CustomizedFixtureOptions = FixtureOptions & + Partial & CustomOptions>; + type CustomizedFixtureContext = FixtureContext & + CustomContext; + type CustomizedMockFixture = MockFixture; + + const testSymbol = Symbol('test'); + const finalOptions = { + performCleanup: true, + use: [] as MockFixture[], + initialFileContents: {}, + ...options + } as CustomizedFixtureOptions & { use: CustomizedMockFixture[] }; + + // TODO: + // @ts-expect-error: TODO: fix this + const ctx = { + root: '', + testIdentifier, + debug, + using: [] as MockFixture[], + options: finalOptions, + fileContents: { ...finalOptions.initialFileContents } + } as CustomizedFixtureContext & { using: CustomizedMockFixture[] }; + + if (finalOptions.use) { + if (finalOptions.use?.[0]?.name != 'root') ctx.using.push(rootFixture()); + ctx.using = [...ctx.using, ...finalOptions.use]; + // ? `describe-root` fixture doesn't have to be the last one, but a fixture + // ? with that name must be included at least once + if (!finalOptions.use.find((f) => f.name == 'describe-root')) + ctx.using.push(describeRootFixture()); + } else ctx.using = [rootFixture(), describeRootFixture()]; + + ctx.using.push({ + name: testSymbol, + description: '', + setup: fn + }); + + let ranDescribe = false; + const cleanupFunctions: NonNullable[] = []; + + const setupDebugger = async (fixture: CustomizedMockFixture, error = false) => { + const toString = async ( + p: CustomizedMockFixture['name'] | CustomizedMockFixture['description'] + ) => + typeof p == 'function' ? p(ctx) : typeof p == 'string' ? p : ':impossible:'; + const name = await toString(fixture.name.toString()); + const desc = await toString(fixture.description); + const dbg = debug.extend(error ? `${name}:` : name); + ctx.debug = dbg; + dbg(desc); + }; + + /*eslint-disable no-await-in-loop */ + try { + for (const mockFixture of ctx.using) { + if (mockFixture.name == testSymbol) { + ctx.debug = debug; + debug('executing test callback'); + } else { + await setupDebugger(mockFixture); + if (mockFixture.teardown) cleanupFunctions.push(mockFixture.teardown); + } + + mockFixture.setup + ? await mockFixture.setup(ctx) + : ctx.debug('(warning: mock fixture has no setup function)'); + + if (mockFixture.name == 'describe-root') ranDescribe = true; + } + } catch (e) { + ctx.debug.extend('')('exception occurred: %O', e); + throw e; + } finally { + if (!ranDescribe) { + const fixture = describeRootFixture(); + await setupDebugger(fixture, true); + await fixture.setup?.(ctx); + } + + ctx.debug = debug.extend(''); + + for (const cfn of cleanupFunctions.reverse()) { + await cfn(ctx).catch((e) => + ctx.debug( + `ignored exception in teardown function: ${ + e?.message || e.toString() || '' + }` + ) + ); + } + } + /*eslint-enable no-await-in-loop */ +} + +// TODO: XXX: make this into a separate (mock-fixture) package (along w/ above) +export function mockFixtureFactory< + // eslint-disable-next-line @typescript-eslint/ban-types + CustomOptions extends Record = {}, + // eslint-disable-next-line @typescript-eslint/ban-types + CustomContext extends Record = {} +>(testIdentifier: string, options?: Partial) { + return ( + fn: FixtureAction< + FixtureContext< + FixtureOptions & Partial & CustomOptions> + > & + CustomContext + > + ) => + withMockedFixture({ fn, testIdentifier, options }); +} diff --git a/tsconfig.docs.json b/tsconfig.docs.json new file mode 100644 index 0000000..119b8be --- /dev/null +++ b/tsconfig.docs.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["types/**/*", "lib/**/*", "src/**/*", "packages/**/*"] +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..e1c9902 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*", "**/.*"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4dd2cf0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,58 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "alwaysStrict": true, + "baseUrl": ".", + "checkJs": false, + // ? Only for Next.js + "jsx": "preserve", + "declaration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "inlineSourceMap": true, + "isolatedModules": true, + "lib": [ + "ESNext", + "DOM", + //"WebWorker.ImportScripts", + "DOM.Iterable" + ], + "module": "esnext", + "moduleResolution": "node", + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "paths": { + // ! If changed, also update these aliases in jest.config.js, + // ! webpack.config.js, and .eslintrc.js + "externals/*": ["external-scripts/*"], + "multiverse/*": ["lib/*"], + "package": ["package.json"], + "pkgverse/*": ["packages/*"], + "testverse/*": ["test/*"], + "types/*": ["types/*"], + "universe/*": ["src/*"], + // ? These are used at various points (including at compile time by + // ? Next.js) to get mongo schema configuration and/or test dummy data. + // ! Must be defined if using @xunnamius/mongo-schema + "configverse/get-schema-config": ["src/backend/db.ts"], + // ! Must be defined if using @xunnamius/mongo-test + "configverse/get-dummy-data": ["test/db.ts"] + }, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext" + }, + "exclude": ["node_modules"], + "include": [ + "types/**/*", + "lib/**/*", + "src/**/*", + "test/**/*", + "external-scripts/**/*", + "packages/**/*" + ] +} diff --git a/tsconfig.lint.json b/tsconfig.lint.json new file mode 100644 index 0000000..569f104 --- /dev/null +++ b/tsconfig.lint.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "types/**/*", + "lib/**/*", + "src/**/*", + "test/**/*", + "external-scripts/**/*", + "packages/**/*" + ] +} diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 0000000..0763785 --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "allowJs": false, + "checkJs": false, + "declaration": true, + "emitDeclarationOnly": true, + "isolatedModules": false, + "noEmit": false, + "outDir": "dist/types", + "rootDir": "./" + }, + "extends": "./tsconfig.json", + "include": ["types/**/*", "lib/**/*", "src/**/*"] +} diff --git a/types/next-env.d.ts b/types/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/types/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/types/next__bundle-analyzer.d.ts b/types/next__bundle-analyzer.d.ts new file mode 100644 index 0000000..f13ba32 --- /dev/null +++ b/types/next__bundle-analyzer.d.ts @@ -0,0 +1 @@ +declare module '@next/bundle-analyzer'; diff --git a/types/random-case.d.ts b/types/random-case.d.ts new file mode 100644 index 0000000..54c2dc7 --- /dev/null +++ b/types/random-case.d.ts @@ -0,0 +1,3 @@ +declare module 'random-case' { + export default function (originalString: string): string; +} diff --git a/types/stackexchange-api.ts b/types/stackexchange-api.ts new file mode 100644 index 0000000..078fa2f --- /dev/null +++ b/types/stackexchange-api.ts @@ -0,0 +1,70 @@ +import { Opaque } from 'type-fest'; + +export type EpochTimeInSeconds = Opaque; +export type SecondsFromNow = Opaque; +export type UriString = Opaque; + +export type StackExchangeQuestion = { + tags: string[]; + owner: StackExchangeUser; + is_answered: boolean; + view_count: number; + protected_date: EpochTimeInSeconds; + accepted_answer_id: number; + answer_count: number; + score: number; + last_activity_date: EpochTimeInSeconds; + creation_date: EpochTimeInSeconds; + last_edit_date: EpochTimeInSeconds; + question_id: number; + content_license: string; + link: UriString; + title: string; + body: string; +}; + +export type StackExchangeAnswer = { + owner: StackExchangeUser; + is_accepted: boolean; + score: number; + last_activity_date: EpochTimeInSeconds; + last_edit_date: EpochTimeInSeconds; + creation_date: EpochTimeInSeconds; + answer_id: number; + question_id: number; + content_license: string; + body: string; +}; + +export type StackExchangeComment = { + owner: StackExchangeUser; + edited: boolean; + score: number; + creation_date: EpochTimeInSeconds; + post_id: number; + comment_id: number; + content_license: string; + body: string; +}; + +export type StackExchangeUser = { + account_id: number; + reputation: number; + user_id: number; + user_type: string; + accept_rate: number; + profile_image: UriString; + display_name: string; + link: UriString; +}; + +export type StackExchangeApiResponse = { + backoff?: SecondsFromNow; + error_id?: number; + error_message?: string; + error_name?: string; + has_more: boolean; + items: T[]; + quota_max: number; + quota_remaining: number; +}; diff --git a/types/unique-filename.d.ts b/types/unique-filename.d.ts new file mode 100644 index 0000000..5b77ba2 --- /dev/null +++ b/types/unique-filename.d.ts @@ -0,0 +1,7 @@ +declare module 'unique-filename' { + export default function ( + dir: string, + filePrefix?: string, + uniqStr?: string + ): string; +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..bff83cb --- /dev/null +++ b/vercel.json @@ -0,0 +1,3 @@ +{ + "trailingSlash": false +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..e28aa46 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,198 @@ +'use strict'; + +// This webpack config is used to transpile src to dist, compile externals, +// compile executables, etc + +const { EnvironmentPlugin, DefinePlugin, BannerPlugin } = require('webpack'); +const { verifyEnvironment } = require('./expect-env'); +const nodeExternals = require('webpack-node-externals'); +const debug = require('debug')(`${require('./package.json').name}:webpack-config`); + +const IMPORT_ALIASES = { + universe: `${__dirname}/src/`, + multiverse: `${__dirname}/lib/`, + testverse: `${__dirname}/test/`, + externals: `${__dirname}/external-scripts/`, + types: `${__dirname}/types/`, + package: `${__dirname}/package.json`, + // ? These are used at various points (including at compile time by + // ? Next.js) to get mongo schema configuration and/or test dummy data. + // ! Must be defined if using @xunnamius/mongo-schema + 'configverse/get-schema-config': `${__dirname}/src/backend/db.ts`, + // ! Must be defined if using @xunnamius/mongo-test + 'configverse/get-dummy-data': `${__dirname}/test/db.ts` +}; + +let sanitizedEnv = {}; +let { NODE_ENV: nodeEnv, ...sanitizedProcessEnv } = { + ...process.env, + NODE_ENV: 'production' +}; + +try { + require('fs').accessSync('.env'); + const { NODE_ENV: forceEnv, ...parsedEnv } = require('dotenv').config().parsed; + nodeEnv = forceEnv || nodeEnv; + sanitizedEnv = parsedEnv; + debug(`NODE_ENV: ${nodeEnv}`); + debug('sanitized .env vars: %O', sanitizedEnv); +} catch (e) { + debug(`.env support disabled; reason: ${e}`); +} + +debug('sanitized process env: %O', sanitizedProcessEnv); +verifyEnvironment(); + +const envPlugins = [ + // ? NODE_ENV is not a "default" (unlike below) but an explicit overwrite + new DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(nodeEnv) + }), + // ? Load our .env results as the defaults (overridden by process.env) + new EnvironmentPlugin({ ...sanitizedEnv, ...sanitizedProcessEnv }), + // ? Create shim process.env for undefined vars + // ! The above already replaces all process.env.X occurrences in the code + // ! first, so plugin order is important here + new DefinePlugin({ 'process.env': '{}' }) +]; + +const externals = [ + nodeExternals(), + ({ request }, cb) => + // ? Externalize all .json imports (required as commonjs modules) + /\.json$/.test(request) ? cb(null, `commonjs ${request}`) : cb() +]; + +/* const libConfig = { + name: 'lib', + mode: 'production', + target: 'node', + node: false, + + entry: `${__dirname}/src/index.ts`, + + output: { + filename: 'index.js', + path: `${__dirname}/dist`, + // ! ▼ Only required for libraries + // ! ▼ Note: ESM outputs are handled by Babel ONLY! + libraryTarget: 'commonjs2' + }, + + externals, + externalsPresets: { node: true }, + + stats: { + orphanModules: true, + providedExports: true, + usedExports: true, + errorDetails: true + }, + + resolve: { + extensions: ['.ts', '.wasm', '.mjs', '.cjs', '.js', '.json'], + // ! If changed, also update these aliases in tsconfig.json, + // ! jest.config.js, next.config.ts, and .eslintrc.js + alias: IMPORT_ALIASES + }, + module: { + rules: [{ test: /\.(ts|js)x?$/, loader: 'babel-loader', exclude: /node_modules/ }] + }, + optimization: { usedExports: true }, + plugins: [...envPlugins] +}; */ + +const externalsConfig = { + name: 'externals', + mode: 'production', + target: 'node', + node: false, + + entry: { + 'ban-hammer': `${__dirname}/external-scripts/ban-hammer.ts`, + 'prune-data': `${__dirname}/external-scripts/prune-data.ts`, + 'log-stats': `${__dirname}/external-scripts/log-stats.ts`, + 'initialize-data': `${__dirname}/external-scripts/initialize-data/index.ts` + // 'simulate-activity': `${__dirname}/external-scripts/simulate-activity.ts` + }, + + output: { + filename: '[name].js', + path: `${__dirname}/external-scripts/bin` + }, + + externals, + externalsPresets: { node: true }, + + stats: { + orphanModules: true, + providedExports: true, + usedExports: true, + errorDetails: true + }, + + resolve: { + extensions: ['.ts', '.wasm', '.mjs', '.cjs', '.js', '.json'], + // ! If changed, also update these aliases in tsconfig.json, + // ! jest.config.js, next.config.ts, and .eslintrc.js + alias: IMPORT_ALIASES + }, + module: { + rules: [ + { + test: /\.(ts|js)x?$/, + exclude: /node_modules/, + use: 'babel-loader' + } + ] + }, + optimization: { usedExports: true }, + plugins: [ + ...envPlugins, + // * ▼ For non-bundled externals, make entry file executable w/ shebang + new BannerPlugin({ banner: '#!/usr/bin/env node', raw: true, entryOnly: true }) + ] +}; + +/* const cliConfig = { + name: 'cli', + mode: 'production', + target: 'node', + node: false, + + entry: `${__dirname}/src/cli.ts`, + + output: { + filename: 'cli.js', + path: `${__dirname}/dist` + }, + + externals, + externalsPresets: { node: true }, + + stats: { + orphanModules: true, + providedExports: true, + usedExports: true, + errorDetails: true + }, + + resolve: { + extensions: ['.ts', '.wasm', '.mjs', '.cjs', '.js', '.json'], + // ! If changed, also update these aliases in tsconfig.json, + // ! jest.config.js, next.config.ts, and .eslintrc.js + alias: IMPORT_ALIASES + }, + module: { + rules: [{ test: /\.(ts|js)x?$/, loader: 'babel-loader', exclude: /node_modules/ }] + }, + optimization: { usedExports: true }, + plugins: [ + ...envPlugins, + // * ▼ For bundled CLI applications, make entry file executable w/ shebang + new BannerPlugin({ banner: '#!/usr/bin/env node', raw: true, entryOnly: true }) + ] +}; */ + +module.exports = [/*libConfig,*/ externalsConfig /*, cliConfig*/]; +debug('exports: %O', module.exports);